Files

123 lines
4.4 KiB
Go

//go:build integration
package integration
import (
"fmt"
"time"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// uniqueRepo returns a collision-resistant repo name scoped to a test.
// Matching the E2E source's naming keeps a future side-by-side diff greppable.
func uniqueRepo(prefix string) string {
return fmt.Sprintf("e2e/%s-%d", prefix, time.Now().UnixNano())
}
// TestECRControlPlane round-trips the ECR control-plane surface: repository
// CRUD, repository policy, and lifecycle policy. All of it dispatches to
// ECRRegistry.Meta over NATS (gateway/ecr_createrepository.go and friends), so
// this needs StartECRDaemonLite subscribed before any call.
func TestECRControlPlane(t *testing.T) {
gw := StartGateway(t)
StartECRDaemonLite(t, gw)
c := gw.ECRClient(t)
repo := uniqueRepo("ctl")
_, err := c.CreateRepository(&ecr.CreateRepositoryInput{RepositoryName: aws.String(repo)})
require.NoError(t, err, "create-repository")
t.Run("an invalid name is refused with InvalidParameterException", func(t *testing.T) {
_, err := c.CreateRepository(&ecr.CreateRepositoryInput{RepositoryName: aws.String("Bad_Name!")})
var aerr awserr.Error
require.ErrorAs(t, err, &aerr)
assert.Equal(t, ecr.ErrCodeInvalidParameterException, aerr.Code())
assert.Equal(t, `Invalid parameter at 'repositoryName' failed to satisfy constraint: 'must satisfy regular expression '[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*''`, aerr.Message())
})
t.Run("DescribeRepositories lists the new repo", func(t *testing.T) {
out, err := c.DescribeRepositories(&ecr.DescribeRepositoriesInput{
RepositoryNames: []*string{aws.String(repo)},
})
require.NoError(t, err)
require.Len(t, out.Repositories, 1)
assert.Equal(t, repo, aws.StringValue(out.Repositories[0].RepositoryName))
assert.Contains(t, aws.StringValue(out.Repositories[0].RepositoryUri), repo)
})
t.Run("repository policy round-trips", func(t *testing.T) {
policy := `{"Version":"2012-10-17","Statement":[{"Sid":"allow-pull","Effect":"Allow","Principal":"*","Action":["ecr:GetDownloadUrlForLayer","ecr:BatchGetImage"]}]}`
_, err := c.SetRepositoryPolicy(&ecr.SetRepositoryPolicyInput{
RepositoryName: aws.String(repo),
PolicyText: aws.String(policy),
})
require.NoError(t, err)
got, err := c.GetRepositoryPolicy(&ecr.GetRepositoryPolicyInput{
RepositoryName: aws.String(repo),
})
require.NoError(t, err)
assert.JSONEq(t, policy, aws.StringValue(got.PolicyText))
})
t.Run("lifecycle policy round-trips", func(t *testing.T) {
policy := `{"rules":[{"rulePriority":1,"selection":{"tagStatus":"untagged","countType":"sinceImagePushed","countUnit":"days","countNumber":14},"action":{"type":"expire"}}]}`
_, err := c.PutLifecyclePolicy(&ecr.PutLifecyclePolicyInput{
RepositoryName: aws.String(repo),
LifecyclePolicyText: aws.String(policy),
})
require.NoError(t, err)
got, err := c.GetLifecyclePolicy(&ecr.GetLifecyclePolicyInput{
RepositoryName: aws.String(repo),
})
require.NoError(t, err)
assert.JSONEq(t, policy, aws.StringValue(got.LifecyclePolicyText))
})
t.Run("DeleteRepository removes it", func(t *testing.T) {
_, err := c.DeleteRepository(&ecr.DeleteRepositoryInput{
RepositoryName: aws.String(repo),
Force: aws.Bool(true),
})
require.NoError(t, err)
_, err = c.DescribeRepositories(&ecr.DescribeRepositoriesInput{
RepositoryNames: []*string{aws.String(repo)},
})
require.Error(t, err, "deleted repo must not be describable")
assert.Contains(t, err.Error(), "RepositoryNotFoundException")
})
}
// TestECRGetLoginPassword confirms GetAuthorizationToken returns a usable
// AWS:<jwt> credential — the input to docker login. Minting needs only
// ECRTokenIssuer, wired unconditionally by StartGateway, so no daemon-lite
// subscription is required here.
func TestECRGetLoginPassword(t *testing.T) {
gw := StartGateway(t)
pass := ecrGetLoginPassword(t, gw.ECRClient(t))
assert.NotEmpty(t, pass)
// JWT shape: three dot-separated segments.
assert.Len(t, splitDots(pass), 3, "authorization password should be a JWT")
}
func splitDots(s string) []string {
var out []string
start := 0
for i := 0; i < len(s); i++ {
if s[i] == '.' {
out = append(out, s[start:i])
start = i + 1
}
}
return append(out, s[start:])
}