Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #384 from nerdalize/feature/private-images
Add option to use private images
- Loading branch information
Showing
17 changed files
with
791 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package svc | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/nerdalize/nerd/pkg/kubevisor" | ||
"github.com/pkg/errors" | ||
|
||
"k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
//CreateSecretInput is the input to CreateSecret | ||
type CreateSecretInput struct { | ||
Image string `validate:"printascii"` | ||
Registry string `validate:"required"` | ||
Project string | ||
Username string `validate:"required"` | ||
Password string `validate:"required"` | ||
} | ||
|
||
//CreateSecretOutput is the output to CreateSecret | ||
type CreateSecretOutput struct { | ||
Name string | ||
} | ||
|
||
//CreateSecret will create a secret on kubernetes | ||
func (k *Kube) CreateSecret(ctx context.Context, in *CreateSecretInput) (out *CreateSecretOutput, err error) { | ||
if err = k.checkInput(ctx, in); err != nil { | ||
return nil, err | ||
} | ||
|
||
secret := &v1.Secret{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Labels: map[string]string{"image": in.Image, "project": in.Project, "registry": in.Registry}, | ||
}, | ||
Type: v1.SecretTypeDockerConfigJson, | ||
Data: map[string][]byte{}, | ||
} | ||
|
||
secret.Data[v1.DockerConfigJsonKey], err = transformCredentials(in.Username, in.Password, in.Registry) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = k.visor.CreateResource(ctx, kubevisor.ResourceTypeSecrets, secret, "") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &CreateSecretOutput{ | ||
Name: secret.Name, | ||
}, nil | ||
} | ||
|
||
func transformCredentials(username, password, registry string) (dockereCfg []byte, err error) { | ||
var dockerCfg []byte | ||
auths := map[string]interface{}{} | ||
cfg := map[string]interface{}{ | ||
"auths": auths, | ||
"HttpHeaders": map[string]interface{}{ | ||
"User-Agent": "Docker-Client/1.11.2 (linux)", | ||
}, | ||
} | ||
authStr := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) | ||
auths[fmt.Sprintf("https://%s/v1/", registry)] = map[string]string{ | ||
"auth": authStr, | ||
} | ||
auths[fmt.Sprintf("%s", registry)] = map[string]string{ | ||
"auth": authStr, | ||
} | ||
if dockerCfg, err = json.Marshal(cfg); err != nil { | ||
return dockerCfg, errors.Wrap(err, "failed to serialize docker secret cfg") | ||
} | ||
return dockerCfg, nil | ||
} | ||
|
||
// ExtractRegistry takes a string as input and divides it in image, project, registry | ||
func ExtractRegistry(image string) (string, string, string) { | ||
// Supported registries: | ||
// - project/image -> index.docker.io | ||
// - ACCOUNT.dkr.ecr.REGION.amazonaws.com/image -> aws | ||
// - azurecr.io/image -> azure | ||
// - quay.io/project/image -> quay.io | ||
// - gcr.io/project/image -> gcr | ||
// gitlab?? other providers? | ||
|
||
parts := strings.Split(image, "/") | ||
switch len(parts) { | ||
case 2: | ||
if !strings.Contains(parts[0], ".") { | ||
return parts[1], parts[0], "index.docker.io" | ||
} | ||
return parts[1], "", parts[0] | ||
case 3: | ||
return parts[2], parts[1], parts[0] | ||
} | ||
return "", "", "" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package svc_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"reflect" | ||
"runtime" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/nerdalize/nerd/svc" | ||
) | ||
|
||
func TestCreateSecret(t *testing.T) { | ||
for _, c := range []struct { | ||
Name string | ||
Timeout time.Duration | ||
Input *svc.CreateSecretInput | ||
IsOutput func(tb testing.TB, out *svc.CreateSecretOutput) | ||
IsErr func(error) bool | ||
}{ | ||
{ | ||
Name: "when a zero value input is provided it should return a validation error", | ||
Timeout: time.Second * 5, | ||
Input: nil, | ||
IsErr: svc.IsValidationErr, | ||
IsOutput: func(t testing.TB, out *svc.CreateSecretOutput) { | ||
assert(t, out == nil, "output should be nil") | ||
}, | ||
}, | ||
{ | ||
Name: "when a valid input is provided it should return a secret with a unique name", | ||
Timeout: time.Second * 5, | ||
Input: &svc.CreateSecretInput{Image: "smoketest", Project: "nerdalize", Registry: "quay.io", Username: "test", Password: "test"}, | ||
IsErr: nil, | ||
IsOutput: func(t testing.TB, out *svc.CreateSecretOutput) { | ||
assert(t, out != nil, "output should not be nil") | ||
assert(t, strings.Contains(out.Name, "s-"), "secret name should be generated and prefixed") | ||
}, | ||
}, | ||
} { | ||
t.Run(c.Name, func(t *testing.T) { | ||
di, clean := testDI(t) | ||
defer clean() | ||
|
||
ctx := context.Background() | ||
ctx, cancel := context.WithTimeout(ctx, c.Timeout) | ||
defer cancel() | ||
|
||
kube := svc.NewKube(di) | ||
out, err := kube.CreateSecret(ctx, c.Input) | ||
if c.IsErr != nil { | ||
assert(t, c.IsErr(err), fmt.Sprintf("unexpected '%#v' to match: %#v", err, runtime.FuncForPC(reflect.ValueOf(c.IsErr).Pointer()).Name())) | ||
} | ||
|
||
if c.IsOutput != nil { | ||
c.IsOutput(t, out) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.