Skip to content

Commit

Permalink
Cartotest supplychains (#1265)
Browse files Browse the repository at this point in the history
* Turn cartotest Expectation into an interface

* SupplyChain interface for Cartotest

* Supply a set of supply chain file paths

Can pass in ytt values as well

* Split large file

* Refactor replaceIfFound

* Update cli suite expected locating

* Change cartotesting workload to an interface

* Turn Givens.Template into an interface

* Rename interface

* Rename supplychain interface

* BlueprintParams as an interface

* Move functions

* Change method to function

* Change BlueprintInputs into an interface

* Blueprint Params and Inputs nested under MockSupplyChain

* Move method

* Change pointers on methods

* Move functions

* Turn SupplyChain into an interface implemented by mock and actual supply chains

* Rename Expectations and Given

* Rename SupplyChainFileSet

* Add fields to supply chain set

* Rename Expectation interface implementations

* Rename Expectation fields

* First passing actual supply chain test

* Change specification of ytt file for cli cartotests

* Nest template cli fields as they are in new template struct

* Nest mockSupplyChain CLI fields

* Nest metadata and givens in CLI info file

* Test CLI tests with go test

* Begin moving CLI TestCase Population to functions

* CLI TestCase Population in function

* Split cli-suite-builder file into suite and case

* Rename variables

* Refactor populate testCase template

* Further refactor populate testCase template

* Rename function

* Even further refactor populate testCase template

* populate TestCase SupplyChain function

* Working cartotest cli with actual supply chain

* Create Comparison Option Field

* CompareOptions yields cmp.Options

* Cartotest CLI uses compareOptions

* Reorder yaml files

* Lint

* Cartotest CLI can leverage number conversion function

* Simplify cartotest cli

Deprecate the 'templates' command

* Ensure targetResource references provided template

* Simplify language

Cartesting tests are Test rather than TemplateTestCase

Similarly Suite rather than TemplateTestSuite

* Convert Blueprint params/inputs to SupplyChain params/inputs
  • Loading branch information
waciumawanjohi authored Jun 21, 2023
1 parent d5a9e41 commit c3aca9b
Show file tree
Hide file tree
Showing 28 changed files with 1,808 additions and 770 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ test-cartotest: test-cartotest-go test-cartotest-cli

.PHONY: test-cartotest-cli
test-cartotest-cli:
go run ./cmd/cartotest/main.go templates --directory ./tests/templates/
go run ./cmd/cartotest/main.go ./tests/templates/

.PHONY: test-cartotest-go
test-cartotest-go:
Expand Down
10 changes: 5 additions & 5 deletions pkg/controllers/workload_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (r *WorkloadReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
}

contextGenerator := realizer.NewContextGenerator(workload, workload.Spec.Params, supplyChain.Spec.Params)
resourceRealizer, err := r.ResourceRealizerBuilder(saToken, workload, contextGenerator, r.Repo, buildWorkloadResourceLabeler(workload, supplyChain))
resourceRealizer, err := r.ResourceRealizerBuilder(saToken, workload, contextGenerator, r.Repo, BuildWorkloadResourceLabeler(workload, supplyChain))
if err != nil {
conditionManager.AddPositive(conditions.ResourceRealizerBuilderErrorCondition(err))
log.Error(err, "failed to build resource realizer")
Expand Down Expand Up @@ -228,7 +228,7 @@ func (r *WorkloadReconciler) isSupplyChainReady(supplyChain *v1alpha1.ClusterSup
return supplyChainReadyCondition.Status == "True"
}

func buildWorkloadResourceLabeler(owner, blueprint client.Object) realizer.ResourceLabeler {
func BuildWorkloadResourceLabeler(owner, blueprint client.Object) realizer.ResourceLabeler {
return func(resource realizer.OwnerResource, reader templates.Reader) templates.Labels {
return templates.Labels{
"carto.run/workload-name": owner.GetName(),
Expand Down Expand Up @@ -278,9 +278,9 @@ func (r *WorkloadReconciler) getSupplyChainsForWorkload(ctx context.Context, wor
if len(supplyChains) > 1 {
conditionManager.AddPositive(conditions.TooManySupplyChainMatchesCondition())
log.Info("more than one supply chain selected for workload",
"supply chains", getSupplyChainNames(supplyChains))
"supply chains", GetSupplyChainNames(supplyChains))
return nil, fmt.Errorf("more than one supply chain selected for workload [%s/%s]: %+v",
workload.Namespace, workload.Name, getSupplyChainNames(supplyChains))
workload.Namespace, workload.Name, GetSupplyChainNames(supplyChains))
}

log.V(logger.DEBUG).Info("supply chain matched for workload", "supply chain", supplyChains[0].Name)
Expand Down Expand Up @@ -388,7 +388,7 @@ func (r *WorkloadReconciler) cleanupOrphanedObjects(ctx context.Context, previou
return nil
}

func getSupplyChainNames(objs []*v1alpha1.ClusterSupplyChain) []string {
func GetSupplyChainNames(objs []*v1alpha1.ClusterSupplyChain) []string {
var names []string
for _, obj := range objs {
names = append(names, obj.GetName())
Expand Down
15 changes: 13 additions & 2 deletions pkg/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,21 @@ func (r *repository) GetSupplyChainsForWorkload(ctx context.Context, workload *v
return nil, fmt.Errorf("unable to list supply chains from api server: %w", err)
}

var selectorGetters []SelectingObject
var supplyChains []*v1alpha1.ClusterSupplyChain

for _, item := range list.Items {
itemValue := item
selectorGetters = append(selectorGetters, &itemValue)
supplyChains = append(supplyChains, &itemValue)
}

return GetSelectedSupplyChain(supplyChains, workload, log)
}

func GetSelectedSupplyChain(allSupplyChains []*v1alpha1.ClusterSupplyChain, workload *v1alpha1.Workload, log logr.Logger) ([]*v1alpha1.ClusterSupplyChain, error) {
var selectorGetters []SelectingObject
for _, item := range allSupplyChains {
itemValue := item
selectorGetters = append(selectorGetters, itemValue)
}

var supplyChains []*v1alpha1.ClusterSupplyChain
Expand Down
113 changes: 113 additions & 0 deletions pkg/testing/case.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2021 VMware
//
// 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 testing

import (
"context"
"errors"
"fmt"

"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"github.com/vmware-tanzu/cartographer/pkg/templates"
)

// Test is an individual template test.
// Given and Expect values must be provided.
// Fields in the expected object's metadata may be ignored
// When run as part of a Suite, an individual case(s) may be focused.
// This will exercise the individual test(s).
// Note that the overall suite will fail (preventing focused tests from passing CI).
type Test struct {
Given Given
Expect Expectation
CompareOptions *CompareOptions
Focus bool
}

// Given must specify a Template and a Workload.
// SupplyChain is optional
type Given struct {
Template Template
Workload Workload
SupplyChain SupplyChain
}

func (c *Test) Run() error {
expectedObject, err := c.Expect.getExpected()
if err != nil {
return fmt.Errorf("failed to get expected object: %w", err)
}

actualObject, err := c.Given.getActualObject()
if errors.Is(err, yttNotFound) {
return fmt.Errorf("test requires ytt, but ytt was not found in path")
} else if err != nil {
return fmt.Errorf("failed to get actual object: %w", err)
}

c.stripIgnoredFields(expectedObject, actualObject)

var opts cmp.Options
if c.CompareOptions != nil && c.CompareOptions.CMPOption != nil {
opts, err = c.CompareOptions.CMPOption()
if err != nil {
return fmt.Errorf("get compare options: %w", err)
}
}

if diff := cmp.Diff(expectedObject.Object, actualObject.Object, opts); diff != "" {
return fmt.Errorf("expected does not equal actual: (-expected +actual):\n%s", diff)
}

return nil
}

func (i *Given) getActualObject() (*unstructured.Unstructured, error) {
ctx := context.Background()

workload, err := i.Workload.GetWorkload()
if err != nil {
return nil, fmt.Errorf("get workload failed: %w", err)
}

apiTemplate, err := i.Template.GetTemplate()
if err != nil {
return nil, fmt.Errorf("get populated template failed: %w", err)
}

if err = (*apiTemplate).ValidateCreate(); err != nil {
return nil, fmt.Errorf("template validation failed: %w", err)
}

template, err := templates.NewReaderFromAPI(*apiTemplate)
if err != nil {
return nil, fmt.Errorf("failed to get cluster template")
}

if template.IsYTTTemplate() {
err = ensureYTTAvailable(ctx)
if err != nil {
return nil, fmt.Errorf("ensure YTT available: %w", err)
}
}

if i.SupplyChain == nil {
i.SupplyChain = &MockSupplyChain{}
}

return i.SupplyChain.stamp(ctx, workload, *apiTemplate, template)
}
Loading

0 comments on commit c3aca9b

Please sign in to comment.