From 6d7428540955baedbdf7fd9f5b5b09a953c196c5 Mon Sep 17 00:00:00 2001 From: "Jose R. Gonzalez" Date: Wed, 11 Jun 2025 15:28:23 -0500 Subject: [PATCH] Remove unpromoted alpha features, organize promoted alpha features Signed-off-by: Jose R. Gonzalez --- go.mod | 1 - go.sum | 2 - internal/ansible/ansible.go | 424 ---------------- internal/ansible/ansible_suite_test.go | 13 - internal/ansible/ansible_test.go | 474 ------------------ .../certifycontainers/certifycontainers.go | 160 ------ .../certifycontainers_suite_test.go | 13 - .../certifyhelmcharts/certifyhelmcharts.go | 139 ----- .../certifyhelmcharts_suite_test.go | 13 - .../cmd/certifyoperators/certifyoperators.go | 139 ----- .../certifyoperators_suite_test.go | 13 - .../productctl/cmd/certtargets/certtargets.go | 278 ---------- .../cmd/certtargets/certtargets_suite_test.go | 13 - internal/cmd/productctl/cmd/cmd.go | 21 +- .../{lsp/lsp.go => jsonschema/jsonschema.go} | 6 +- .../jsonschema_suite_test.go} | 4 +- .../jsonschema_test.go} | 8 +- 17 files changed, 11 insertions(+), 1710 deletions(-) delete mode 100644 internal/ansible/ansible.go delete mode 100644 internal/ansible/ansible_suite_test.go delete mode 100644 internal/ansible/ansible_test.go delete mode 100644 internal/cmd/productctl/cmd/certifycontainers/certifycontainers.go delete mode 100644 internal/cmd/productctl/cmd/certifycontainers/certifycontainers_suite_test.go delete mode 100644 internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts.go delete mode 100644 internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts_suite_test.go delete mode 100644 internal/cmd/productctl/cmd/certifyoperators/certifyoperators.go delete mode 100644 internal/cmd/productctl/cmd/certifyoperators/certifyoperators_suite_test.go delete mode 100644 internal/cmd/productctl/cmd/certtargets/certtargets.go delete mode 100644 internal/cmd/productctl/cmd/certtargets/certtargets_suite_test.go rename internal/cmd/productctl/cmd/{lsp/lsp.go => jsonschema/jsonschema.go} (86%) rename internal/cmd/productctl/cmd/{lsp/lsp_suite_test.go => jsonschema/jsonschema_suite_test.go} (71%) rename internal/cmd/productctl/cmd/{lsp/lsp_test.go => jsonschema/jsonschema_test.go} (83%) diff --git a/go.mod b/go.mod index acda483..fa9e4a6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/opdev/productctl go 1.24.0 require ( - dario.cat/mergo v1.0.2 github.com/Khan/genqlient v0.8.1 github.com/invopop/jsonschema v0.13.0 github.com/onsi/ginkgo/v2 v2.23.4 diff --git a/go.sum b/go.sum index 787b706..c12d210 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= -dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/Khan/genqlient v0.8.1 h1:wtOCc8N9rNynRLXN3k3CnfzheCUNKBcvXmVv5zt6WCs= github.com/Khan/genqlient v0.8.1/go.mod h1:R2G6DzjBvCbhjsEajfRjbWdVglSH/73kSivC9TLWVjU= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= diff --git a/internal/ansible/ansible.go b/internal/ansible/ansible.go deleted file mode 100644 index 8ccb2aa..0000000 --- a/internal/ansible/ansible.go +++ /dev/null @@ -1,424 +0,0 @@ -package ansible - -import ( - "context" - "errors" - "strings" - - "github.com/opdev/productctl/internal/logger" - "github.com/opdev/productctl/internal/resource" -) - -var ErrNoComponentsDeclared = errors.New("no components declared") - -const ( - AnsibleConnectionLocal = "local" - InventoryKeyContainer = "container_components" - InventoryKeyOperator = "operator_components" - InventoryKeyHelmChart = "helm_chart_components" -) - -// HelmChartComponentHostVars contains the variables for a container component -type HelmChartComponentHostVars struct { - AnsibleConnection string `json:"ansible_connection"` - ChartURI string `json:"chart_uri"` - Component *resource.Component `json:"component"` - Product *ProductMeta `json:"product"` - // BUG: sigs.k8s.io/yaml does not seem to support inlining, or else we'd be - // inlining this key. For now, we'll set a key. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -// ContainerComponentHostVars contains the variables for a container component -// cert. -type ContainerComponentHostVars struct { - AnsibleConnection string `json:"ansible_connection"` - Image string `json:"image"` - Component *resource.Component `json:"component"` - Product *ProductMeta `json:"product"` - // BUG: sigs.k8s.io/yaml does not seem to support inlining, or else we'd be - // inlining this key. For now, we'll set a key. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -type ProductMeta struct { - ProductName string `json:"product_name"` - ProductID string `json:"product_id"` -} - -// GenerateInventory produces the Ansible Inventory content representing the -// given product's components and the associated mapping containing -// certification targets for each component. -func GenerateInventory( - ctx context.Context, - product *resource.ProductListingDeclaration, - mapping MappingDeclaration, -) (map[string]any, error) { - L := logger.FromContextOrDiscard(ctx) - if !product.HasComponents() { - return nil, ErrNoComponentsDeclared - } - - L.Debug("Checking product for container components") - containerHosts, err := generateContainerComponentInventory(ctx, product, mapping.ContainerComponents) - if err != nil { - return nil, err - } - - L.Debug("Checking product for helm chart components") - helmHosts, err := generateHelmComponentInventory(ctx, product, mapping.HelmChartComponents) - if err != nil { - return nil, err - } - - L.Debug("Checking product for operator components") - operatorHosts, err := generateOperatorComponentInventory(ctx, product, mapping.OperatorComponents) - if err != nil { - return nil, err - } - - inventory := map[string]any{ - InventoryKeyContainer: map[string]any{ - "hosts": containerHosts, - }, - InventoryKeyOperator: map[string]any{ - "hosts": operatorHosts, - }, - InventoryKeyHelmChart: map[string]any{ - "hosts": helmHosts, - }, - } - - return inventory, nil -} - -// generateContainerComponentInventory produces an Ansible inventory merging -// product and mapping. -func generateContainerComponentInventory( - ctx context.Context, - product *resource.ProductListingDeclaration, - mapping ComponentCertificationConfig[ContainerCertTarget], -) (HostMap[ContainerComponentHostVars], error) { // nolint:unparam // ignoring lint flagging error return value that's always nil - L := logger.FromContextOrDiscard(ctx) - - hosts := map[string]*ContainerComponentHostVars{} - - for _, cmp := range product.With.Components { - if cmp.Container == nil { - L.Debug( - "Skipping component that does not have container metadata", - "component_id", cmp.ID, - "component_name", cmp.Name, - ) - continue - } - - if cmp.Container.OSContentType == resource.ContentTypeOperatorBundle { - L.Debug( - "Skipping component that isn't an application container type, inferred from content type metadata", - "component_id", cmp.ID, - "component_name", cmp.Name, - "os_content_type", cmp.Container.OSContentType, - ) - continue - } - - var certificationCfg ContainerCertTarget - var entryExists bool - if certificationCfg, entryExists = mapping[cmp.ID]; !entryExists { - L.Debug( - "component did not have a mapping entry. refusing to generate an inventory entry for component", - "component_id", cmp.ID, - "component_name", cmp.Name, - ) - continue - } - - for _, tagConfig := range certificationCfg.Tags { - imageTarget := strings.Join([]string{certificationCfg.ImageRef, tagConfig.Tag}, ":") - vars := ContainerComponentHostVars{ - ToolFlags: certificationCfg.ToolFlags, - AnsibleConnection: AnsibleConnectionLocal, - Image: imageTarget, - Product: &ProductMeta{ - ProductName: product.Spec.Name, - ProductID: product.Spec.ID, - }, - Component: cmp, - } - - // if tagConfig.ToolFlags != nil { - if len(tagConfig.ToolFlags) > 0 { - L.Debug("container tag has custom tool flags.") - vars.ToolFlags = tagConfig.ToolFlags - } - - // TODO: if it doesn't have a component ID, we should omit it or - // throw an error. - name := strings.Join([]string{cmp.ID, vars.Image}, "-") - name = normalizeContainerHostname(name) - hosts[name] = &vars - } - } - - return hosts, nil -} - -// generateOperatorComponentInventory produces an ansible inventory items for -// operators, merging product declaration and certification target mappings. -func generateOperatorComponentInventory( - ctx context.Context, - product *resource.ProductListingDeclaration, - mapping ComponentCertificationConfig[OperatorCertTarget], -) (HostMap[OperatorComponentHostVars], error) { // nolint:unparam // ignoring lint flagging error return value that's always nil - L := logger.FromContextOrDiscard(ctx) - - hosts := HostMap[OperatorComponentHostVars]{} - - for _, cmp := range product.With.Components { - if cmp.Container == nil { - L.Debug( - "Skipping component that does not have container metadata", - "component_id", cmp.ID, - "component_name", cmp.Name, - ) - continue - } - - if cmp.Container.OSContentType != resource.ContentTypeOperatorBundle { - L.Debug( - "Skipping component that isn't an operator container type, inferred from content type metadata", - "component_id", cmp.ID, - "component_name", cmp.Name, - "os_content_type", cmp.Container.OSContentType, - ) - continue - } - - var certificationCfg OperatorCertTarget - var entryExists bool - if certificationCfg, entryExists = mapping[cmp.ID]; !entryExists { - L.Debug( - "component did not have a mapping entry. refusing to generate an inventory entry for component", - "component_id", cmp.ID, - "component_name", cmp.Name, - ) - continue - } - - // for i, imageTarget := range imageRefMatrix(certificationCfg.ImageRef, tagIter) { - for _, tagConfig := range certificationCfg.Tags { - imageTarget := strings.Join([]string{certificationCfg.ImageRef, tagConfig.Tag}, ":") - vars := OperatorComponentHostVars{ - ToolFlags: certificationCfg.ToolFlags, - AnsibleConnection: AnsibleConnectionLocal, - Image: imageTarget, - Product: &ProductMeta{ - ProductName: product.Spec.Name, - ProductID: product.Spec.ID, - }, - Component: cmp, - IndexImage: certificationCfg.IndexImage, - } - - // All "hosts" get the base index image unless they've specified one - // along with a specific tag. - if tagConfig.IndexImage != "" { - L.Debug("operator tag has custom index image.") - vars.IndexImage = tagConfig.IndexImage - } - - if tagConfig.ToolFlags != nil { - L.Debug("operator tag has custom tool flags.") - vars.ToolFlags = tagConfig.ToolFlags - } - - // TODO: if it doesn't have a component ID, we should omit it or - // throw an error. - name := strings.Join([]string{cmp.ID, vars.Image}, "-") - name = normalizeContainerHostname(name) - hosts[name] = &vars - } - } - - return hosts, nil -} - -// generateHelmComponentInventory produces the helm components of the Ansible -// inventory. -func generateHelmComponentInventory( - ctx context.Context, - product *resource.ProductListingDeclaration, - mapping ComponentCertificationConfig[HelmCertTarget], -) (HostMap[HelmChartComponentHostVars], error) { // nolint:unparam // ignoring lint flagging error return value that's always nil - L := logger.FromContextOrDiscard(ctx) - - hosts := HostMap[HelmChartComponentHostVars]{} - - for _, cmp := range product.With.Components { - if cmp.HelmChart == nil { - L.Debug( - "Skipping component that isn't a Helm chart, inferred from content type metadata", - "component_id", cmp.ID, - "component_name", cmp.Name, - "component_type", cmp.Type, - ) - continue - } - - var certificationCfg HelmCertTarget - var entryExists bool - if certificationCfg, entryExists = mapping[cmp.ID]; !entryExists { - L.Debug( - "component did not have a mapping entry. refusing to generate an inventory entry for component", - "component_id", cmp.ID, - "component_name", cmp.Name, - ) - continue - } - - vars := HelmChartComponentHostVars{ - ToolFlags: certificationCfg.ToolFlags, - ChartURI: certificationCfg.ChartURI, - AnsibleConnection: AnsibleConnectionLocal, - Product: &ProductMeta{ - ProductName: product.Spec.Name, - ProductID: product.Spec.ID, - }, - Component: cmp, - } - - name := strings.Join([]string{cmp.ID, vars.ChartURI}, "-") - name = normalizeChartHostname(name) - hosts[name] = &vars - } - - return hosts, nil -} - -// MappingDeclaration represents the extra data provided to be merged with a -// product declaration in order to produce a usable Ansible inventory. -// -// TODO: We should offer a JSONSchema corresponding to this data, given that -// this data will be hand-typed by a user. -type MappingDeclaration struct { - ContainerComponents ComponentCertificationConfig[ContainerCertTarget] `json:"container_components"` - HelmChartComponents ComponentCertificationConfig[HelmCertTarget] `json:"helm_chart_components"` - OperatorComponents ComponentCertificationConfig[OperatorCertTarget] `json:"operator_components"` -} - -// HelmCertTarget contains required inputs necessary to run helm certification -// tooling. -type HelmCertTarget struct { - ChartURI string `json:"chart_uri"` - ToolFlags map[string]any `json:"tool_flags,omitempty"` -} - -// OperatorCertTarget contains information necessary to run Operator -// certification tooling. -type OperatorCertTarget struct { - // ImageRef is the image URI for the operator to certify. No tags should be - // specified here. - ImageRef string `json:"image_ref"` - - // Tags contains the list of tags to certify. - Tags []OperatorTagandOptions `json:"tags"` - - // IndexImage is the catalog that contains the operator used for - // certification. Users are required to build these catalogs. - IndexImage string `json:"index_image"` - // BUG: sigs.k8s.io/yaml does not seem to support inlining, or else we'd be - // inlining this key. For now, we'll set a key. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -// OperatorTagandOptions is a tag to certify, and any configurables that can -// change on a per-tag basis. -type OperatorTagandOptions struct { - // The image tag to use for certification. E.g. v1.0.0. - Tag string `json:"tag"` - // IndexImage represents a custom index image to use for this particular - // tag. If this is not set, the fallback IndexImage, set at the - // OperatorCertTarget is used instead. - IndexImage string `json:"index_image,omitempty"` - // ToolFlags contains inputs necessary to run the certification tooling - // against a specific tag. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -// ContainertagAndOptions is a tag to certify, and any configurables that can -// change on a per-tag basis. -type ContainerTagAndOptions struct { - // The image tag to use for certification. E.g. v1.0.0. - Tag string `json:"tag"` - // ToolFlags contains inputs necessary to run the certification tooling - // against a specific tag. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -// ContainerCertTarget represents the image's URI and all tags that the user -// wants to certify -type ContainerCertTarget struct { - ImageRef string `json:"image_ref"` - Tags []ContainerTagAndOptions `json:"tags"` - // BUG: sigs.k8s.io/yaml does not seem to support inlining, or else we'd be - // inlining this key. For now, we'll set a key. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -// ComponentCertificationConfig is a map of Component IDs to the corresponding -// certification inputs. -// -// In effect, this is a mapping of a component's ID (as it would exist in a -// product's declaration) to the configuration inputs needed to run -// certification tools against that product. -type ComponentCertificationConfig[T ContainerCertTarget | HelmCertTarget | OperatorCertTarget] map[string]T - -type OperatorComponentHostVars struct { - AnsibleConnection string `json:"ansible_connection"` - Image string `json:"image"` - IndexImage string `json:"index_image"` - Component *resource.Component `json:"component"` - Product *ProductMeta `json:"product"` - // BUG: sigs.k8s.io/yaml does not seem to support inlining, or else we'd be - // inlining this key. For now, we'll set a key. - ToolFlags map[string]any `json:"tool_flags,inline,omitempty"` -} - -// HostMap map is a representation of an ansible Host map, where a given host -// name corresponds to a collection of variables. -type HostMap[T ContainerComponentHostVars | HelmChartComponentHostVars | OperatorComponentHostVars] map[string]*T - -// normalizeStringFn defines a string manipulation to be used in a processing strings. -type normalizeStringFn = func(s string) string - -// normalizeString processes s with all normalizationFns, in order, and returns -// the result. -func normalizeString(s string, normalizationFns ...normalizeStringFn) string { - for _, fn := range normalizationFns { - s = fn(s) - } - - return s -} - -func normalizeContainerHostname(s string) string { - return normalizeString( - s, - // Remove the colon from the tag - func(n string) string { return strings.Replace(n, ":", "_", -1) }, - // Remove slashes from URI - func(n string) string { return strings.Replace(n, "/", "_", -1) }, - ) -} - -func normalizeChartHostname(s string) string { - return normalizeString( - s, - // Strip protocol - func(n string) string { return strings.Replace(n, "https://", "remotechart-", -1) }, - func(n string) string { return strings.Replace(n, "http://", "remotechart-", -1) }, - // Remove remaining slashes from URI - func(n string) string { return strings.Replace(n, "/", "_", -1) }, - ) -} diff --git a/internal/ansible/ansible_suite_test.go b/internal/ansible/ansible_suite_test.go deleted file mode 100644 index e8d3b6a..0000000 --- a/internal/ansible/ansible_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package ansible_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestAnsible(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Ansible Suite") -} diff --git a/internal/ansible/ansible_test.go b/internal/ansible/ansible_test.go deleted file mode 100644 index 57a59bd..0000000 --- a/internal/ansible/ansible_test.go +++ /dev/null @@ -1,474 +0,0 @@ -package ansible - -import ( - "context" - "fmt" - "maps" - "strings" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/opdev/productctl/internal/resource" - - "sigs.k8s.io/yaml" -) - -var _ = Describe("Ansible", func() { - var ( - ctx context.Context - listing *resource.ProductListingDeclaration - ) - - BeforeEach(func() { - ctx = context.TODO() - listing = &resource.ProductListingDeclaration{} - }) - - When("generating inventory for all components", func() { - var mapping MappingDeclaration - - BeforeEach(func() { - mapping = MappingDeclaration{ - ContainerComponents: ComponentCertificationConfig[ContainerCertTarget]{}, - HelmChartComponents: ComponentCertificationConfig[HelmCertTarget]{}, - OperatorComponents: ComponentCertificationConfig[OperatorCertTarget]{}, - } - }) - - When("the product listing has no componenents defined", func() { - It("should return an error", func() { - _, err := GenerateInventory(ctx, listing, mapping) - Expect(err).To(MatchError(ErrNoComponentsDeclared)) - }) - }) - - When("valid components of each type are defined in the listing", func() { - var container, operator, helmchart resource.Component - BeforeEach(func() { - container = resource.Component{ - ID: "container", - Type: resource.ComponentTypeContainer, - Container: &resource.ContainerComponent{ - Type: resource.ComponentTypeContainer, - OSContentType: resource.ContentTypeUBI, - }, - } - operator = resource.Component{ - ID: "operator", - Type: resource.ComponentTypeContainer, - Container: &resource.ContainerComponent{ - Type: resource.ComponentTypeContainer, - OSContentType: resource.ContentTypeOperatorBundle, - DistributionMethod: resource.ContainerDistributionExternal, - }, - } - - helmchart = resource.Component{ - ID: "helmchart", - Type: resource.ComponentTypeHelmChart, - HelmChart: &resource.HelmChartComponent{ - ChartName: "placeholder-chart", - }, - } - - listing.With.Components = []*resource.Component{ - &container, - &operator, - &helmchart, - } - }) - When("valid component mappings exist for each component", func() { - BeforeEach(func() { - mapping.ContainerComponents[container.ID] = ContainerCertTarget{ - ImageRef: "example.com/example/image", - Tags: []ContainerTagAndOptions{{Tag: "tag"}}, - } - mapping.OperatorComponents[operator.ID] = OperatorCertTarget{ - ImageRef: "example.com/example/image", - Tags: []OperatorTagandOptions{{Tag: "tag"}}, - } - mapping.HelmChartComponents[helmchart.ID] = HelmCertTarget{ - ChartURI: "https://example.com/path/to/chart.0.0.1.tgz", - } - }) - }) - It("should be valid YAML", func() { - inventory, err := GenerateInventory(ctx, listing, mapping) - Expect(err).ToNot(HaveOccurred()) - Expect(inventory[InventoryKeyContainer]).To(HaveLen(1)) - Expect(inventory[InventoryKeyOperator]).To(HaveLen(1)) - Expect(inventory[InventoryKeyHelmChart]).To(HaveLen(1)) - - b, err := yaml.Marshal(inventory) - Expect(err).ToNot(HaveOccurred()) - Expect(b).ToNot(BeEmpty()) - }) - }) - }) - - When("generating inventory for container components", func() { - var certTarget ComponentCertificationConfig[ContainerCertTarget] - - BeforeEach(func() { - certTarget = ComponentCertificationConfig[ContainerCertTarget]{} - }) - - When("the product contains no container components", func() { - It("should produce an empty container component host map", func() { - hostmap, err := generateContainerComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(0)) - }) - }) - - When("the product contains a valid application container component", func() { - var validApplicationContainerComponent resource.Component - BeforeEach(func() { - validApplicationContainerComponent = resource.Component{ - ID: "placeholder", - Type: resource.ComponentTypeContainer, - Container: &resource.ContainerComponent{ - Type: resource.ComponentTypeContainer, - OSContentType: resource.ContentTypeUBI, - }, - } - - listing.With.Components = []*resource.Component{&validApplicationContainerComponent} - }) - When("the mapping does not contain a corresponding entry", func() { - It("should skip the component and produce an empty host map", func() { - hostmap, err := generateContainerComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(0)) - }) - }) - - When("the mapping contains a corresponding entry", func() { - var validContainerCertTarget ContainerCertTarget - BeforeEach(func() { - validContainerCertTarget = ContainerCertTarget{ - ImageRef: "example.com/example/image", - Tags: []ContainerTagAndOptions{{Tag: "tag"}}, - } - - certTarget[validApplicationContainerComponent.ID] = validContainerCertTarget - }) - - It("should add the container to the hostmap", func() { - hostmap, err := generateContainerComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - imageTarget := strings.Join([]string{validContainerCertTarget.ImageRef, validContainerCertTarget.Tags[0].Tag}, ":") - hostname := normalizeContainerHostname( - strings.Join([]string{ - validApplicationContainerComponent.ID, - imageTarget, - }, "-"), - ) - Expect(hostmap).To(HaveKey(hostname)) - Expect(hostmap[hostname].Image).To(Equal(imageTarget)) - Expect(hostmap[hostname].Component.ID).To(Equal(validApplicationContainerComponent.ID)) - }) - - When("top level tool flags are defined", func() { - var topLevelToolFlags map[string]any - BeforeEach(func() { - validContainerCertTarget.ToolFlags = map[string]any{} - topLevelToolFlags = map[string]any{"foo": 3} - maps.Copy(validContainerCertTarget.ToolFlags, topLevelToolFlags) - // Re-add the target to the mapping - certTarget[validApplicationContainerComponent.ID] = validContainerCertTarget - }) - - It("should pass the tool flags to the returned host map", func() { - hostmap, err := generateContainerComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - imageTarget := strings.Join([]string{validContainerCertTarget.ImageRef, validContainerCertTarget.Tags[0].Tag}, ":") - hostname := normalizeContainerHostname( - strings.Join([]string{ - validApplicationContainerComponent.ID, - imageTarget, - }, "-"), - ) - Expect(hostmap[hostname].ToolFlags).To(Equal(topLevelToolFlags)) - }) - - When("tool flags are defined on a specific tag", func() { - var tagSpecificToolFlags map[string]any - BeforeEach(func() { - validContainerCertTarget.Tags[0].ToolFlags = map[string]any{} - tagSpecificToolFlags = map[string]any{"foo": 4} - maps.Copy(validContainerCertTarget.Tags[0].ToolFlags, tagSpecificToolFlags) - // Re-add the target to the mapping - certTarget[validApplicationContainerComponent.ID] = validContainerCertTarget - }) - It("should override top level flags for the specified tag", func() { - hostmap, err := generateContainerComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - imageTarget := strings.Join([]string{validContainerCertTarget.ImageRef, validContainerCertTarget.Tags[0].Tag}, ":") - hostname := normalizeContainerHostname( - strings.Join([]string{ - validApplicationContainerComponent.ID, - imageTarget, - }, "-"), - ) - Expect(hostmap[hostname].ToolFlags).To(Equal(tagSpecificToolFlags)) - }) - }) - }) - }) - }) - }) - - When("generating inventory for operator components", func() { - var certTarget ComponentCertificationConfig[OperatorCertTarget] - - BeforeEach(func() { - certTarget = ComponentCertificationConfig[OperatorCertTarget]{} - }) - - When("the product contains no operator components", func() { - It("should produce an empty host map", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(0)) - }) - }) - - When("the product contains a valid operator component", func() { - var validOperatorComponent resource.Component - BeforeEach(func() { - validOperatorComponent = resource.Component{ - ID: "placeholder", - Type: resource.ComponentTypeContainer, - Container: &resource.ContainerComponent{ - Type: resource.ComponentTypeContainer, - OSContentType: resource.ContentTypeOperatorBundle, - DistributionMethod: resource.ContainerDistributionExternal, - }, - } - - listing.With.Components = []*resource.Component{&validOperatorComponent} - }) - - When("the mapping does not contain a corresponding entry", func() { - It("should skip the component and produce an empty host map", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(0)) - }) - }) - - When("the mapping contains a corresponding entry", func() { - var ( - validCertTarget OperatorCertTarget - imageTarget string - hostname string - ) - BeforeEach(func() { - validCertTarget = OperatorCertTarget{ - ImageRef: "example.com/example/image", - Tags: []OperatorTagandOptions{{Tag: "tag"}}, - } - - certTarget[validOperatorComponent.ID] = validCertTarget - }) - - JustBeforeEach(func() { - imageTarget = strings.Join([]string{validCertTarget.ImageRef, validCertTarget.Tags[0].Tag}, ":") - hostname = normalizeContainerHostname( - strings.Join([]string{ - validOperatorComponent.ID, - imageTarget, - }, "-"), - ) - }) - - It("should add the operator to the hostmap", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - Expect(hostmap).To(HaveKey(hostname)) - Expect(hostmap[hostname].Image).To(Equal(imageTarget)) - Expect(hostmap[hostname].Component.ID).To(Equal(validOperatorComponent.ID)) - }) - - When("a top level index image is defined", func() { - var topLevelIndexImage string - BeforeEach(func() { - topLevelIndexImage = "example.io/example/index-image:tag" - validCertTarget.IndexImage = topLevelIndexImage - certTarget[validOperatorComponent.ID] = validCertTarget - }) - - It("should pass the index image to entry", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - Expect(hostmap[hostname].IndexImage).To(Equal(topLevelIndexImage)) - }) - - When("a tag-specific index image is defined", func() { - var tagSpecificIndexImage string - BeforeEach(func() { - tagSpecificIndexImage = "example.io/example/index-image:tag-specific" - validCertTarget.IndexImage = tagSpecificIndexImage - certTarget[validOperatorComponent.ID] = validCertTarget - }) - - It("should pass the index image to entry", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - Expect(hostmap[hostname].IndexImage).To(Equal(tagSpecificIndexImage)) - }) - }) - }) - - When("top level tool flags are defined", func() { - var topLevelToolFlags map[string]any - BeforeEach(func() { - validCertTarget.ToolFlags = map[string]any{} - topLevelToolFlags = map[string]any{"foo": 3} - maps.Copy(validCertTarget.ToolFlags, topLevelToolFlags) - // Re-add the target to the mapping - certTarget[validOperatorComponent.ID] = validCertTarget - }) - - It("should pass the tool flags to the returned host map", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - imageTarget := strings.Join([]string{validCertTarget.ImageRef, validCertTarget.Tags[0].Tag}, ":") - hostname := normalizeContainerHostname( - strings.Join([]string{ - validOperatorComponent.ID, - imageTarget, - }, "-"), - ) - Expect(hostmap[hostname].ToolFlags).To(Equal(topLevelToolFlags)) - }) - - When("tool flags are defined on a specific tag", func() { - var tagSpecificToolFlags map[string]any - BeforeEach(func() { - validCertTarget.Tags[0].ToolFlags = map[string]any{} - tagSpecificToolFlags = map[string]any{"foo": 4} - maps.Copy(validCertTarget.Tags[0].ToolFlags, tagSpecificToolFlags) - // Re-add the target to the mapping - certTarget[validOperatorComponent.ID] = validCertTarget - }) - It("should override top level flags for the specified tag", func() { - hostmap, err := generateOperatorComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - imageTarget := strings.Join([]string{validCertTarget.ImageRef, validCertTarget.Tags[0].Tag}, ":") - hostname := normalizeContainerHostname( - strings.Join([]string{ - validOperatorComponent.ID, - imageTarget, - }, "-"), - ) - Expect(hostmap[hostname].ToolFlags).To(Equal(tagSpecificToolFlags)) - }) - }) - }) - }) - }) - }) - - When("generating inventory for helm chart components", func() { - var certTarget ComponentCertificationConfig[HelmCertTarget] - - BeforeEach(func() { - certTarget = ComponentCertificationConfig[HelmCertTarget]{} - }) - - When("the product contains no helm chart components", func() { - It("should produce an empty host map", func() { - hostmap, err := generateHelmComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(0)) - }) - }) - - When("the product contains a valid helm chart component", func() { - var validComponent resource.Component - BeforeEach(func() { - validComponent = resource.Component{ - ID: "placeholder", - Type: resource.ComponentTypeHelmChart, - HelmChart: &resource.HelmChartComponent{ - ChartName: "placeholder-chart", - }, - } - - listing.With.Components = []*resource.Component{&validComponent} - }) - - When("the mapping does not contain a corresponding entry", func() { - It("should skip the component and produce an empty host map", func() { - hostmap, err := generateHelmComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(0)) - }) - }) - - When("the mapping contains a corresponding entry", func() { - var ( - validCertTarget HelmCertTarget - chartURI string - hostname string - ) - BeforeEach(func() { - chartURI = "https://example.com/path/to/chart.0.0.1.tgz" - validCertTarget = HelmCertTarget{ - ChartURI: chartURI, - } - - certTarget[validComponent.ID] = validCertTarget - }) - - JustBeforeEach(func() { - hostname = normalizeChartHostname( - strings.Join([]string{ - validComponent.ID, - chartURI, - }, "-"), - ) - fmt.Println(hostname) - }) - - It("should add the helm chart to the hostmap", func() { - hostmap, err := generateHelmComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - Expect(hostmap).To(HaveKey(hostname)) - Expect(hostmap[hostname].ChartURI).To(Equal(chartURI)) - Expect(hostmap[hostname].Component.ID).To(Equal(validComponent.ID)) - }) - - When("top level tool flags are defined", func() { - var toolFlags map[string]any - BeforeEach(func() { - validCertTarget.ToolFlags = map[string]any{} - toolFlags = map[string]any{"foo": 3} - maps.Copy(validCertTarget.ToolFlags, toolFlags) - // Re-add the target to the mapping - certTarget[validComponent.ID] = validCertTarget - }) - - It("should pass the tool flags to the returned host map", func() { - hostmap, err := generateHelmComponentInventory(ctx, listing, certTarget) - Expect(err).ToNot(HaveOccurred()) - Expect(hostmap).To(HaveLen(1)) - Expect(hostmap[hostname].ToolFlags).To(Equal(toolFlags)) - }) - }) - }) - }) - }) -}) diff --git a/internal/cmd/productctl/cmd/certifycontainers/certifycontainers.go b/internal/cmd/productctl/cmd/certifycontainers/certifycontainers.go deleted file mode 100644 index e65b5b1..0000000 --- a/internal/cmd/productctl/cmd/certifycontainers/certifycontainers.go +++ /dev/null @@ -1,160 +0,0 @@ -package certifycontainers - -import ( - "context" - "fmt" - "io" - "os" - "path/filepath" - - "dario.cat/mergo" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "github.com/opdev/productctl/internal/ansible" - "github.com/opdev/productctl/internal/cli" - "github.com/opdev/productctl/internal/libcerttoolrunner" - "github.com/opdev/productctl/internal/libcerttoolrunner/execpodman" - "github.com/opdev/productctl/internal/logger" - "github.com/opdev/productctl/internal/resource" -) - -func Command() *cobra.Command { - cmd := &cobra.Command{ - Use: "containers ", - Short: "Run Container Certification for a given Product Listing", - Args: cobra.ExactArgs(2), - RunE: runE, - } - - flags := cmd.Flags() - flags.String(cli.FlagIDUserfilesDir, "", "A full path to a user files") - flags.String(cli.FlagIDLogsDir, "", "A full path to the logs directory") - flags.String(cli.FlagIDCatalogAPIToken, "", "The catalog API token to use for submit requests") - flags.String(cli.FlagIDRuntimeImage, libcerttoolrunner.DefaultImageCertifyContainers, "The container image to use to certify containers") - - // Debug Flags - flags.Bool(cli.FlagIDKeepTempDir, false, "keep the temporary directory where generated assets are stored") - - return cmd -} - -func runE(cmd *cobra.Command, args []string) error { - L := logger.FromContextOrDiscard(cmd.Context()) - - L.Debug("Reading product from declaration", "file", args[0]) - productFile, err := os.Open(args[0]) - if err != nil { - return err - } - defer productFile.Close() - - L.Debug("Reading component certification mappings from provided mapping", "file", args[1]) - mappingFile, err := os.Open(args[1]) - if err != nil { - return err - } - - defer mappingFile.Close() - - declaration, err := resource.ReadProductListing(productFile) - if err != nil { - return err - } - - mappingB, err := io.ReadAll(mappingFile) - if err != nil { - return err - } - - var mapping ansible.MappingDeclaration - err = yaml.Unmarshal(mappingB, &mapping) - if err != nil { - return err - } - - L.Debug("Generating inventory from product and mappings") - inventory, err := ansible.GenerateInventory( - cmd.Context(), - declaration, - mapping, - ) - if err != nil { - return err - } - - runBaseDir, err := os.MkdirTemp(os.TempDir(), "cert-automation-") - if err != nil { - return err - } - - keep, _ := cmd.Flags().GetBool(cli.FlagIDKeepTempDir) - if !keep { - defer func() { - err = os.RemoveAll(runBaseDir) - if err != nil { - L.Error("unable to clean up temporary directory", "errorMsg", err, "tempdir", runBaseDir) - } - }() - } else { - defer L.Debug("tempdir kept per flag", "path", runBaseDir) - } - - inventoryDir, err := os.MkdirTemp(runBaseDir, "inventory-") - if err != nil { - return err - } - - inventoryData, err := yaml.Marshal(inventory) - if err != nil { - return err - } - - L.Debug("Writing generated inventory to temporary directory", "tmpdir", runBaseDir) - err = os.WriteFile(filepath.Join(inventoryDir, "generated.product.inventory.yaml"), inventoryData, 0o600) - if err != nil { - return err - } - - envVarFileName := "" - - if token, err := cmd.Flags().GetString(cli.FlagIDCatalogAPIToken); err == nil && token != "" { - L.Debug("Writing generated envvar file to temporary directory", "tmpdir", runBaseDir) - envVarFile, err := os.CreateTemp(runBaseDir, "envvar-") - if err != nil { - return err - } - - _, err = fmt.Fprintf(envVarFile, "---\nPFLT_PYXIS_API_TOKEN: %s", token) - if err != nil { - return err - } - - envVarFileName = envVarFile.Name() - envVarFile.Close() - } - - userfilesDir, _ := cmd.Flags().GetString(cli.FlagIDUserfilesDir) - logsDir, _ := cmd.Flags().GetString(cli.FlagIDLogsDir) - runtimeImage, _ := cmd.Flags().GetString(cli.FlagIDRuntimeImage) - - execContainerConfig := execpodman.DefaultConfig() - err = mergo.Merge(execContainerConfig, &execpodman.Config{ - UserfilesDir: userfilesDir, - UserHostLogDir: logsDir, - UserInventoryDir: inventoryDir, - EnvVarsFile: envVarFileName, - }, mergo.WithOverride) - if err != nil { - L.Error("error merging user configuration over default", "errMsg", err) - return err - } - - err = execpodman.Execute(context.TODO(), runtimeImage, os.Stdout, os.Stderr, L, execContainerConfig) - if err != nil { - L.Error("error running certification workload", "errMsg", err) - return err - } - - return nil -} diff --git a/internal/cmd/productctl/cmd/certifycontainers/certifycontainers_suite_test.go b/internal/cmd/productctl/cmd/certifycontainers/certifycontainers_suite_test.go deleted file mode 100644 index e793a71..0000000 --- a/internal/cmd/productctl/cmd/certifycontainers/certifycontainers_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package certifycontainers_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestCertifycontainers(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Certifycontainers Suite") -} diff --git a/internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts.go b/internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts.go deleted file mode 100644 index 98f57c1..0000000 --- a/internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts.go +++ /dev/null @@ -1,139 +0,0 @@ -package certifyhelmcharts - -import ( - "context" - "io" - "os" - "path/filepath" - - "dario.cat/mergo" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "github.com/opdev/productctl/internal/ansible" - "github.com/opdev/productctl/internal/cli" - "github.com/opdev/productctl/internal/libcerttoolrunner" - "github.com/opdev/productctl/internal/libcerttoolrunner/execpodman" - "github.com/opdev/productctl/internal/logger" - "github.com/opdev/productctl/internal/resource" -) - -func Command() *cobra.Command { - cmd := &cobra.Command{ - Use: "helm-charts /path/to/product-declaration.yaml /path/to/generated-mappings.yaml", - Short: "Run Helm Chart Certification for a given Product Listing", - Args: cobra.ExactArgs(2), - RunE: runE, - } - - flags := cmd.Flags() - flags.String(cli.FlagIDUserfilesDir, "", "A full path to a user files") - flags.String(cli.FlagIDLogsDir, "", "A full path to the logs directory") - flags.String(cli.FlagIDRuntimeImage, libcerttoolrunner.DefaultImageCertifyHelmCharts, "The container image to use to certify helm charts") - - // Debug Flags - flags.Bool(cli.FlagIDKeepTempDir, false, "keep the temporary directory where generated assets are stored") - - return cmd -} - -func runE(cmd *cobra.Command, args []string) error { - L := logger.FromContextOrDiscard(cmd.Context()) - - L.Debug("Reading product from declaration", "file", args[0]) - productFile, err := os.Open(args[0]) - if err != nil { - return err - } - defer productFile.Close() - - L.Debug("Reading component certification mappings from provided mapping", "file", args[1]) - mappingFile, err := os.Open(args[1]) - if err != nil { - return err - } - - defer mappingFile.Close() - - declaration, err := resource.ReadProductListing(productFile) - if err != nil { - return err - } - - mappingB, err := io.ReadAll(mappingFile) - if err != nil { - return err - } - - var mapping ansible.MappingDeclaration - err = yaml.Unmarshal(mappingB, &mapping) - if err != nil { - return err - } - - L.Debug("Generating inventory from product and mappings") - inventory, err := ansible.GenerateInventory( - cmd.Context(), - declaration, - mapping, - ) - if err != nil { - return err - } - - runBaseDir, err := os.MkdirTemp(os.TempDir(), "cert-automation-") - if err != nil { - return err - } - - keep, _ := cmd.Flags().GetBool(cli.FlagIDKeepTempDir) - if !keep { - defer func() { - err = os.RemoveAll(runBaseDir) - if err != nil { - L.Error("unable to clean up temporary directory", "errorMsg", err, "tempdir", runBaseDir) - } - }() - } else { - defer L.Debug("tempdir kept per flag", "path", runBaseDir) - } - - inventoryDir, err := os.MkdirTemp(runBaseDir, "inventory-") - if err != nil { - return err - } - - inventoryData, err := yaml.Marshal(inventory) - if err != nil { - return err - } - - L.Debug("Writing generated inventory to temporary directory", "tmpdir", runBaseDir) - err = os.WriteFile(filepath.Join(inventoryDir, "generated.product.inventory.yaml"), inventoryData, 0o600) - if err != nil { - return err - } - - userfilesDir, _ := cmd.Flags().GetString(cli.FlagIDUserfilesDir) - logsDir, _ := cmd.Flags().GetString(cli.FlagIDLogsDir) - runtimeImage, _ := cmd.Flags().GetString(cli.FlagIDRuntimeImage) - - execContainerConfig := execpodman.DefaultConfig() - err = mergo.Merge(execContainerConfig, &execpodman.Config{ - UserfilesDir: userfilesDir, - UserHostLogDir: logsDir, - UserInventoryDir: inventoryDir, - }, mergo.WithOverride) - if err != nil { - L.Error("error merging user configuration over default", "errMsg", err) - return err - } - - err = execpodman.Execute(context.TODO(), runtimeImage, os.Stdout, os.Stderr, L, execContainerConfig) - if err != nil { - L.Error("error running certification workload", "errMsg", err) - return err - } - - return nil -} diff --git a/internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts_suite_test.go b/internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts_suite_test.go deleted file mode 100644 index f390230..0000000 --- a/internal/cmd/productctl/cmd/certifyhelmcharts/certifyhelmcharts_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package certifyhelmcharts_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestCertifyhelmcharts(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Certifyhelmcharts Suite") -} diff --git a/internal/cmd/productctl/cmd/certifyoperators/certifyoperators.go b/internal/cmd/productctl/cmd/certifyoperators/certifyoperators.go deleted file mode 100644 index f12c956..0000000 --- a/internal/cmd/productctl/cmd/certifyoperators/certifyoperators.go +++ /dev/null @@ -1,139 +0,0 @@ -package certifyoperators - -import ( - "context" - "io" - "os" - "path/filepath" - - "dario.cat/mergo" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "github.com/opdev/productctl/internal/ansible" - "github.com/opdev/productctl/internal/cli" - "github.com/opdev/productctl/internal/libcerttoolrunner" - "github.com/opdev/productctl/internal/libcerttoolrunner/execpodman" - "github.com/opdev/productctl/internal/logger" - "github.com/opdev/productctl/internal/resource" -) - -func Command() *cobra.Command { - cmd := &cobra.Command{ - Use: "operators /path/to/product-declaration.yaml /path/to/generated-mappings.yaml", - Short: "Run Operator Certification for a given Product Listing", - Args: cobra.ExactArgs(2), - RunE: runE, - } - - flags := cmd.Flags() - flags.String(cli.FlagIDUserfilesDir, "", "A full path to a user files") - flags.String(cli.FlagIDLogsDir, "", "A full path to the logs directory") - flags.String(cli.FlagIDRuntimeImage, libcerttoolrunner.DefaultImageCertifyOperators, "The container image to use to certify Operator bundles") - - // Debug Flags - flags.Bool(cli.FlagIDKeepTempDir, false, "keep the temporary directory where generated assets are stored") - - return cmd -} - -func runE(cmd *cobra.Command, args []string) error { - L := logger.FromContextOrDiscard(cmd.Context()) - - L.Debug("Reading product from declaration", "file", args[0]) - productFile, err := os.Open(args[0]) - if err != nil { - return err - } - defer productFile.Close() - - L.Debug("Reading component certification mappings from provided mapping", "file", args[1]) - mappingFile, err := os.Open(args[1]) - if err != nil { - return err - } - - defer mappingFile.Close() - - declaration, err := resource.ReadProductListing(productFile) - if err != nil { - return err - } - - mappingB, err := io.ReadAll(mappingFile) - if err != nil { - return err - } - - var mapping ansible.MappingDeclaration - err = yaml.Unmarshal(mappingB, &mapping) - if err != nil { - return err - } - - L.Debug("Generating inventory from product and mappings") - inventory, err := ansible.GenerateInventory( - cmd.Context(), - declaration, - mapping, - ) - if err != nil { - return err - } - - runBaseDir, err := os.MkdirTemp(os.TempDir(), "cert-automation-") - if err != nil { - return err - } - - keep, _ := cmd.Flags().GetBool(cli.FlagIDKeepTempDir) - if !keep { - defer func() { - err = os.RemoveAll(runBaseDir) - if err != nil { - L.Error("unable to clean up temporary directory", "errorMsg", err, "tempdir", runBaseDir) - } - }() - } else { - defer L.Debug("tempdir kept per flag", "path", runBaseDir) - } - - inventoryDir, err := os.MkdirTemp(runBaseDir, "inventory-") - if err != nil { - return err - } - - inventoryData, err := yaml.Marshal(inventory) - if err != nil { - return err - } - - L.Debug("Writing generated inventory to temporary directory", "tmpdir", runBaseDir) - err = os.WriteFile(filepath.Join(inventoryDir, "generated.product.inventory.yaml"), inventoryData, 0o600) - if err != nil { - return err - } - - userfilesDir, _ := cmd.Flags().GetString(cli.FlagIDUserfilesDir) - logsDir, _ := cmd.Flags().GetString(cli.FlagIDLogsDir) - runtimeImage, _ := cmd.Flags().GetString(cli.FlagIDRuntimeImage) - - execContainerConfig := execpodman.DefaultConfig() - err = mergo.Merge(execContainerConfig, &execpodman.Config{ - UserfilesDir: userfilesDir, - UserHostLogDir: logsDir, - UserInventoryDir: inventoryDir, - }, mergo.WithOverride) - if err != nil { - L.Error("error merging user configuration over default", "errMsg", err) - return err - } - - err = execpodman.Execute(context.TODO(), runtimeImage, os.Stdout, os.Stderr, L, execContainerConfig) - if err != nil { - L.Error("error running certification workload", "errMsg", err) - return err - } - - return nil -} diff --git a/internal/cmd/productctl/cmd/certifyoperators/certifyoperators_suite_test.go b/internal/cmd/productctl/cmd/certifyoperators/certifyoperators_suite_test.go deleted file mode 100644 index 71b8a2f..0000000 --- a/internal/cmd/productctl/cmd/certifyoperators/certifyoperators_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package certifyoperators_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestCertifyoperators(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Certifyoperators Suite") -} diff --git a/internal/cmd/productctl/cmd/certtargets/certtargets.go b/internal/cmd/productctl/cmd/certtargets/certtargets.go deleted file mode 100644 index 12ca949..0000000 --- a/internal/cmd/productctl/cmd/certtargets/certtargets.go +++ /dev/null @@ -1,278 +0,0 @@ -package certtargets - -import ( - "bytes" - "errors" - "fmt" - "io" - "os" - "strings" - "text/template" - - "github.com/spf13/cobra" - - "github.com/opdev/productctl/internal/logger" - "github.com/opdev/productctl/internal/resource" -) - -func Command() *cobra.Command { - cmd := &cobra.Command{ - Use: "generate-component-mappings [new-product.yaml]", - Short: "Generates a certification mapping file for a given product.", - RunE: runE, - Args: cobra.MinimumNArgs(1), - } - - return cmd -} - -func runE(cmd *cobra.Command, args []string) error { - L := logger.FromContextOrDiscard(cmd.Context()) - productFile, err := os.Open(args[0]) - if err != nil { - return err - } - - defer productFile.Close() - - declaration, err := resource.ReadProductListing(productFile) - if err != nil { - return err - } - - components := declaration.With.Components - if len(components) == 0 { - return errors.New("no components in this product") - } - - // Sort the components by their types. - helmComponents := []*resource.Component{} - operatorComponents := []*resource.Component{} - containerComponents := []*resource.Component{} - - L.Debug("sorting containers by type") - for _, cmp := range components { - // Components must have IDs to be mapped and processed correctly. If a - // component does not have an ID, we assume it does not exist in the - // upstream. - if cmp.ID == "" { - L.Warn("Skipping component that does not have an ID in provided product declaration", "component_name", cmp.Name) - continue - } - - switch cmp.Type { - case resource.ComponentTypeHelmChart: - L.Debug("component is a helm chart", "component_id", cmp.ID) - helmComponents = append(helmComponents, cmp) - case resource.ComponentTypeContainer: - L.Debug("component is a container", "component_id", cmp.ID) - if cmp.Container == nil { - L.Debug( - "Skipping container component that doesn't have container details", - "component_id", cmp.ID, - "component_name", cmp.Name, - ) - continue - } - - if cmp.Container.OSContentType == resource.ContentTypeOperatorBundle && cmp.Container.Type == resource.ContainerTypeOperatorBundle { - L.Debug("more specifically, the component is an operator bundle", "component_id", cmp.ID) - operatorComponents = append(operatorComponents, cmp) - continue - } - - if cmp.Container.Type == resource.ContainerTypeContainer { - L.Debug("more specifically, the component is a standard container") - containerComponents = append(containerComponents, cmp) - continue - } - - L.Warn("container component could not be sorted", "_id", cmp.ID) - default: - L.Warn("component was skipped because it was not of an expected type", "componentType", cmp.Type) - continue - } - } - L.Debug("done sorting containers by type") - - // buffer out contains all of the data to be written. It's stored here so - // that it can be written to the runtime output (e.g. stdout) all at once. - out := &bytes.Buffer{} - - containerComponentsWritten := 0 - for _, cmp := range containerComponents { - image := "registry.example.com/placeholder/placeholder" - if cmp.Container.Registry != "" || cmp.Container.Repository != "" || cmp.Container.RepositoryName != "" { - image = strings.Join([]string{cmp.Container.Registry, cmp.Container.Repository, cmp.Container.RepositoryName}, "/") - } - L.Debug("executing container template") - b := &bytes.Buffer{} - - // Only write the section title just before the first component is - // written. - if containerComponentsWritten == 0 { - fmt.Fprintln(b, "container_components:") - } - err := containerTemplate(image, cmp.ID, cmp.Name, b) - if err != nil { - L.Error("failed to generate template for component", "errMsg", err) - continue - } - - // Template executions were already successful, so we can just copy all - // of the content through to the final buffer. - if _, err := io.Copy(out, b); err != nil { - return err - } - containerComponentsWritten++ - } - - helmComponentsWritten := 0 - for _, cmp := range helmComponents { - chartURI := "https://example.com/path/to/your/chart-0.1.1.tgz" - - b := &bytes.Buffer{} - if helmComponentsWritten == 0 { - fmt.Fprintln(b, "helm_chart_components:") - } - err := helmChartTemplate(chartURI, cmp.ID, cmp.Name, b) - if err != nil { - L.Error("failed to generate template for component", "errMsg", err) - continue - } - - if _, err := io.Copy(out, b); err != nil { - return err - } - helmComponentsWritten++ - } - - operatorComponentsWritten := 0 - for _, cmp := range operatorComponents { - image := "registry.example.com/operatorbundle/placeholder" - if cmp.Container.Registry != "" || cmp.Container.Repository != "" || cmp.Container.RepositoryName != "" { - image = strings.Join([]string{cmp.Container.Registry, cmp.Container.Repository, cmp.Container.RepositoryName}, "/") - } - L.Debug("executing operator template") - b := &bytes.Buffer{} - // Make sure the parent key is written before the first component. - if operatorComponentsWritten == 0 { - fmt.Fprintln(b, "operator_components:") - } - err := operatorTemplate(image, cmp.ID, cmp.Name, b) - if err != nil { - L.Error("failed to generate template for component", "errMsg", err) - continue - } - - if _, err := io.Copy(out, b); err != nil { - return err - } - operatorComponentsWritten++ - } - - fmt.Fprint(os.Stdout, out) - L.Info( - "Total components written successfully", - "container_components", containerComponentsWritten, - "operator_components", operatorComponentsWritten, - "helm_chart_components", helmComponentsWritten, - ) - return nil -} - -func containerTemplate(img, key, name string, out io.Writer) error { - t := ` ## {{ .Name }} - {{ .Key }}: - image_ref: {{ .Image }} - tool_flags: - submit: true - tags: - ## A tag you want to certify. - - tag: placeholder - ## If this tag should use different tool_flags than what you configure at - ## the top level for this component, uncomment this line and specify that - ## here. - # tool_flags: {} -` - - entryTemplate, err := template.New("entry").Parse(t) - if err != nil { - return err - } - - err = entryTemplate.Execute(out, map[string]string{ - "Image": img, - "Name": name, - "Key": key, - }) - if err != nil { - return err - } - - return nil -} - -func helmChartTemplate(chartURI, key, name string, out io.Writer) error { - t := ` ## {{ .Name }} - {{ .Key }}: - chart_uri: {{ .ChartURI }} -` - - entryTemplate, err := template.New("entry").Parse(t) - if err != nil { - return err - } - - err = entryTemplate.Execute(out, map[string]string{ - "ChartURI": chartURI, - "Name": name, - "Key": key, - }) - if err != nil { - return err - } - - return nil -} - -func operatorTemplate(img, key, name string, out io.Writer) error { - t := ` ## {{ .Name }} - {{ .Key }}: - image_ref: {{ .Image }} - ## The operator index image (or catalog) that contains the image_ref at the - ## listed tags. Per-tag index_images can also be configured alongside the tag - ## definition - index_image: placeholder.example.com/namespace/index-image:latest - ## The tool_flags directive contains any flags to set on the - ## certification tooling. - tool_flags: - # kubeconfig is a path to the kubeconfig to use for this component. - kubeconfig: component-kubeconfig.yaml - ## tags you wish to certify are configured here. - tags: - - tag: placeholder - ## Different tags different sets of flags to pass to certification tools. - ## If that's the case, you can configure those on a per-tag basis by uncommenting - ## the below configuration - # tool_flags: {} - ## If this tag exists in a custom index image, uncommnent and set that here. - # index_image: placeholder.example.com/namespace/per-tag-index-image:latest -` - - entryTemplate, err := template.New("entry").Parse(t) - if err != nil { - return err - } - - err = entryTemplate.Execute(out, map[string]string{ - "Image": img, - "Name": name, - "Key": key, - }) - if err != nil { - return err - } - - return nil -} diff --git a/internal/cmd/productctl/cmd/certtargets/certtargets_suite_test.go b/internal/cmd/productctl/cmd/certtargets/certtargets_suite_test.go deleted file mode 100644 index e278c3d..0000000 --- a/internal/cmd/productctl/cmd/certtargets/certtargets_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package certtargets_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestCerttargets(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Certtargets Suite") -} diff --git a/internal/cmd/productctl/cmd/cmd.go b/internal/cmd/productctl/cmd/cmd.go index 04f1a55..cd5dd7b 100644 --- a/internal/cmd/productctl/cmd/cmd.go +++ b/internal/cmd/productctl/cmd/cmd.go @@ -10,14 +10,10 @@ import ( "github.com/opdev/productctl/internal/cli" "github.com/opdev/productctl/internal/cmd/productctl/cmd/apply" "github.com/opdev/productctl/internal/cmd/productctl/cmd/bridge" - "github.com/opdev/productctl/internal/cmd/productctl/cmd/certifycontainers" - "github.com/opdev/productctl/internal/cmd/productctl/cmd/certifyhelmcharts" - "github.com/opdev/productctl/internal/cmd/productctl/cmd/certifyoperators" - "github.com/opdev/productctl/internal/cmd/productctl/cmd/certtargets" "github.com/opdev/productctl/internal/cmd/productctl/cmd/cleanup" "github.com/opdev/productctl/internal/cmd/productctl/cmd/create" "github.com/opdev/productctl/internal/cmd/productctl/cmd/fetch" - "github.com/opdev/productctl/internal/cmd/productctl/cmd/lsp" + "github.com/opdev/productctl/internal/cmd/productctl/cmd/jsonschema" "github.com/opdev/productctl/internal/cmd/productctl/cmd/sanitize" "github.com/opdev/productctl/internal/cmd/productctl/cmd/version" libversion "github.com/opdev/productctl/internal/version" @@ -49,22 +45,9 @@ func RootCmd() *cobra.Command { product.AddCommand(fetch.Command()) product.AddCommand(sanitize.Command()) product.AddCommand(cleanup.Command()) - + product.AddCommand(jsonschema.Command()) cmd.AddCommand(product) - // Build the cert tool running command tree - certify := bridge.Command("certify", "Run cert-tool-runners for specified certifications") - certify.AddCommand(certifycontainers.Command()) - certify.AddCommand(certifyhelmcharts.Command()) - certify.AddCommand(certifyoperators.Command()) - - // Build alpha commands. - alpha := bridge.Command("alpha", "Experimental commands subject to removal or change at any time") - alpha.AddCommand(lsp.Command()) - alpha.AddCommand(certtargets.Command()) - alpha.AddCommand(certify) - cmd.AddCommand(alpha) - return cmd } diff --git a/internal/cmd/productctl/cmd/lsp/lsp.go b/internal/cmd/productctl/cmd/jsonschema/jsonschema.go similarity index 86% rename from internal/cmd/productctl/cmd/lsp/lsp.go rename to internal/cmd/productctl/cmd/jsonschema/jsonschema.go index c1ae892..a5ab40f 100644 --- a/internal/cmd/productctl/cmd/lsp/lsp.go +++ b/internal/cmd/productctl/cmd/jsonschema/jsonschema.go @@ -1,4 +1,4 @@ -package lsp +package jsonschema import ( "encoding/json" @@ -12,8 +12,8 @@ import ( func Command() *cobra.Command { cmd := &cobra.Command{ - Use: "lsp-completion", - Short: "Generate resource schema for LSPs that support it.", + Use: "jsonschema", + Short: "Generate resource jsonschema for LSPs that support it.", RunE: func(cmd *cobra.Command, _ []string) error { // TODO: If this functionality will remain and be useful, we should // add comments, enums, and warnings when things are immutable after diff --git a/internal/cmd/productctl/cmd/lsp/lsp_suite_test.go b/internal/cmd/productctl/cmd/jsonschema/jsonschema_suite_test.go similarity index 71% rename from internal/cmd/productctl/cmd/lsp/lsp_suite_test.go rename to internal/cmd/productctl/cmd/jsonschema/jsonschema_suite_test.go index 3fe5fd5..2ef4fde 100644 --- a/internal/cmd/productctl/cmd/lsp/lsp_suite_test.go +++ b/internal/cmd/productctl/cmd/jsonschema/jsonschema_suite_test.go @@ -1,4 +1,4 @@ -package lsp_test +package jsonschema_test import ( "testing" @@ -9,5 +9,5 @@ import ( func TestLSP(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Lsp Suite") + RunSpecs(t, "JSONschema Suite") } diff --git a/internal/cmd/productctl/cmd/lsp/lsp_test.go b/internal/cmd/productctl/cmd/jsonschema/jsonschema_test.go similarity index 83% rename from internal/cmd/productctl/cmd/lsp/lsp_test.go rename to internal/cmd/productctl/cmd/jsonschema/jsonschema_test.go index 0addc50..fff5492 100644 --- a/internal/cmd/productctl/cmd/lsp/lsp_test.go +++ b/internal/cmd/productctl/cmd/jsonschema/jsonschema_test.go @@ -1,4 +1,4 @@ -package lsp_test +package jsonschema_test import ( "encoding/json" @@ -6,12 +6,12 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/opdev/productctl/internal/cmd/productctl/cmd/lsp" + "github.com/opdev/productctl/internal/cmd/productctl/cmd/jsonschema" "github.com/opdev/productctl/internal/cmd/productctl/cmd/testutils" "github.com/opdev/productctl/internal/resource" ) -var _ = Describe("LSP", func() { +var _ = Describe("jsonschema", func() { When("generating resource schemas", func() { var ( cmdOut string @@ -20,7 +20,7 @@ var _ = Describe("LSP", func() { ) BeforeEach(func() { - cmdOut, cmdErr = testutils.ExecuteCommand(lsp.Command(), args...) + cmdOut, cmdErr = testutils.ExecuteCommand(jsonschema.Command(), args...) }) It("should not be empty", func() {