Skip to content

Commit

Permalink
Merge pull request #44 from buildtool/gcr
Browse files Browse the repository at this point in the history
feat: initial support for GCP Container registry
  • Loading branch information
peter-svensson authored Mar 20, 2020
2 parents fe77277 + 75166d5 commit a0a95b2
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pkg/build/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func TestBuild_FeatureBranch(t *testing.T) {
eout := &bytes.Buffer{}
client := &docker.MockDocker{}
buildContext, _ := archive.Generate("Dockerfile", "FROM scratch")
code := build(client, name, ioutil.NopCloser(buildContext), out, eout,"Dockerfile", arrayFlags{}, false, false)
code := build(client, name, ioutil.NopCloser(buildContext), out, eout, "Dockerfile", arrayFlags{}, false, false)

assert.Equal(t, 0, code)
assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile)
Expand Down
4 changes: 3 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type RegistryConfig struct {
Github *registry.Github `yaml:"github"`
Gitlab *registry.Gitlab `yaml:"gitlab"`
Quay *registry.Quay `yaml:"quay"`
GCR *registry.GCR `yaml:"gcr"`
}

type Environment struct {
Expand Down Expand Up @@ -101,10 +102,11 @@ func InitEmptyConfig() *Config {
Github: &registry.Github{},
Gitlab: &registry.Gitlab{},
Quay: &registry.Quay{},
GCR: &registry.GCR{},
},
}
c.AvailableCI = []ci.CI{c.CI.Azure, c.CI.Buildkite, c.CI.Gitlab, c.CI.TeamCity, c.CI.Github}
c.AvailableRegistries = []registry.Registry{c.Registry.Dockerhub, c.Registry.ECR, c.Registry.Github, c.Registry.Gitlab, c.Registry.Quay}
c.AvailableRegistries = []registry.Registry{c.Registry.Dockerhub, c.Registry.ECR, c.Registry.Github, c.Registry.Gitlab, c.Registry.Quay, c.Registry.GCR}
return c
}

Expand Down
62 changes: 62 additions & 0 deletions pkg/registry/gcr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package registry

import (
"context"
"docker.io/go-docker/api/types"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/buildtool/build-tools/pkg/docker"
"io"
)

type GCR struct {
dockerRegistry
Url string `yaml:"url" env:"GCR_URL"`
KeyFileContent string `yaml:"keyfileContent" env:"GCR_KEYFILE_CONTENT"`
}

var _ Registry = &GCR{}

func (r *GCR) Name() string {
return "GCR"
}

func (r *GCR) Configured() bool {
if len(r.Url) > 0 {
return true
}
return false
}

func (r *GCR) Login(client docker.Client, out io.Writer) error {
auth := r.GetAuthConfig()
auth.ServerAddress = r.Url
if ok, err := client.RegistryLogin(context.Background(), auth); err == nil {
_, _ = fmt.Fprintln(out, ok.Status)
return nil
} else {
return err
}
}

func (r *GCR) GetAuthConfig() types.AuthConfig {
decoded, err := base64.StdEncoding.DecodeString(r.KeyFileContent)
if err != nil {
return types.AuthConfig{}
}
return types.AuthConfig{Username: "_json_key", Password: string(decoded)}
}

func (r *GCR) GetAuthInfo() string {
authBytes, _ := json.Marshal(r.GetAuthConfig())
return base64.URLEncoding.EncodeToString(authBytes)
}

func (r GCR) RegistryUrl() string {
return r.Url
}

func (r GCR) Create(repository string) error {
return nil
}
163 changes: 163 additions & 0 deletions pkg/registry/gcr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package registry

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestGcr_Name(t *testing.T) {
registry := &GCR{
Url: "url",
KeyFileContent: "a2V5ZmlsZSBjb250ZW50Cg==",
}
assert.Equal(t, "GCR", registry.Name())
}

func TestGcr_Configured(t *testing.T) {
registry := &GCR{
Url: "url",
KeyFileContent: "a2V5ZmlsZSBjb250ZW50Cg==",
}

assert.True(t, registry.Configured())
}

func TestGcr_GetAuthInfo(t *testing.T) {
registry := &GCR{
Url: "url",
KeyFileContent: "a2V5ZmlsZSBjb250ZW50Cg==",
}

auth := registry.GetAuthInfo()
assert.Equal(t, "eyJ1c2VybmFtZSI6Il9qc29uX2tleSIsInBhc3N3b3JkIjoia2V5ZmlsZSBjb250ZW50XG4ifQ==", auth)
}

//
//func TestEcr_LoginAuthRequestFailed(t *testing.T) {
// client := &docker.MockDocker{}
// registry := &ECR{Url: "ecr-url", Region: "eu-west-1", svc: &MockECR{loginError: fmt.Errorf("auth failure")}}
// out := &bytes.Buffer{}
// err := registry.Login(client, out)
// assert.EqualError(t, err, "auth failure")
// assert.Equal(t, "", out.String())
//}
//
//func TestEcr_LoginInvalidAuthData(t *testing.T) {
// client := &docker.MockDocker{}
// registry := &ECR{Url: "ecr-url", Region: "eu-west-1", svc: &MockECR{authData: "aaabbb"}}
// out := &bytes.Buffer{}
// err := registry.Login(client, out)
// assert.EqualError(t, err, "illegal base64 data at input byte 4")
// assert.Equal(t, "", out.String())
//}
//
//func TestEcr_LoginFailed(t *testing.T) {
// client := &docker.MockDocker{LoginError: fmt.Errorf("invalid username/password")}
// registry := &ECR{Url: "ecr-url", Region: "eu-west-1", svc: &MockECR{authData: "QVdTOmFiYzEyMw=="}}
// out := &bytes.Buffer{}
// err := registry.Login(client, out)
// assert.EqualError(t, err, "invalid username/password")
// assert.Equal(t, "", out.String())
//}
//
//func TestEcr_LoginSuccess(t *testing.T) {
// client := &docker.MockDocker{}
// registry := &ECR{Url: "ecr-url", Region: "eu-west-1", svc: &MockECR{authData: "QVdTOmFiYzEyMw=="}}
// out := &bytes.Buffer{}
// err := registry.Login(client, out)
// assert.Nil(t, err)
// assert.Equal(t, "AWS", client.Username)
// assert.Equal(t, "abc123", client.Password)
// assert.Equal(t, "ecr-url", client.ServerAddress)
// assert.Equal(t, "Logged in\n", out.String())
//}
//
//func TestEcr_GetAuthInfo(t *testing.T) {
// registry := &ECR{Url: "ecr-url", Region: "eu-west-1", username: "AWS", password: "abc123"}
// auth := registry.GetAuthInfo()
// assert.Equal(t, "eyJ1c2VybmFtZSI6IkFXUyIsInBhc3N3b3JkIjoiYWJjMTIzIn0=", auth)
//}
//
//func TestEcr_ExistingRepository(t *testing.T) {
// mock := &MockECR{repoExists: true}
// registry := &ECR{svc: mock}
// repo := "repo"
// err := registry.Create(repo)
// assert.Nil(t, err)
// assert.Equal(t, []*string{&repo}, mock.describeRepositoriesInput.RepositoryNames)
//}
//
//func TestEcr_NewRepositoryCreateError(t *testing.T) {
// registry := &ECR{svc: &MockECR{createError: fmt.Errorf("create error")}}
// err := registry.Create("repo")
// assert.EqualError(t, err, "create error")
//}
//
//func TestEcr_NewRepositoryPutError(t *testing.T) {
// registry := &ECR{svc: &MockECR{putError: fmt.Errorf("put error")}}
// err := registry.Create("repo")
// assert.EqualError(t, err, "put error")
//}
//
//func TestEcr_NewRepository(t *testing.T) {
// mock := &MockECR{}
// registry := &ECR{svc: mock}
// repo := "repo"
// err := registry.Create(repo)
// assert.Nil(t, err)
// assert.Equal(t, &repo, mock.createRepositoryInput.RepositoryName)
// policyText := `{"rules":[{"rulePriority":10,"description":"Only keep 20 images","selection":{"tagStatus":"untagged","countType":"imageCountMoreThan","countNumber":20},"action":{"type":"expire"}}]}`
// assert.Equal(t, &policyText, mock.putLifecyclePolicyInput.LifecyclePolicyText)
//}
//
//func TestEcr_ParseECRUrlIfNoRegionIsSet(t *testing.T) {
// ecr := ECR{
// Url: "12345678.dkr.ecr.eu-west-1.amazonaws.com",
// }
// assert.Equal(t, "eu-west-1", *ecr.region())
//}
//
//func TestEcr_UseRegionIfSet(t *testing.T) {
// ecr := ECR{
// Url: "12345678.dkr.ecr.eu-west-1.amazonaws.com",
// Region: "region",
// }
// assert.Equal(t, "region", *ecr.region())
//}
//
//type MockECR struct {
// ecriface.ECRAPI
// loginError error
// authData string
// describeRepositoriesInput *awsecr.DescribeRepositoriesInput
// repoExists bool
// createError error
// createRepositoryInput *awsecr.CreateRepositoryInput
// putLifecyclePolicyInput *awsecr.PutLifecyclePolicyInput
// putError error
//}
//
//func (r MockECR) GetAuthorizationToken(input *awsecr.GetAuthorizationTokenInput) (*awsecr.GetAuthorizationTokenOutput, error) {
// if r.loginError != nil {
// return &awsecr.GetAuthorizationTokenOutput{AuthorizationData: []*awsecr.AuthorizationData{}}, r.loginError
// }
// return &awsecr.GetAuthorizationTokenOutput{AuthorizationData: []*awsecr.AuthorizationData{{AuthorizationToken: &r.authData}}}, nil
//}
//
//func (r *MockECR) DescribeRepositories(input *awsecr.DescribeRepositoriesInput) (*awsecr.DescribeRepositoriesOutput, error) {
// r.describeRepositoriesInput = input
// if r.repoExists {
// return &awsecr.DescribeRepositoriesOutput{Repositories: []*awsecr.Repository{}}, nil
// }
// return &awsecr.DescribeRepositoriesOutput{Repositories: []*awsecr.Repository{}}, fmt.Errorf("NoDockerRegistry repository found")
//}
//
//func (r *MockECR) CreateRepository(input *awsecr.CreateRepositoryInput) (*awsecr.CreateRepositoryOutput, error) {
// r.createRepositoryInput = input
// return &awsecr.CreateRepositoryOutput{}, r.createError
//}
//
//func (r *MockECR) PutLifecyclePolicy(input *awsecr.PutLifecyclePolicyInput) (*awsecr.PutLifecyclePolicyOutput, error) {
// r.putLifecyclePolicyInput = input
// return &awsecr.PutLifecyclePolicyOutput{}, r.putError
//}
10 changes: 10 additions & 0 deletions www/content/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The following registries are supported:
| [`github`](#github) | [Github package registry](https://help.github.com/en/github/managing-packages-with-github-package-registry/about-github-package-registry) |
| [`gitlab`](#gitlab) | [Gitlab container registry](https://docs.gitlab.com/ee/user/packages/container_registry/) |
| [`quay`](#quay) | [Quay docker registry](https://docs.quay.io/) |
| [`gcr`](#gcr) | [Google Container registry](https://cloud.google.com/container-registry) |

### dockerhub

Expand Down Expand Up @@ -63,3 +64,12 @@ To authenticate `token` or a combination of `username` and `password` must be pr
| `repository` | The repository part of the docker image name | `QUAY_REPOSITORY` |
| `username` | User to authenticate | `QUAY_USERNAME` |
| `password` | Password for `user` authentication | `QUAY_PASSWORD` |

### gcr

GCP Credentials must be supplied as [service account json key](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key) (Base64 encoded)

| Parameter | Description | Env variable |
| :---------------- | :-------------------------------- | :--------------------- |
| `url` | The GCR registry URL | `GCR_URL` |
| `keyfileContent` | ServiceAccount keyfile content | `GCR_KEYFILE_CONTENT`

0 comments on commit a0a95b2

Please sign in to comment.