Skip to content

Commit

Permalink
Fix GitHub action URL (#80)
Browse files Browse the repository at this point in the history
Our docs and default value for the GitHub action cache URL were
referring to the
[wrong](https://github.com/tonistiigi/go-actions-cache/blob/master/api.md)
environment variable.

I was under the impression that the underlying library ends up checking
for the correct variable later so this would be only cosmetic, but a
user
[confirmed](#77 (comment))
this does appear to break things.

I brought over an old test I had written while this was still in the
pulumi-docker repo, but unfortunately it doesn't currently test GHA due
to ci-mgmt limitations
#82.

Fixes #77.
  • Loading branch information
blampe authored May 29, 2024
1 parent c9e5913 commit f0aaf70
Show file tree
Hide file tree
Showing 22 changed files with 2,228 additions and 64 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Unreleased

### Fixed

- Fixed the default value for `ACTIONS_CACHE_URL` when using GitHub action caching. (https://github.com/pulumi/pulumi-docker-build/pull/80)

## 0.0.2 (2024-04-25)

### Fixed

- Upgraded pulumi-go-provider to fix a panic during cancellation.

## 0.0.1 (2024-04-23)

Initial release.
185 changes: 185 additions & 0 deletions examples/nodejs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@
package examples

import (
"encoding/base64"
"fmt"
"math/rand"
"os"
"path"
"strings"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/pulumi/pulumi/pkg/v3/testing/integration"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -26,3 +34,180 @@ func TestNodeExample(t *testing.T) {

integration.ProgramTest(t, &test)
}

// TestCaching simulates a slow build with --cache-to enabled. We aren't able
// to directly detect cache hits, so we re-run the update and confirm it took
// less time than the image originally took to build.
//
// This is a moderately slow test because we need to "build" (i.e., sleep)
// longer than it would take for cache layer uploads under slow network
// conditions.
func TestCaching(t *testing.T) {
t.Parallel()

sleep := 20.0 // seconds

// Provision ECR outside of our stack, because the cache needs to be shared
// across updates.
ecr, ecrOK := tmpEcr(t)

cwd, err := os.Getwd()
require.NoError(t, err)

localCache := t.TempDir()

tests := []struct {
name string
skip bool

cacheTo string
cacheFrom string
address string
username string
password string
}{
{
name: "local",
cacheTo: fmt.Sprintf("type=local,mode=max,oci-mediatypes=true,dest=%s", localCache),
cacheFrom: fmt.Sprintf("type=local,src=%s", localCache),
},
{
name: "gha",
skip: os.Getenv("ACTIONS_CACHE_URL") == "",
cacheTo: "type=gha,mode=max,scope=cache-test",
cacheFrom: "type=gha,scope=cache-test",
},
{
name: "dockerhub",
skip: os.Getenv("DOCKER_HUB_PASSWORD") == "",
cacheTo: "type=registry,mode=max,ref=docker.io/pulumibot/myapp:cache",
cacheFrom: "type=registry,ref=docker.io/pulumibot/myapp:cache",
address: "docker.io",
username: "pulumibot",
password: os.Getenv("DOCKER_HUB_PASSWORD"),
},
{
name: "ecr",
skip: !ecrOK,
cacheTo: fmt.Sprintf("type=registry,mode=max,image-manifest=true,oci-mediatypes=true,ref=%s:cache", ecr.address),
cacheFrom: fmt.Sprintf("type=registry,ref=%s:cache", ecr.address),
address: ecr.address,
username: ecr.username,
password: ecr.password,
},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
if tt.skip {
t.Skip("Missing environment variables")
}

sleepFuzzed := sleep + rand.Float64() // Add some fuzz to bust any prior build caches.

test := integration.ProgramTestOptions{
Dir: path.Join(cwd, "tests", "caching"),
ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
duration, ok := stack.Outputs["durationSeconds"]
assert.True(t, ok)
assert.Greater(t, duration.(float64), sleepFuzzed)
},
Dependencies: []string{"@pulumi/docker-build"},
Config: map[string]string{
"SLEEP_SECONDS": fmt.Sprint(sleepFuzzed),
"cacheTo": tt.cacheTo,
"cacheFrom": tt.cacheFrom,
"name": tt.name,
"address": tt.address,
"username": tt.username,
},
Secrets: map[string]string{
"password": tt.password,
},
NoParallel: true,
Quick: true,
SkipPreview: true,
SkipRefresh: true,
Verbose: true,
}

// First run should be un-cached.
integration.ProgramTest(t, &test)

// Now run again and confirm our build was faster due to a cache hit.
test.ExtraRuntimeValidation = func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
duration, ok := stack.Outputs["durationSeconds"]
assert.True(t, ok)
assert.Less(t, duration.(float64), sleepFuzzed)
}
test.Config["name"] += "-cached"
integration.ProgramTest(t, &test)
})
}
}

type ECR struct {
address string
username string
password string
}

// tmpEcr creates a new ECR repo and cleans it up after the test concludes.
func tmpEcr(t *testing.T) (ECR, bool) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-west-2"),
})
if err != nil {
return ECR{}, false
}

svc := ecr.New(sess)
name := strings.ToLower(t.Name())

// Always attempt to delete pre-existing repos, in case our cleanup didn't
// run.
_, _ = svc.DeleteRepository(&ecr.DeleteRepositoryInput{
Force: aws.Bool(true),
RepositoryName: aws.String(name),
})

params := &ecr.CreateRepositoryInput{
RepositoryName: aws.String(name),
}
resp, err := svc.CreateRepository(params)
if err != nil {
return ECR{}, false
}
repo := resp.Repository
t.Cleanup(func() {
svc.DeleteRepository(&ecr.DeleteRepositoryInput{
Force: aws.Bool(true),
RegistryId: repo.RegistryId,
RepositoryName: repo.RepositoryName,
})
})

// Now grab auth for the repo.
auth, err := svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
if err != nil {
return ECR{}, false
}
b64token := auth.AuthorizationData[0].AuthorizationToken
token, err := base64.StdEncoding.DecodeString(*b64token)
if err != nil {
return ECR{}, false
}
parts := strings.SplitN(string(token), ":", 2)
if len(parts) != 2 {
return ECR{}, false
}
username := parts[0]
password := parts[1]

return ECR{
address: *repo.RepositoryUri,
username: username,
password: password,
}, true
}
7 changes: 7 additions & 0 deletions examples/tests/caching/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM --platform=$BUILDPLATFORM golang:1.21.6-alpine3.18 as initial
ARG SLEEP_SECONDS
RUN sleep ${SLEEP_SECONDS} && echo ${SLEEP_SECONDS} > output

FROM alpine:3.18 as final
COPY --from=initial /go/output output
RUN cat output
3 changes: 3 additions & 0 deletions examples/tests/caching/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: test-buildx-caching
runtime: nodejs
description: A minimal TypeScript Pulumi program
40 changes: 40 additions & 0 deletions examples/tests/caching/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as buildx from "@pulumi/docker-build";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();

const start = new Date().getTime();

// docker buildx build \
// -f Dockerfile \
// --cache-to type=local,dest=tmp,mode=max,oci-mediatypes=true \
// --cache-from type=local,src=tmp \
// --build-arg SLEEP-MS=$SLEEP_MS \
// -t not-pushed \
// -f Dockerfile \
// .
const img = new buildx.Image(`buildx-${config.require("name")}`, {
tags: ["not-pushed"],
dockerfile: { location: "Dockerfile" },
push: false,
context: { location: "." },
buildArgs: {
SLEEP_SECONDS: config.require("SLEEP_SECONDS"),
},
cacheTo: [{ raw: config.require("cacheTo") }],
cacheFrom: [{ raw: config.require("cacheFrom") }],
// Set registry auth if it was provided.
registries: config.require("username")
? [
{
address: config.getSecret("address"),
username: config.getSecret("username"),
password: config.getSecret("password"),
},
]
: undefined,
});

export const durationSeconds = img.ref.apply(
(_) => (new Date().getTime() - start) / 1000.0
);
9 changes: 9 additions & 0 deletions examples/tests/caching/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "test-buildx-caching",
"devDependencies": {
"@types/node": "^20.0.0"
},
"dependencies": {
"@pulumi/pulumi": "^3.0.0"
}
}
Loading

0 comments on commit f0aaf70

Please sign in to comment.