Gerard/sc 106216/b registry image collector (#1570)

* update registry auth with username and password

* add unit test
This commit is contained in:
Gerard Nguyen
2024-07-03 08:06:37 +10:00
committed by GitHub
parent d79ff950ca
commit e882f44ae9
2 changed files with 91 additions and 3 deletions
+21 -3
View File
@@ -21,6 +21,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)
type RegistryImage struct {
@@ -97,6 +98,7 @@ func imageExists(namespace string, clientConfig *rest.Config, registryCollector
authConfig, err := getImageAuthConfig(namespace, clientConfig, registryCollector, imageRef)
if err != nil {
klog.Errorf("failed to get auth config: %v", err)
return false, errors.Wrap(err, "failed to get auth config")
}
@@ -115,10 +117,13 @@ func imageExists(namespace string, clientConfig *rest.Config, registryCollector
remoteImage, err := imageRef.NewImage(context.Background(), &sysCtx)
if err == nil {
klog.Infof("image %s exists", image)
remoteImage.Close()
return true, nil
}
klog.Errorf("failed to get image %s: %v", image, err)
if strings.Contains(err.Error(), "no image found in manifest list for architecture") {
// manifest was downloaded, but no matching architecture found in manifest
// should this count as image does not exist?
@@ -188,7 +193,9 @@ func getImageAuthConfigFromData(imageRef types.ImageReference, pullSecrets *v1be
dockerCfgJSON := struct {
Auths map[string]struct {
Auth []byte `json:"auth"`
Auth string `json:"auth"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"auths"`
}{}
@@ -203,14 +210,25 @@ func getImageAuthConfigFromData(imageRef types.ImageReference, pullSecrets *v1be
return nil, nil
}
// gcr.io auth uses username and password, e.g. username: _json_key, password: <sa_key>
if auth.Username != "" && auth.Password != "" {
return &registryAuthConfig{
username: auth.Username,
password: auth.Password,
}, nil
}
// docker.io auth uses auth, e.g. auth: <base64_encoded_username_password>
// username and password can't contain colon
// at least according to https://github.com/docker/cli/blob/v27.0.3/cli/config/configfile/file.go#L247
parts := strings.Split(string(auth.Auth), ":")
if len(parts) != 2 {
return nil, errors.Errorf("expected 2 parts in the string, but found %d", len(parts))
return nil, errors.Errorf("expected 2 parts in the auth string, but found %d", len(parts))
}
authConfig := registryAuthConfig{
username: parts[0],
password: parts[1],
password: strings.Trim(parts[1], "\x00"),
}
return &authConfig, nil
+70
View File
@@ -0,0 +1,70 @@
package collect
import (
"encoding/base64"
"fmt"
"testing"
"github.com/containers/image/v5/transports/alltransports"
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
)
func TestGetImageAuthConfigFromData(t *testing.T) {
tests := []struct {
name string
imageName string
dockerConfigJSON string
expectedUsername string
expectedPassword string
expectedError bool
}{
{
name: "docker.io auth",
imageName: "docker.io/myimage",
dockerConfigJSON: `{"auths":{"docker.io":{"auth":"username:password"}}}`,
expectedUsername: "username",
expectedPassword: "password",
expectedError: false,
},
{
name: "docker.io auth multi colon",
imageName: "docker.io/myimage",
dockerConfigJSON: `{"auths":{"docker.io":{"auth":"user:name:pass:word"}}}`,
expectedError: true,
},
{
name: "gcr.io auth",
imageName: "gcr.io/myimage",
dockerConfigJSON: `{"auths":{"gcr.io":{"username":"_json_key","password":"sa-key"}}}`,
expectedUsername: "_json_key",
expectedPassword: "sa-key",
expectedError: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
imageRef, err := alltransports.ParseImageName(fmt.Sprintf("docker://%s", test.imageName))
assert.NoError(t, err)
pullSecrets := &v1beta2.ImagePullSecrets{
SecretType: "kubernetes.io/dockerconfigjson",
Data: map[string]string{
".dockerconfigjson": base64.StdEncoding.EncodeToString([]byte(test.dockerConfigJSON)),
},
}
authConfig, err := getImageAuthConfigFromData(imageRef, pullSecrets)
if test.expectedError {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.NotNil(t, authConfig)
assert.Equal(t, test.expectedUsername, authConfig.username)
assert.Equal(t, test.expectedPassword, authConfig.password)
})
}
}