diff --git a/.github/workflows/enki.yml b/.github/workflows/enki.yml new file mode 100644 index 0000000..fd84ca5 --- /dev/null +++ b/.github/workflows/enki.yml @@ -0,0 +1,24 @@ +--- +name: 'run enki unit tests' + +on: + pull_request: + +concurrency: + group: enki-${{ github.ref || github.head_ref }} + cancel-in-progress: true + +env: + FORCE_COLOR: 1 + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: earthly/actions/setup-earthly@v1 + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Build + run: cd tools-image/enki && earthly -P +test diff --git a/tools-image/enki/Earthfile b/tools-image/enki/Earthfile new file mode 100644 index 0000000..9419411 --- /dev/null +++ b/tools-image/enki/Earthfile @@ -0,0 +1,19 @@ +VERSION 0.7 + +# renovate: datasource=docker depName=golang +ARG --global GO_VERSION=1.20-alpine3.18 + +test: + FROM golang:$GO_VERSION + RUN apk add rsync gcc musl-dev docker jq + WORKDIR /build + COPY . . + RUN go mod download + ARG TEST_PATHS=./... + ARG LABEL_FILTER= + ENV CGO_ENABLED=1 + # Some test require the docker sock exposed + WITH DOCKER + RUN go run github.com/onsi/ginkgo/v2/ginkgo run --label-filter "$LABEL_FILTER" -v --fail-fast --race --covermode=atomic --coverprofile=coverage.out --coverpkg=github.com/rancher/elemental-cli/... -p -r $TEST_PATHS + END + SAVE ARTIFACT coverage.out AS LOCAL coverage.out diff --git a/tools-image/enki/cmd/build-iso.go b/tools-image/enki/cmd/build-iso.go index 21c0ba6..1a0be30 100644 --- a/tools-image/enki/cmd/build-iso.go +++ b/tools-image/enki/cmd/build-iso.go @@ -13,10 +13,9 @@ import ( "k8s.io/mount-utils" ) -// NewBuildISO returns a new instance of the buid-iso subcommand and appends it to -// the root command. requireRoot is to initiate it with or without the CheckRoot -// pre-run check. This method is mostly used for testing purposes. -func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { +// NewBuildISOCmd returns a new instance of the build-iso subcommand and appends it to +// the root command. +func NewBuildISOCmd() *cobra.Command { c := &cobra.Command{ Use: "build-iso SOURCE", Short: "Build bootable installation media ISOs", @@ -26,10 +25,7 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { " * - is path to file or directory, image name with tag version", Args: cobra.MaximumNArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { - if addCheckRoot { - return CheckRoot() - } - return nil + return CheckRoot() }, RunE: func(cmd *cobra.Command, args []string) error { path, err := exec.LookPath("mount") @@ -108,7 +104,6 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { return nil }, } - root.AddCommand(c) c.Flags().StringP("name", "n", "", "Basename of the generated ISO file") c.Flags().StringP("output", "o", "", "Output directory (defaults to current directory)") c.Flags().Bool("date", false, "Adds a date suffix into the generated ISO file") @@ -122,5 +117,6 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { return c } -// register the subcommand into rootCmd -var _ = NewBuildISO(rootCmd, true) +func init() { + rootCmd.AddCommand(NewBuildISOCmd()) +} diff --git a/tools-image/enki/cmd/build-iso_test.go b/tools-image/enki/cmd/build-iso_test.go new file mode 100644 index 0000000..c38dfe1 --- /dev/null +++ b/tools-image/enki/cmd/build-iso_test.go @@ -0,0 +1,70 @@ +/* +Copyright © 2022 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "bytes" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/viper" +) + +var _ = Describe("BuildISO", Label("iso", "cmd"), func() { + var buf *bytes.Buffer + BeforeEach(func() { + buf = new(bytes.Buffer) + rootCmd.SetOut(buf) + rootCmd.SetErr(buf) + }) + AfterEach(func() { + viper.Reset() + }) + It("Errors out if no rootfs sources are defined", Label("flags"), func() { + _, _, err := executeCommandC(rootCmd, "build-iso") + fmt.Println(buf) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("rootfs source image for building ISO was not provided")) + }) + It("Errors out if rootfs is a non valid argument", Label("flags"), func() { + _, _, err := executeCommandC(rootCmd, "build-iso", "/no/image/reference") + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("invalid image reference")) + }) + It("Errors out if overlay roofs path does not exist", Label("flags"), func() { + _, _, err := executeCommandC( + rootCmd, "build-iso", "system/cos", "--overlay-rootfs", "/nonexistingpath", + ) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("Invalid path")) + }) + It("Errors out if overlay uefi path does not exist", Label("flags"), func() { + _, _, err := executeCommandC( + rootCmd, "build-iso", "someimage:latest", "--overlay-uefi", "/nonexistingpath", + ) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("Invalid path")) + }) + It("Errors out if overlay iso path does not exist", Label("flags"), func() { + _, _, err := executeCommandC( + rootCmd, "build-iso", "some/image:latest", "--overlay-iso", "/nonexistingpath", + ) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("Invalid path")) + }) +}) diff --git a/tools-image/enki/cmd/cmd_suite_test.go b/tools-image/enki/cmd/cmd_suite_test.go new file mode 100644 index 0000000..c406f75 --- /dev/null +++ b/tools-image/enki/cmd/cmd_suite_test.go @@ -0,0 +1,29 @@ +/* +Copyright © 2021 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestWhitebox(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CLI whitebox test suite") +} diff --git a/tools-image/enki/cmd/command_test.go b/tools-image/enki/cmd/command_test.go new file mode 100644 index 0000000..3288d9f --- /dev/null +++ b/tools-image/enki/cmd/command_test.go @@ -0,0 +1,53 @@ +/* +Copyright © 2021 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "io/ioutil" + "os" + + "github.com/spf13/cobra" +) + +func executeCommandC(cmd *cobra.Command, args ...string) (c *cobra.Command, output string, err error) { + // Set args to command + cmd.SetArgs(args) + // store old stdout + oldStdout := os.Stdout + r, w, _ := os.Pipe() + // Change stdout to our pipe + os.Stdout = w + // run the command + c, err = cmd.ExecuteC() + if err != nil { + // Remember to restore stdout! + os.Stdout = oldStdout + return nil, "", err + } + err = w.Close() + if err != nil { + // Remember to restore stdout! + os.Stdout = oldStdout + return nil, "", err + } + // Read output from our pipe + out, _ := ioutil.ReadAll(r) + // restore stdout + os.Stdout = oldStdout + + return c, string(out), nil +} diff --git a/tools-image/enki/go.mod b/tools-image/enki/go.mod index 967bfec..53685b2 100644 --- a/tools-image/enki/go.mod +++ b/tools-image/enki/go.mod @@ -6,6 +6,8 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/kairos-io/kairos-agent/v2 v2.1.11-0.20230713071318-9a16b94e2af6 github.com/mitchellh/mapstructure v1.5.0 + github.com/onsi/ginkgo/v2 v2.9.7 + github.com/onsi/gomega v1.27.8 github.com/sanity-io/litter v1.5.5 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 @@ -54,10 +56,13 @@ require ( github.com/go-git/go-git/v5 v5.4.2 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-containerregistry v0.15.2 // indirect + github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gookit/color v1.5.3 // indirect diff --git a/tools-image/enki/go.sum b/tools-image/enki/go.sum index 3ba9200..38b0917 100644 --- a/tools-image/enki/go.sum +++ b/tools-image/enki/go.sum @@ -192,6 +192,7 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -245,6 +246,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -261,6 +263,7 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso= +github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= @@ -398,9 +401,11 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=