Skip to content

Commit

Permalink
Making ratchet compatible with tekton by adding tekton parser (#72)
Browse files Browse the repository at this point in the history
Added a tekton parser and a check to see if current image uses a tag, if
so, keep that tag and and the updated sha ref

Signed-off-by: Arif Ashogbon <[email protected]>
  • Loading branch information
arifash01 committed Apr 12, 2024
1 parent f0773aa commit 8f1cce7
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 0 deletions.
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

0 comments on commit 8f1cce7

Please sign in to comment.