Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Securesign project structural and acceptance tests. Based on
* Securesign releases: https://github.com/securesign/releases
* Securesign operator: https://github.com/securesign/secure-sign-operator
* Securesign Ansible collection: https://github.com/securesign/artifact-signer-ansible
* Policy Controller operator: https://github.com/securesign/policy-controller-operator

## Automation
Current automation is done via Github actions here: https://github.com/securesign/releases/actions/workflows/structural.yml
Expand Down Expand Up @@ -37,23 +38,28 @@ Run tests based on a github file:

SNAPSHOT=https://raw.githubusercontent.com/securesign/releases/refs/heads/feat/release-1.1.1/1.1.1/stable/snapshot.json \
TEST_GITHUB_TOKEN=ghp_Ae \
go test -v ./test/... --ginkgo.v
go test -v ./test/acceptance/rhtas/... --ginkgo.v

Run the same tests on a local (cloned) file:

SNAPSHOT=../releases/1.1.1/stable/snapshot.json \
go test -v ./test/... --ginkgo.v
go test -v ./test/acceptance/rhtas/... --ginkgo.v

Force different ansible collection instead of the one defined in ``snapshot.json`` file. This may be useful, when checking ansible collection not yet published:

SNAPSHOT=../releases/1.1.1/stable/snapshot.json \
ANSIBLE=https://api.github.com/repos/securesign/artifact-signer-ansible/actions/artifacts/2442056100/zip \
go test -v ./test/... --ginkgo.v
go test -v ./test/acceptance/rhtas/... --ginkgo.v

To run just individual test use ``--ginkgo.fokus-file`` parameter:

SNAPSHOT=../releases/1.1.1/stable/snapshot.json \
go test -v ./test/... --ginkgo.v --ginkgo.focus-file "ansible"
go test -v ./test/acceptance/rhtas/... --ginkgo.v --ginkgo.focus-file "ansible"

To run policy controller operator tests use:
```
go test -v ./test/acceptance/policy_controller/... --ginkgo.v
```

## Repository List
The [repositories.json](testdata/repositories.json) file is used to check of all images are published correctly. To pull the list of repositories from Pyxis API:
Expand Down
114 changes: 114 additions & 0 deletions test/acceptance/policy_controller/fbc_images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package acceptance

import (
"context"
"fmt"
"os"
"strings"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/securesign/structural-tests/test/support"
"github.com/securesign/structural-tests/test/support/olm"
)

const (
olmPackage = "policy-controller-operator"
operatorBundleImage = "registry.redhat.io/rhtas/policy-controller-operator-bundle"
fbcCatalogFileName = "catalog.json"
fbcCatalogPath = "/configs/policy-controller-operator/catalog.json"
defaultChannel = "tech-preview"
)

var _ = Describe("File-based catalog images", Ordered, func() {

defer GinkgoRecover()
var ocps []TableEntry
var bundleImage string

snapshotData, err := support.ParseSnapshotData()
Expect(err).NotTo(HaveOccurred())
for key, snapshotImage := range snapshotData.Images {
if strings.Index(key, "pco-fbc-") == 0 {
ocps = append(ocps, Entry(key, key, snapshotImage))
}
}
Expect(ocps).NotTo(BeEmpty())

bundleImage = snapshotData.Images[support.PolicyControllerOperatorBundleImageKey]

DescribeTableSubtree("ocp",
func(key, fbcImage string) {

var bundles []olm.Bundle
var channels []olm.Channel
var packages []olm.Package

It("extract catalog.json", func() {
dir, err := os.MkdirTemp("", key)
Expect(err).NotTo(HaveOccurred())
defer os.RemoveAll(dir)

Expect(support.FileFromImage(context.Background(), fbcImage, fbcCatalogPath, dir)).To(Succeed())
file, err := os.Open(dir + "/" + fbcCatalogFileName)
Expect(err).NotTo(HaveOccurred())
defer file.Close()

catalog, err := olm.ParseCatalogJSON(file)
Expect(err).NotTo(HaveOccurred())
Expect(catalog).NotTo(BeNil())

for _, obj := range catalog {
switch typedObj := obj.(type) {
case olm.Bundle:
bundles = append(bundles, typedObj)
case olm.Channel:
channels = append(channels, typedObj)
case olm.Package:
packages = append(packages, typedObj)
}
}

Expect(bundles).ToNot(BeEmpty())
Expect(channels).ToNot(BeEmpty())
Expect(packages).ToNot(BeEmpty())
})

It("extract bundle-image from snapshot.json", func() {
snapshotData, err := support.ParseSnapshotData()
Expect(err).NotTo(HaveOccurred())
Expect(snapshotData.Images).NotTo(BeEmpty())

})

It("verify package", func() {
for _, p := range packages {
Expect(p.Name).To(Equal(olmPackage))
Expect(p.DefaultChannel).To(Equal(defaultChannel))
}
})

It("verify channels", func() {
expectedChannels := []string{"tech-preview"}
Expect(channels).To(HaveLen(len(expectedChannels)))

for _, channel := range channels {
Expect(channel.Package).To(Equal(olmPackage))
Expect(expectedChannels).To(ContainElement(channel.Name))
}
})

It("contains operator-bundle", func() {
bundleImageHash := support.ExtractHash(bundleImage)
exists := false

for _, bundle := range bundles {
Expect(bundle.Package).To(Equal(olmPackage))
if bundle.Image == fmt.Sprintf("%s@sha256:%s", operatorBundleImage, bundleImageHash) {
exists = true
}
}
Expect(exists).To(BeTrue(), fmt.Sprintf("olm bundle with %s hash not found", bundleImageHash))
})
}, ocps)
})
132 changes: 132 additions & 0 deletions test/acceptance/policy_controller/operator_images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package acceptance

import (
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"regexp"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/securesign/structural-tests/test/support"
)

var ErrNotFoundInRegistry = errors.New("not found in registry")

var _ = Describe("Policy Controller Operator", Ordered, func() {

var (
snapshotData support.SnapshotData
repositories *support.RepositoryList
operatorPcoImages support.OperatorMap
operatorOtherImages support.OperatorMap
operator string
)

BeforeAll(func() {
var err error
snapshotData, err = support.ParseSnapshotData()
Expect(err).NotTo(HaveOccurred())
Expect(snapshotData.Images).NotTo(BeEmpty(), "No images were detected in snapshot file")

repositories, err = support.LoadRepositoryList()
Expect(err).NotTo(HaveOccurred())
Expect(repositories.Data).NotTo(BeEmpty(), "No images were detected in repositories file")
})

It("get operator image", func() {
operator = snapshotData.Images[support.PolicyControllerOperatorImageKey]
Expect(operator).NotTo(BeEmpty(), "Operator image not detected in snapshot file")
log.Printf("Using %s\n", operator)
})

It("get all PCO images used by this operator", func() {
valuesFile, err := support.RunImage(operator, []string{"cat"}, []string{"helm-charts/policy-controller-operator/values.yaml"})
Expect(err).NotTo(HaveOccurred())

operatorPcoImages, operatorOtherImages = support.ParsePCOperatorImages(valuesFile)
Expect(operatorPcoImages).NotTo(BeEmpty())
Expect(operatorOtherImages).NotTo(BeEmpty())
support.LogMap(fmt.Sprintf("Operator Pco images (%d):", len(operatorPcoImages)), operatorPcoImages)
support.LogMap(fmt.Sprintf("Operator other images (%d):", len(operatorOtherImages)), operatorOtherImages)
})

It("operator images are listed in registry.redhat.io", func() {
var errs []error
for _, image := range operatorPcoImages {
if repositories.FindByImage(image) == nil {
errs = append(errs, fmt.Errorf("%w: %s", ErrNotFoundInRegistry, image))
}
}
Expect(errs).To(BeEmpty())
})

It("operator PCO images are all valid", func() {
Expect(support.GetMapKeys(operatorPcoImages)).To(ContainElements(support.MandatoryPcoOperatorImageKeys()))
Expect(len(operatorPcoImages)).To(BeNumerically("==", len(support.MandatoryPcoOperatorImageKeys())))
Expect(operatorPcoImages).To(HaveEach(MatchRegexp(support.TasImageDefinitionRegexp)))
})

It("operator other images are all valid", func() {
Expect(support.GetMapKeys(operatorOtherImages)).To(ContainElements(support.OtherPCOOperatorImageKeys()))
Expect(len(operatorOtherImages)).To(BeNumerically("==", len(support.OtherPCOOperatorImageKeys())))
Expect(operatorOtherImages).To(HaveEach(MatchRegexp(support.OtherImageDefinitionRegexp)))
})

It("all image hashes are also defined in releases snapshot", func() {
mapped := make(map[string]string)
for _, imageKey := range support.MandatoryPcoOperatorImageKeys() {
oSha := support.ExtractHash(operatorPcoImages[imageKey])
if _, keyExist := snapshotData.Images[imageKey]; !keyExist {
mapped[imageKey] = "MISSING"
continue
}
sSha := support.ExtractHash(snapshotData.Images[imageKey])
if oSha == sSha {
mapped[imageKey] = "match"
} else {
mapped[imageKey] = "DIFFERENT HASHES"
}
}
Expect(mapped).To(HaveEach("match"), "Operator images are missing or have different hashes in snapshot file")
})

It("image hashes are all unique", func() {
operatorHashes := support.ExtractHashes(support.GetMapValues(operatorPcoImages))
mapped := make(map[string]int)
for _, hash := range operatorHashes {
_, exist := mapped[hash]
if exist {
mapped[hash]++
} else {
mapped[hash] = 1
}
}
Expect(mapped).To(HaveEach(1))
Expect(operatorPcoImages).To(HaveLen(len(mapped)))
})

It("operator-bundle use the right operator", func() {
dir, err := os.MkdirTemp("", "bundle")
Expect(err).NotTo(HaveOccurred())
defer os.RemoveAll(dir)

Expect(support.FileFromImage(
context.Background(),
snapshotData.Images[support.PolicyControllerOperatorBundleImageKey],
support.PolicyControllerOperatorBundleClusterServiceVersionPath, dir),
).To(Succeed())
fileContent, err := os.ReadFile(filepath.Join(dir, support.PolicyControllerOperatorBundleClusterServiceVersionFile))
Expect(err).NotTo(HaveOccurred())
Expect(fileContent).NotTo(BeEmpty())

operatorHash := support.ExtractHash(snapshotData.Images[support.PolicyControllerOperatorImageKey])
re := regexp.MustCompile(`(\w+:\s*[\w./-]+operator[\w-]*@sha256:` + operatorHash + `)`)
matches := re.FindAllString(string(fileContent), -1)
Expect(matches).NotTo(BeEmpty())
support.LogArray("Operator images found in operator-bundle:", matches)
})
})
18 changes: 18 additions & 0 deletions test/acceptance/policy_controller/pco_acceptance_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package acceptance

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/format"
"sigs.k8s.io/controller-runtime/pkg/log"
)

func TestAcceptance(t *testing.T) {
format.MaxLength = 0

RegisterFailHandler(Fail)
log.SetLogger(GinkgoLogr)
RunSpecs(t, "Policy Controller Acceptance Tests Suite")
}
43 changes: 43 additions & 0 deletions test/acceptance/policy_controller/releases_images_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package acceptance

import (
"fmt"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/securesign/structural-tests/test/support"
)

var _ = Describe("Policy Controller Operator Releases", Ordered, func() {

var (
snapshotData support.SnapshotData
)

It("snapshot.json file exist and is parseable", func() {
var err error
snapshotData, err = support.ParseSnapshotData()
Expect(err).NotTo(HaveOccurred())
support.LogMap(fmt.Sprintf("Snapshot images (%d):", len(snapshotData.Images)), snapshotData.Images)
Expect(snapshotData.Images).NotTo(BeEmpty(), "No images were detected in snapshot file")
})

It("snapshot.json file contains valid images", func() {
Expect(snapshotData.Images).To(HaveEach(MatchRegexp(support.SnapshotImageDefinitionRegexp)))
})

It("snapshot.json file image snapshots are all unique", func() {
snapshotHashes := support.ExtractHashes(support.GetMapValues(snapshotData.Images))
mapped := make(map[string]int)
for _, hash := range snapshotHashes {
_, exist := mapped[hash]
if exist {
mapped[hash]++
} else {
mapped[hash] = 1
}
}
Expect(mapped).To(HaveEach(1))
Expect(len(snapshotData.Images)).To(BeNumerically("==", len(mapped)))
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ var _ = Describe("Trusted Artifact Signer Operator", Ordered, func() {
})

It("get all TAS images used by this operator", func() {
helpLogs, err := support.RunImage(operator, []string{"-h"})
helpLogs, err := support.RunImage(operator, []string{}, []string{"-h"})
Expect(err).NotTo(HaveOccurred())

operatorTasImages, operatorOtherImages = support.ParseOperatorImages(helpLogs)
Expand Down
21 changes: 21 additions & 0 deletions test/support/acceptance.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ func ParseOperatorImages(helpContent string) (OperatorMap, OperatorMap) {
return operatorTasImages, operatorOtherImages
}

func ParsePCOperatorImages(valuesFile string) (OperatorMap, OperatorMap) {
const minMatchLength = 3
imageRegex := regexp.MustCompile(`repository:\s*([^\s]+)[\s\S]+?version:\s*([^\s]+)[\s\S]+?`)
matches := imageRegex.FindAllStringSubmatch(valuesFile, -1)
operatorPcoImages := make(OperatorMap)
operatorOtherImages := make(OperatorMap)
for _, match := range matches {
if len(match) < minMatchLength {
continue
}
repo, version := match[1], match[2]
switch {
case strings.Contains(repo, "registry.redhat.io/rhtas/policy-controller-rhel9"):
operatorPcoImages["policy-controller-image"] = fmt.Sprintf("%s@%s", repo, version)
case strings.Contains(repo, "registry.redhat.io/openshift4/ose-cli"):
operatorOtherImages["ose-cli-image"] = fmt.Sprintf("%s@sha256:%s", repo, version)
}
}
return operatorPcoImages, operatorOtherImages
}

func MapAnsibleImages(ansibleDefinitionFileContent []byte) (AnsibleMap, error) {
var ansibleImages AnsibleMap
err := yaml.Unmarshal(ansibleDefinitionFileContent, &ansibleImages)
Expand Down
Loading
Loading