Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making ratchet compatible with tekton by adding tekton parser #72

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Cargo, Go modules, NPM, Pip, or Yarn, but for CI/CD workflows. Ratchet supports:
- GitLab CI
- Google Cloud Build
- Harness Drone
- Tekton

**⚠️ Warning!** The README corresponds to the `main` branch of ratchet's
development, and it may contain unreleased features.
Expand Down Expand Up @@ -85,9 +86,13 @@ ratchet pin -parser drone drone.yml
# pin a gitlab file
ratchet pin -parser gitlabci gitlabci.yml

# output to a tekton file
ratchet pin -out -parser tekton tekton.yml

# output to a different path
ratchet pin -out workflow-compiled.yml workflow.yml
```
```

#### Unpin

Expand Down
1 change: 1 addition & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var parserFactory = map[string]func() Parser{
"cloudbuild": func() Parser { return new(CloudBuild) },
"drone": func() Parser { return new(Drone) },
"gitlabci": func() Parser { return new(GitLabCI) },
"tekton": func() Parser { return new(Tekton) },
}

// For returns the parser that corresponds to the given name.
Expand Down
73 changes: 73 additions & 0 deletions parser/tekton.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package parser

import (
"fmt"

// Using banydonk/yaml instead of the default yaml pkg because the default
// pkg incorrectly escapes unicode. https://github.com/go-yaml/yaml/issues/737
"github.com/braydonk/yaml"
"github.com/sethvargo/ratchet/resolver"
)

type Tekton struct{}

// Parse pulls the Tekton Ci refs from the documents.
func (d *Tekton) Parse(nodes []*yaml.Node) (*RefsList, error) {
var refs RefsList
for i, node := range nodes {
if err := d.parseOne(&refs, node); err != nil {
return nil, fmt.Errorf("failed to parse node %d: %w", i, err)
}
}

return &refs, nil
}

func (d *Tekton) parseOne(refs *RefsList, node *yaml.Node) error {
if node == nil {
return nil
}

if node.Kind != yaml.DocumentNode {
return fmt.Errorf("expected document node, got %v", node.Kind)
}

for _, docMap := range node.Content {
if docMap.Kind != yaml.MappingNode {
continue
}

// Confirm it's a tekton file then proceed to look for image keyword
for _, stepsMap := range docMap.Content {
if stepsMap.Value == "apiVersion" {
d.findSpecs(refs, docMap)
break
}
}
}

return nil
}
func (d *Tekton) findSpecs(refs *RefsList, node *yaml.Node) error {
for i, specsMap := range node.Content {
if specsMap.Value == "spec" {
specs := node.Content[i+1]
d.findImages(refs, specs)
}
}
return nil
}

func (d *Tekton) findImages(refs *RefsList, node *yaml.Node) error {
for i, property := range node.Content {
if property.Value == "image" {
image := node.Content[i+1]
ref := resolver.NormalizeContainerRef(image.Value)
refs.Add(ref, image)
break
} else {
d.findImages(refs, property)
}
}
return nil
}
64 changes: 64 additions & 0 deletions parser/tekton_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package parser

import (
"reflect"
"testing"

"github.com/braydonk/yaml"
)

func TestTekton_Parse(t *testing.T) {
t.Parallel()

cases := []struct {
name string
in string
exp []string
}{
{
name: "mostly_empty_file",
in: `
jobs:
`,
exp: []string{},
},
{
name: "steps",
in: `
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: testData
spec:
params:
- name: username
type: string
steps:
- name: git
image: alpine/git
`,
exp: []string{
"container://alpine/git",
},
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

m := helperStringToYAML(t, tc.in)

refs, err := new(Tekton).Parse([]*yaml.Node{m})
if err != nil {
t.Fatal(err)
}

if got, want := refs.Refs(), tc.exp; !reflect.DeepEqual(got, want) {
t.Errorf("expected %q to be %q", got, want)
}
})
}
}
Binary file added ratchet
Binary file not shown.
3 changes: 3 additions & 0 deletions resolver/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func (g *Container) Resolve(ctx context.Context, value string) (string, error) {
if err != nil {
return "", fmt.Errorf("failed to lookup container ref: %w", err)
}
if value == ref.Name() {
return fmt.Sprintf("%s@%s", ref.Name(), resp.Digest.String()), nil
}

return fmt.Sprintf("%s@%s", ref.Context().Name(), resp.Digest.String()), nil
}
11 changes: 11 additions & 0 deletions testdata/tekton.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: testData
spec:
params:
- name: username
type: string
steps:
- name: build
image: golang:1.12