Skip to content

Commit

Permalink
new(tests): update tests for push command
Browse files Browse the repository at this point in the history
Signed-off-by: Aldo Lacuku <[email protected]>
  • Loading branch information
alacuku committed Mar 18, 2024
1 parent 2052aa0 commit 1df4a40
Show file tree
Hide file tree
Showing 4 changed files with 940 additions and 667 deletions.
308 changes: 308 additions & 0 deletions cmd/registry/push/push_plugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
// Copyright 2024 The Falco Authors

Check failure on line 1 in cmd/registry/push/push_plugins_test.go

View workflow job for this annotation

GitHub Actions / Lint golang files

Actual: Copyright 2024 The Falco Authors
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

package push_test

import (
"fmt"
"os"
"path/filepath"
"regexp"

. "github.com/onsi/ginkgo/v2"

Check warning on line 25 in cmd/registry/push/push_plugins_test.go

View workflow job for this annotation

GitHub Actions / Lint golang files

dot-imports: should not use dot imports (revive)
. "github.com/onsi/gomega"

Check warning on line 26 in cmd/registry/push/push_plugins_test.go

View workflow job for this annotation

GitHub Actions / Lint golang files

dot-imports: should not use dot imports (revive)
"github.com/onsi/gomega/gbytes"
v1 "github.com/opencontainers/image-spec/specs-go/v1"

"github.com/falcosecurity/falcoctl/cmd"
"github.com/falcosecurity/falcoctl/internal/utils"
"github.com/falcosecurity/falcoctl/pkg/oci"
testutils "github.com/falcosecurity/falcoctl/pkg/test"
)

var _ = Describe("pushing plugins", func() {
var (
registryCmd = "registry"
pushCmd = "push"
version = "1.1.1"
// fullRepoName is set each time before each test.
fullRepoName string
// repoName same as fullRepoName.
repoName string
// It is set in the config layer.
artifactNameInConfigLayer = "test-push-plugins"
pushedTags = []string{"tag1", "tag2", "latest"}

// Plugin's platforms.
platformARM64 = "linux/arm64"
platformAMD64 = "linux/amd64"

// Paths pointing to plugins that will be pushed.
// Some of the functions expect these two variable to be set to valid paths.
// They are set in beforeEach blocks by tests that need them.
pluginOne string
pluginTwo string
// Data fetched from registry and used for assertions.
pluginData *testutils.PluginArtifact
)

const (
// Used as flags for all the test cases.
requirement = "plugin_api_version:3.2.1"
anSource = "myrepo.com/rules.git"
pluginsRepoBaseName = "push-plugins-tests"
)

var AssertSuccessBehaviour = func(deps []oci.ArtifactDependency, reqs []oci.ArtifactRequirement, annotations map[string]string, platforms []string) {
It("should succeed", func() {
// We do not check the error here since we are checking it after
// pushing the artifact.
By("checking no error in output")
Expect(output).ShouldNot(gbytes.Say("ERROR"))
Expect(output).ShouldNot(gbytes.Say("Unable to remove temporary dir"))

By("checking descriptor")
Expect(pluginData.Descriptor.MediaType).Should(Equal(v1.MediaTypeImageIndex))
Expect(output).Should(gbytes.Say(regexp.QuoteMeta(pluginData.Descriptor.Digest.String())))

By("checking index")
Expect(pluginData.Index.Manifests).Should(HaveLen(len(platforms)))

By("checking platforms")
for _, p := range platforms {
Expect(pluginData.Platforms).Should(HaveKey(p))
}

By("checking config layers")
for plat, p := range pluginData.Platforms {
By(fmt.Sprintf("platform %s", plat))
Expect(p.Config.Version).Should(Equal(version))
Expect(p.Config.Name).Should(Equal(artifactNameInConfigLayer))

By("checking dependencies")
Expect(p.Config.Dependencies).Should(HaveLen(len(deps)))
for _, dep := range deps {
Expect(p.Config.Dependencies).Should(ContainElement(dep))
}

By("checking requirements")
Expect(p.Config.Requirements).Should(HaveLen(len(reqs)))
for _, req := range reqs {
Expect(p.Config.Requirements).Should(ContainElement(req))
}

By("checking annotations")
// The creation timestamp is always present.
Expect(p.Manifest.Annotations).Should(HaveLen(len(annotations) + 1))
for key, val := range annotations {
Expect(p.Manifest.Annotations).Should(HaveKeyWithValue(key, val))
}
}

By("checking tags")
Expect(pluginData.Tags).Should(HaveLen(len(pushedTags)))
Expect(pluginData.Tags).Should(ContainElements(pushedTags))

By("checking that temporary dirs have been removed")
entries, err := os.ReadDir("/tmp")
Expect(err).ShouldNot(HaveOccurred())
for _, e := range entries {
if e.IsDir() {
matched, err := filepath.Match(utils.TmpDirPrefix+"*", regexp.QuoteMeta(e.Name()))
Expect(err).ShouldNot(HaveOccurred())
Expect(matched).ShouldNot(BeTrue())
}
}
})
}

// Each test gets its own root command and runs it.
// The err variable is asserted by each test.
JustBeforeEach(func() {
rootCmd = cmd.New(ctx, opt)
err = executeRoot(args)
})

JustAfterEach(func() {
// Reset the status after each test.
// This variable could be changed by single tests.
// Make sure to set them at their default values.
Expect(output.Clear()).ShouldNot(HaveOccurred())
artifactNameInConfigLayer = "test-plugin"
pushedTags = []string{"tag1", "tag2", "latest"}
pluginOne = ""
pluginTwo = ""
})

Context("success", func() {
JustBeforeEach(func() {
// Check the returned error before proceeding.
Expect(err).ShouldNot(HaveOccurred())
pluginData, err = testutils.FetchPluginFromRegistry(ctx, repoName, pushedTags[0], orasRegistry)
Expect(err).ShouldNot(HaveOccurred())
})

When("two platforms, shared objects", func() {
BeforeEach(func() {
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
pluginOne = pluginAMD64SO
pluginTwo = pluginARM64SO

args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, pluginTwo, "--type", "plugin", "--platform",
platformAMD64, "--platform", platformARM64, "--version", version, "--config", configFile,
"--plain-http", "--depends-on", "my-test:4.3.2", "--requires", requirement, "--annotation-source", anSource,
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
})

// We expect to succeed:
// deps should be the one provided by the cli flags.
AssertSuccessBehaviour([]oci.ArtifactDependency{{
Name: "my-test",
Version: "4.3.2",
Alternatives: nil,
}}, []oci.ArtifactRequirement{
{
Name: "plugin_api_version",
Version: "3.2.1",
},
}, map[string]string{
"org.opencontainers.image.source": anSource,
}, []string{
platformAMD64, platformARM64,
})
})

When("two platforms, shared object + tgz", func() {
BeforeEach(func() {
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
pluginOne = pluginAMD64SO
pluginTwo = plugintgz

args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, pluginTwo, "--type", "plugin", "--platform",
platformAMD64, "--platform", platformARM64, "--version", version, "--config", configFile,
"--plain-http", "--depends-on", "my-test:4.3.2", "--requires", requirement, "--annotation-source", anSource,
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
})

AssertSuccessBehaviour([]oci.ArtifactDependency{{
Name: "my-test",
Version: "4.3.2",
Alternatives: nil,
}}, []oci.ArtifactRequirement{
{
Name: "plugin_api_version",
Version: "3.2.1",
},
}, map[string]string{
"org.opencontainers.image.source": anSource,
}, []string{
platformAMD64, platformARM64,
})
})

When("one platform, shared object, no reqs", func() {
BeforeEach(func() {
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
pluginOne = pluginAMD64SO
args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, "--type", "plugin", "--platform",
platformAMD64, "--version", version, "--config", configFile,
"--plain-http", "--depends-on", "my-test:4.3.2", "--annotation-source", anSource,
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
})
// We expect to succeed and that the requirement is extracted by the plugin itself
AssertSuccessBehaviour([]oci.ArtifactDependency{{
Name: "my-test",
Version: "4.3.2",
Alternatives: nil,
}}, []oci.ArtifactRequirement{
{
Name: "plugin_api_version",
Version: "3.1.0",
},
}, map[string]string{
"org.opencontainers.image.source": anSource,
}, []string{
platformAMD64,
})
})

When("one platform, shared object, no reqs, no plugin of same platform", func() {
BeforeEach(func() {
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
pluginOne = pluginARM64SO
args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, "--type", "plugin", "--platform",
platformARM64, "--version", version, "--config", configFile,
"--plain-http", "--depends-on", "my-test:4.3.2", "--annotation-source", anSource,
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
})
// We expect to succeed and that the requirement is not set:
// 1: no user provided;
// 2: plugin can not be loaded since is of different platform than the one where we are running.
AssertSuccessBehaviour([]oci.ArtifactDependency{{
Name: "my-test",
Version: "4.3.2",
Alternatives: nil,
}}, []oci.ArtifactRequirement{},
map[string]string{
"org.opencontainers.image.source": anSource,
}, []string{
platformARM64,
})
})

When("two platforms, shared object + tgz, no reqs, tgz + no plugin of same platform, no deps", func() {
BeforeEach(func() {
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
pluginOne = plugintgz
pluginTwo = pluginARM64SO
args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, pluginTwo, "--type", "plugin", "--platform",
platformAMD64, "--platform", platformARM64, "--version", version, "--config", configFile,
"--plain-http", "--annotation-source", anSource,
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
})
// We expect to succeed and that the requirement is not set:
// 1: no user provided;
// 2: plugin can not be loaded since is of different platform than the one where we are running.
// 3: the other plugin is in tgz format cannot be loaded.
AssertSuccessBehaviour([]oci.ArtifactDependency{},
[]oci.ArtifactRequirement{},
map[string]string{
"org.opencontainers.image.source": anSource,
}, []string{
platformARM64,
platformAMD64,
})
})
})

Context("failure", func() {
When("one platform, shared object, no reqs, unable to load plugin", func() {
BeforeEach(func() {
repoName, fullRepoName = randomRulesRepoName(registry, pluginsRepoBaseName)
pluginOne = pluginARM64SO
args = []string{registryCmd, pushCmd, fullRepoName, pluginOne, "--type", "plugin", "--platform",
platformAMD64, "--version", version, "--config", configFile,
"--plain-http", "--depends-on", "my-test:4.3.2", "--annotation-source", anSource,
"--tag", pushedTags[0], "--tag", pushedTags[1], "--tag", pushedTags[2], "--name", artifactNameInConfigLayer}
})
It("expect to fail since cannot load the shared object but the platform is the same", func() {
Expect(err).To(HaveOccurred())
Expect(err.Error()).Should(Equal("unable to open plugin \"../../../pkg/test/data/libk8smeta-arm64.so\":" +
" can't load plugin dynamic library: ../../../pkg/test/data/libk8smeta-arm64.so: cannot open shared object file: No such file or directory"))
})
})
})
})
Loading

0 comments on commit 1df4a40

Please sign in to comment.