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

feat: added support for custom tags #175

Merged
merged 2 commits into from
Jul 6, 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
1 change: 1 addition & 0 deletions docs/resources/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Sample resource in the Terraform provider scaffolding.
- `platforms` (List of String) Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*
- `repo` (String) Container repository to publish images to. If set, this overrides the provider's `repo`, and the image name will be exactly the specified `repo`, without the importpath appended.
- `sbom` (String) The SBOM media type to use (none will disable SBOM synthesis and upload).
- `tags` (List of String) Which tags to use for the produced image instead of the default 'latest' tag
- `working_dir` (String) working directory for the build

### Read-Only
Expand Down
15 changes: 15 additions & 0 deletions internal/provider/resource_ko_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ func resourceBuild() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true, // Any time this changes, don't try to update in-place, just create it.
},
"tags": {
Description: "Which tags to use for the produced image instead of the default 'latest' tag",
Optional: true,
pcanilho marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true, // Any time this changes, don't try to update in-place, just create it.
},
},
}
}
Expand All @@ -134,6 +141,7 @@ type buildOptions struct {
bare bool // If true, use the "bare" namer that doesn't append the importpath.
ldflags []string // Extra ldflags to pass to the go build.
env []string // Extra environment variables to pass to the go build.
tags []string // Which tags to use for the produced image instead of the default 'latest'
}

var (
Expand Down Expand Up @@ -253,6 +261,7 @@ func namer(opts buildOptions) publish.Namer {
DockerRepo: opts.imageRepo,
Bare: opts.bare,
PreserveImportPaths: !opts.bare,
Tags: opts.tags,
})
}

Expand All @@ -261,12 +270,17 @@ func doPublish(ctx context.Context, r build.Result, opts buildOptions) (string,
if opts.auth != nil {
kc = authn.NewMultiKeychain(staticKeychain{opts.imageRepo, opts.auth}, kc)
}

po := []publish.Option{
publish.WithAuthFromKeychain(kc),
publish.WithNamer(namer(opts)),
publish.WithUserAgent(userAgent),
}

if opts.tags != nil && len(opts.tags) > 0 {
po = append(po, publish.WithTags(opts.tags))
}

p, err := publish.NewDefault(opts.imageRepo, po...)
if err != nil {
return "", fmt.Errorf("NewDefault: %w", err)
Expand Down Expand Up @@ -300,6 +314,7 @@ func fromData(d *schema.ResourceData, po *Opts) buildOptions {
bare: bare,
ldflags: toStringSlice(d.Get("ldflags").([]interface{})),
env: toStringSlice(d.Get("env").([]interface{})),
tags: toStringSlice(d.Get("tags").([]interface{})),
}
}

Expand Down
95 changes: 95 additions & 0 deletions internal/provider/resource_ko_build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"fmt"
"net/http/httptest"
"regexp"
"slices"
"strings"
"testing"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/registry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -195,6 +197,99 @@ func TestAccResourceKoBuild(t *testing.T) {
})
}

func TestAccResourceKoBuild_Tags(t *testing.T) {
type testCase struct {
name string
config string
expectNamedTags []string
}

path := "github.com/ko-build/terraform-provider-ko/cmd/test"
testCases := []testCase{
{
name: "named_tags_foo_bar",
config: `
resource "ko_build" "foo" {
sbom = "none"
importpath = "%s"
tags = ["foo", "bar"]
}
`,
expectNamedTags: []string{"foo", "bar"},
},
{
name: "named_tags_v1.0.0_stable",
config: `
resource "ko_build" "foo" {
sbom = "none"
importpath = "%s"
tags = ["v1.0.0", "stable"]
}
`,
expectNamedTags: []string{"v1.0.0", "stable"},
},
{
name: "named_tags_multiple_including_latest",
config: `
resource "ko_build" "foo" {
sbom = "none"
importpath = "%s"
tags = ["v1.0.0", "stable", "latest", "production"]
}
`,
expectNamedTags: []string{"v1.0.0", "stable", "latest", "production"},
},
{
name: "named_tags_omit_default_latest",
config: `
resource "ko_build" "foo" {
sbom = "none"
importpath = "%s"
}
`,
expectNamedTags: []string{"latest"},
},
}

var srv *httptest.Server
for _, tc := range testCases {
// Setup a local registry and have tests push to that.
srv = httptest.NewServer(registry.New())
parts := strings.Split(srv.URL, ":")
url := fmt.Sprintf("localhost:%s/test", parts[len(parts)-1])
t.Setenv("KO_DOCKER_REPO", url)

imageRefRE := regexp.MustCompile("^" + url + fmt.Sprintf("/%s@sha256:", path))

t.Run(tc.name, func(t *testing.T) {
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
Steps: []resource.TestStep{{
Config: fmt.Sprintf(tc.config, path),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("ko_build.foo", "image_ref", imageRefRE),
),
}},
})
tags, err := crane.ListTags(fmt.Sprintf("%s/%s", url, path),
crane.WithTransport(srv.Client().Transport))
if err != nil {
t.Fatalf("failed to list tags: %v", err)
}
if len(tags) != len(tc.expectNamedTags) {
t.Fatalf("expected %d tags, got %d", len(tc.expectNamedTags), len(tags))
}
slices.Sort(tc.expectNamedTags)
slices.Sort(tags)
if !slices.Equal(tc.expectNamedTags, tags) {
t.Fatalf("expected tags %v, got %v", tc.expectNamedTags, tags)
}
})

srv.Close()
}
}

func TestAccResourceKoBuild_ImageRepo(t *testing.T) {
// Setup a local registry and have tests push to that.
srv := httptest.NewServer(registry.New())
Expand Down
Loading