Skip to content

Commit

Permalink
Merge pull request #75 from rusenask/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
rusenask committed Aug 3, 2017
2 parents 69d9bbb + 4617a5a commit 2604414
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 8 deletions.
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,12 @@ test:

build:
@echo "++ Building keel"
CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags "$(LDFLAGS)" -o keel .
CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags "$(LDFLAGS)" -o keel .

image:
docker build -t karolisr/keel:alpha -f Dockerfile .

alpha: image
@echo "++ Pushing keel alpha"
docker push karolisr/keel:alpha

77 changes: 74 additions & 3 deletions secrets/secrets.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package secrets

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/url"
"strings"

"github.com/rusenask/keel/provider/helm"
"github.com/rusenask/keel/provider/kubernetes"
Expand Down Expand Up @@ -72,9 +75,28 @@ func (g *DefaultGetter) lookupSecrets(image *types.TrackedImage) ([]string, erro

for _, pod := range podList.Items {
podSecrets := getPodImagePullSecrets(&pod)
log.WithFields(log.Fields{
"namespace": image.Namespace,
"provider": image.Provider,
"registry": image.Image.Registry(),
"image": image.Image.Repository(),
"pod_selector": selector,
"secrets": podSecrets,
}).Info("secrets.defaultGetter.lookupSecrets: pod secrets found")
secrets = append(secrets, podSecrets...)
}

if len(secrets) == 0 {
log.WithFields(log.Fields{
"namespace": image.Namespace,
"provider": image.Provider,
"registry": image.Image.Registry(),
"image": image.Image.Repository(),
"pod_selector": selector,
"pods_checked": len(podList.Items),
}).Info("secrets.defaultGetter.lookupSecrets: no secrets for image found")
}

return secrets, nil
}

Expand Down Expand Up @@ -108,7 +130,7 @@ func (g *DefaultGetter) getCredentialsFromSecret(image *types.TrackedImage) (*ty
"namespace": image.Namespace,
"secret_ref": secretRef,
"type": secret.Type,
}).Warn("secrets.defaultGetter: supplied secret is not kubernetes.io/dockerconfigjson, ignoring")
}).Warn("secrets.defaultGetter: supplied secret is not kubernetes.io/dockercfg, ignoring")
continue
}

Expand Down Expand Up @@ -150,8 +172,33 @@ func (g *DefaultGetter) getCredentialsFromSecret(image *types.TrackedImage) (*ty
}

if h == image.Image.Registry() {
credentials.Username = auth.Username
credentials.Password = auth.Password
if auth.Username != "" && auth.Password != "" {
credentials.Username = auth.Username
credentials.Password = auth.Password
} else if auth.Auth != "" {
username, password, err := decodeBase64Secret(auth.Auth)
if err != nil {
log.WithFields(log.Fields{
"image": image.Image.Repository(),
"namespace": image.Namespace,
"registry": registry,
"secret_ref": secretRef,
"error": err,
}).Error("secrets.defaultGetter: failed to decode auth secret")
continue
}
credentials.Username = username
credentials.Password = password
} else {
log.WithFields(log.Fields{
"image": image.Image.Repository(),
"namespace": image.Namespace,
"registry": registry,
"secret_ref": secretRef,
"error": err,
}).Warn("secrets.defaultGetter: secret doesn't have username, password and base64 encoded auth, skipping")
continue
}

log.WithFields(log.Fields{
"namespace": image.Namespace,
Expand All @@ -163,12 +210,36 @@ func (g *DefaultGetter) getCredentialsFromSecret(image *types.TrackedImage) (*ty
return credentials, nil
}
}
}

if len(image.Secrets) > 0 {
log.WithFields(log.Fields{
"namespace": image.Namespace,
"provider": image.Provider,
"registry": image.Image.Registry(),
"image": image.Image.Repository(),
"secrets": image.Secrets,
}).Warn("secrets.defaultGetter.lookupSecrets: docker credentials were not found among secrets")
}

return credentials, nil
}

func decodeBase64Secret(authSecret string) (username, password string, err error) {
decoded, err := base64.StdEncoding.DecodeString(authSecret)
if err != nil {
return
}

parts := strings.Split(string(decoded), ":")

if len(parts) != 2 {
return "", "", fmt.Errorf("unexpected auth secret format")
}

return parts[0], parts[1], nil
}

func hostname(registry string) (string, error) {
u, err := url.Parse(registry)
if err != nil {
Expand Down
151 changes: 147 additions & 4 deletions secrets/secrets_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package secrets

import (
"encoding/base64"
"fmt"
"testing"

"github.com/rusenask/keel/types"
"github.com/rusenask/keel/util/image"

"k8s.io/client-go/pkg/api/v1"

testutil "github.com/rusenask/keel/util/testing"
"testing"
"k8s.io/client-go/pkg/api/v1"
)

var secretDataPayload = `{"https://index.docker.io/v1/":{"username":"user-x","password":"pass-x","email":"karolis.rusenas@gmail.com","auth":"somethinghere"}}`

func mustEncode(data string) string {
return base64.StdEncoding.EncodeToString([]byte(data))
}

func TestGetSecret(t *testing.T) {
imgRef, _ := image.Parse("karolisr/webhook-demo:0.0.11")

Expand Down Expand Up @@ -77,9 +80,57 @@ func TestGetSecretNotFound(t *testing.T) {
}
}

var secretDataPayloadEncoded = `{"https://index.docker.io/v1/":{"auth": "%s"}}`

func TestLookupHelmSecret(t *testing.T) {
imgRef, _ := image.Parse("karolisr/webhook-demo:0.0.11")

impl := &testutil.FakeK8sImplementer{
AvailablePods: &v1.PodList{
Items: []v1.Pod{
v1.Pod{
Spec: v1.PodSpec{ImagePullSecrets: []v1.LocalObjectReference{
v1.LocalObjectReference{
Name: "very-secret",
},
},
},
},
},
},
AvailableSecret: &v1.Secret{
Data: map[string][]byte{
dockerConfigJSONKey: []byte(fmt.Sprintf(secretDataPayloadEncoded, mustEncode("user-y:pass-y"))),
},
Type: v1.SecretTypeDockercfg,
},
}

getter := NewGetter(impl)

trackedImage := &types.TrackedImage{
Image: imgRef,
Namespace: "default",
Secrets: []string{"myregistrysecret"},
}

creds, err := getter.Get(trackedImage)
if err != nil {
t.Errorf("failed to get creds: %s", err)
}

if creds.Username != "user-y" {
t.Errorf("unexpected username: %s", creds.Username)
}

if creds.Password != "pass-y" {
t.Errorf("unexpected pass: %s", creds.Password)
}
}

func TestLookupHelmEncodedSecret(t *testing.T) {
imgRef, _ := image.Parse("karolisr/webhook-demo:0.0.11")

impl := &testutil.FakeK8sImplementer{
AvailablePods: &v1.PodList{
Items: []v1.Pod{
Expand Down Expand Up @@ -122,3 +173,95 @@ func TestLookupHelmSecret(t *testing.T) {
t.Errorf("unexpected pass: %s", creds.Password)
}
}

func TestLookupHelmNoSecretsFound(t *testing.T) {
imgRef, _ := image.Parse("karolisr/webhook-demo:0.0.11")

impl := &testutil.FakeK8sImplementer{
AvailablePods: &v1.PodList{
Items: []v1.Pod{
v1.Pod{
Spec: v1.PodSpec{ImagePullSecrets: []v1.LocalObjectReference{
v1.LocalObjectReference{
Name: "very-secret",
},
},
},
},
},
},
Error: fmt.Errorf("not found"),
}

getter := NewGetter(impl)

trackedImage := &types.TrackedImage{
Image: imgRef,
Namespace: "default",
Secrets: []string{"myregistrysecret"},
}

creds, err := getter.Get(trackedImage)
if err != nil {
t.Errorf("failed to get creds: %s", err)
}

// should be anonymous
if creds.Username != "" {
t.Errorf("unexpected username: %s", creds.Username)
}

if creds.Password != "" {
t.Errorf("unexpected pass: %s", creds.Password)
}
}

func Test_decodeBase64Secret(t *testing.T) {
type args struct {
authSecret string
}
tests := []struct {
name string
args args
wantUsername string
wantPassword string
wantErr bool
}{
{
name: "hello there",
args: args{authSecret: "aGVsbG86dGhlcmU="},
wantUsername: "hello",
wantPassword: "there",
wantErr: false,
},
{
name: "hello there, encoded",
args: args{authSecret: mustEncode("hello:there")},
wantUsername: "hello",
wantPassword: "there",
wantErr: false,
},
{
name: "empty",
args: args{authSecret: ""},
wantUsername: "",
wantPassword: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotUsername, gotPassword, err := decodeBase64Secret(tt.args.authSecret)
if (err != nil) != tt.wantErr {
t.Errorf("decodeBase64Secret() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotUsername != tt.wantUsername {
t.Errorf("decodeBase64Secret() gotUsername = %v, want %v", gotUsername, tt.wantUsername)
}
if gotPassword != tt.wantPassword {
t.Errorf("decodeBase64Secret() gotPassword = %v, want %v", gotPassword, tt.wantPassword)
}
})
}
}

0 comments on commit 2604414

Please sign in to comment.