jx/internal/resource/container_image_test.go
Matthew Rich 27ab3711a6
Some checks failed
Declarative Tests / test (push) Waiting to run
Declarative Tests / build-fedora (push) Waiting to run
Declarative Tests / build-ubuntu-focal (push) Waiting to run
Lint / golangci-lint (push) Has been cancelled
fix lint errors
2024-09-27 02:13:01 +00:00

347 lines
10 KiB
Go

// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
package resource
import (
"context"
"decl/tests/mocks"
_ "encoding/json"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/stretchr/testify/assert"
"io"
_ "net/http"
_ "net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"path/filepath"
"decl/internal/data"
"decl/internal/folio"
"decl/internal/codec"
//_ "decl/internal/fan"
"strconv"
)
type MockConfigValueGetter func(key string) (any, error)
func (m MockConfigValueGetter) GetValue(key string) (any, error) {
return m(key)
}
func TestNewContainerImageResource(t *testing.T) {
c := NewContainerImage(&mocks.MockContainerClient{})
assert.NotNil(t, c)
}
func TestContainerImageURI(t *testing.T) {
case0URI := URIFromContainerImageName("foo")
assert.Equal(t, "container-image:///foo", case0URI)
case1URI := URIFromContainerImageName("foo:bar")
assert.Equal(t, "container-image:///foo:bar", case1URI)
case2URI := URIFromContainerImageName("quuz/foo:bar")
assert.Equal(t, "container-image:///quuz/foo:bar", case2URI)
case3URI := URIFromContainerImageName("myhost/quuz/foo:bar")
assert.Equal(t, "container-image://myhost/quuz/foo:bar", case3URI)
}
func TestLoadFromContainerImageURI(t *testing.T) {
testURI := URIFromContainerImageName("myhost/quuz/foo:bar")
newResource, resourceErr := folio.DocumentRegistry.ResourceTypes.New(testURI)
assert.Nil(t, resourceErr)
assert.NotNil(t, newResource)
assert.IsType(t, &ContainerImage{}, newResource)
assert.Equal(t, "myhost/quuz/foo:bar", newResource.(*ContainerImage).Name)
assert.Equal(t, testURI, newResource.URI())
}
func TestContainerImageNameFromURI(t *testing.T) {
case0u,_ := url.Parse("container-image:///foo")
case0Image := ContainerImageNameFromURI(case0u)
assert.Equal(t, "foo", case0Image)
case1u,_ := url.Parse("container-image:///foo:bar")
case1Image := ContainerImageNameFromURI(case1u)
assert.Equal(t, "foo:bar", case1Image)
case2u,_ := url.Parse("container-image:///quuz/foo:bar")
case2Image := ContainerImageNameFromURI(case2u)
assert.Equal(t, "quuz/foo:bar", case2Image)
case3u,_ := url.Parse("container-image://myhost/quuz/foo:bar")
case3Image := ContainerImageNameFromURI(case3u)
assert.Equal(t, "myhost/quuz/foo:bar", case3Image)
}
func TestReadContainerImage(t *testing.T) {
output := io.NopCloser(strings.NewReader("testdata"))
ctx := context.Background()
decl := `
name: "alpine:latest"
state: present
`
m := &mocks.MockContainerClient{
InjectImagePull: func(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error) {
return output, nil
},
InjectImageRemove: func(ctx context.Context, imageID string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
return nil, nil
},
InjectImageInspectWithRaw: func(ctx context.Context, imageID string) (types.ImageInspect, []byte, error) {
return types.ImageInspect{
ID: "sha256:123456789abc",
}, nil, nil
},
}
c := NewContainerImage(m)
assert.NotNil(t, c)
e := c.LoadDecl(decl)
assert.Equal(t, nil, e)
assert.Equal(t, "alpine:latest", c.Name)
resourceYaml, readContainerErr := c.Read(ctx)
assert.Equal(t, nil, readContainerErr)
assert.Greater(t, len(resourceYaml), 0)
}
func TestCreateContainerImage(t *testing.T) {
m := &mocks.MockContainerClient{
InjectImageBuild: func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return types.ImageBuildResponse{Body: io.NopCloser(strings.NewReader("image built")) }, nil
},
}
decl := fmt.Sprintf(`
name: "testcontainerimage"
image: "alpine"
contextref: file://%s
`, "")
c := NewContainerImage(m)
stater := c.StateMachine()
e := c.LoadDecl(decl)
assert.Nil(t, e)
assert.Equal(t, "testcontainerimage", c.Name)
assert.Nil(t, stater.Trigger("create"))
}
func TestCreateContainerImagePush(t *testing.T) {
m := &mocks.MockContainerClient{
InjectImageBuild: func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return types.ImageBuildResponse{Body: io.NopCloser(strings.NewReader("image built")) }, nil
},
InjectImagePush: func(ctx context.Context, image string, options image.PushOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("foo")), nil
},
}
decl := fmt.Sprintf(`
name: "testcontainerimage"
image: "alpine"
push: true
contextref: file://%s
`, "")
c := NewContainerImage(m)
stater := c.StateMachine()
e := c.LoadDecl(decl)
assert.Nil(t, e)
assert.Equal(t, "testcontainerimage", c.Name)
c.UseConfig(MockConfigValueGetter(func(key string) (any, error) {
switch key {
case "repo_password":
return "bar", nil
case "repo_username":
return "foo", nil
case "repo_server":
return "", nil
}
return nil, fmt.Errorf("%w: %s", data.ErrUnknownConfigurationKey, key)
}))
assert.Nil(t, stater.Trigger("create"))
assert.Nil(t, c.Push(context.Background()))
assert.True(t, c.PushImage)
// assert.Equal(t, "foo", c.Output)
}
func TestContainerImageContextDocument(t *testing.T) {
contextDir, _ := filepath.Abs(TempDir.FilePath("context"))
etcDir, _ := filepath.Abs(filepath.Join(contextDir, "etc"))
binDir, _ := filepath.Abs(filepath.Join(contextDir, "bin"))
assert.Nil(t, os.Mkdir(contextDir, os.ModePerm))
assert.Nil(t, os.Mkdir(etcDir, os.ModePerm))
assert.Nil(t, os.Mkdir(binDir, os.ModePerm))
m := &mocks.MockContainerClient{
InjectImageBuild: func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return types.ImageBuildResponse{Body: io.NopCloser(strings.NewReader("image built")) }, nil
},
}
contextDirUri := fmt.Sprintf("file://%s", contextDir)
contextDirDecl := fmt.Sprintf(`
type: file
attributes:
path: %s
`, contextDir)
decl := fmt.Sprintf(`
name: "testcontainerimage"
image: "alpine"
contextref: %s
`, contextDirUri)
c := NewContainerImage(m)
c.ConverterTypes = TestConverterTypes
//stater := c.StateMachine()
e := c.LoadDecl(decl)
assert.Nil(t, e)
c.ContextRef = folio.ResourceReference(contextDirUri)
contextFile := folio.NewDeclaration()
assert.Nil(t, contextFile.NewResource(&contextDirUri))
assert.Nil(t, contextFile.LoadString(contextDirDecl, codec.FormatYaml))
_, readErr := contextFile.Resource().Read(context.Background())
assert.Nil(t, readErr)
c.Resources = data.NewResourceMapper()
c.Resources.Set(contextDirUri, contextFile)
d, contextErr := c.ContextDocument()
assert.Nil(t, contextErr)
assert.NotNil(t, d)
assert.Greater(t, 3, d.Len())
}
func TestContainerError(t *testing.T) {
var expected, err, boErr ContainerError
expected.Detail.ErrorMessage = "invalid reference format"
expected.Error = "invalid reference format"
buildOutput := `"{\"stream\":\"Step 1/6 : ARG DIST\"}\r\n{\"stream\":\"\\n\"}\r\n{\"stream\":\"Step 2/6 : FROM golang:${DIST}\"}\r\n{\"stream\":\"\\n\"}\r\n{\"errorDetail\":{\"message\":\"invalid reference format\"},\"error\":\"invalid reference format\"}\r\n"`
unqBuildOutput, buildOutputErr := strconv.Unquote(buildOutput)
assert.Nil(t, buildOutputErr)
dec := codec.NewJSONStringDecoder(strings.Split(unqBuildOutput, "\r\n")[4])
assert.Nil(t, dec.Decode(&boErr))
assert.Equal(t, expected, boErr)
msg := `"{\"errorDetail\":{\"message\":\"invalid reference format\"},\"error\":\"invalid reference format\"}"`
unquotedmsg := `{"errorDetail":{"message":"invalid reference format"},"error":"invalid reference format"}`
c,_ := strconv.Unquote(msg)
assert.Equal(t, unquotedmsg, c)
decoder := codec.NewJSONStringDecoder(c)
assert.Nil(t, decoder.Decode(&err))
assert.Equal(t, expected, err)
}
func TestContainerLogUnmarshal(t *testing.T) {
/*
m := &mocks.MockContainerClient{
InjectImageBuild: func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return types.ImageBuildResponse{Body: io.NopCloser(strings.NewReader("image built")) }, nil
},
}
c := NewContainerImage(m)
assert.NotNil(t, c)
*/
buildOutput := `"{\"stream\":\"Step 1/6 : ARG DIST\"}\r\n{\"stream\":\"\\n\"}\r\n{\"stream\":\"Step 2/6 : FROM golang:${DIST}\"}\r\n{\"stream\":\"\\n\"}\r\n{\"errorDetail\":{\"message\":\"invalid reference format\"},\"error\":\"invalid reference format\"}\r\n"`
unqBuildOutput, buildOutputErr := strconv.Unquote(buildOutput)
assert.Nil(t, buildOutputErr)
for _, v := range strings.Split(unqBuildOutput, "\r\n") {
var clog ContainerLog
outputDecoder := codec.NewJSONStringDecoder(v)
err := outputDecoder.Decode(&clog)
if err == io.EOF {
break
}
if clog.ContainerLogStream != nil {
assert.Greater(t, len(clog.ContainerLogStream.Stream), 0)
}
}
//assert.Equal(t, "Step 1/6 : ARG DIST", clog[0].ContainerLogStream.Stream)
}
func TestContainerImageRegistryAuthConfig(t *testing.T) {
m := &mocks.MockContainerClient{
InjectImageBuild: func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return types.ImageBuildResponse{Body: io.NopCloser(strings.NewReader("image built")) }, nil
},
}
c := NewContainerImage(m)
assert.NotNil(t, c)
c.UseConfig(MockConfigValueGetter(func(key string) (any, error) {
switch key {
case "repo_password":
return "bar", nil
case "repo_username":
return "foo", nil
case "repo_server":
return "", nil
}
return nil, fmt.Errorf("%w: %s", data.ErrUnknownConfigurationKey, key)
}))
authConfig, err := c.RegistryAuthConfig()
assert.NotNil(t, authConfig)
assert.Nil(t, err)
assert.Equal(t, "bar", authConfig.Password)
assert.Equal(t, "foo", authConfig.Username)
}
func TestContainerImageRegistryAuth(t *testing.T) {
m := &mocks.MockContainerClient{
InjectImageBuild: func(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
return types.ImageBuildResponse{Body: io.NopCloser(strings.NewReader("image built")) }, nil
},
}
c := NewContainerImage(m)
assert.NotNil(t, c)
c.UseConfig(MockConfigValueGetter(func(key string) (any, error) {
switch key {
case "repo_password":
return "bar", nil
case "repo_username":
return "foo", nil
case "repo_server":
return "", nil
}
return nil, fmt.Errorf("%w: %s", data.ErrUnknownConfigurationKey, key)
}))
auth, err := c.RegistryAuth()
assert.NotNil(t, auth)
assert.Nil(t, err)
assert.Greater(t, len(auth), 1)
}