Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
35 changes: 28 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ endif
REPO = github.com/operator-framework/api
BUILD_PATH = $(REPO)/cmd/operator-verify
PKGS = $(shell go list ./... | grep -v /vendor/)
YQ_INTERNAL := $(Q) go run $(MOD_FLAGS) ./vendor/github.com/mikefarah/yq/v2/

.PHONY: help
help: ## Show this help screen
Expand All @@ -23,7 +24,7 @@ help: ## Show this help screen

.PHONY: install

install: ## Build & install the operator-verify
install: ## Build & install operator-verify

$(Q)go install \
-gcflags "all=-trimpath=${GOPATH}" \
Expand All @@ -35,22 +36,35 @@ install: ## Build & install the operator-verify
$(BUILD_PATH)

# Code management.
.PHONY: format tidy clean
.PHONY: format tidy clean vendor generate

format: ## Format the source code
$(Q)go fmt $(PKGS)

tidy: ## Update dependencies
$(Q)go mod tidy -v

vendor: tidy ## Update vendor directory
$(Q)go mod vendor

clean: ## Clean up the build artifacts
$(Q)rm -rf build

##############################
# Tests #
##############################
generate: controller-gen ## Generate code
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./...

manifests: controller-gen ## Generate manifests e.g. CRD, RBAC etc
@# Handle >v1 APIs
$(CONTROLLER_GEN) crd paths=./pkg/operators/v2alpha1... output:crd:dir=./crds

@# Handle <=v1 APIs
$(CONTROLLER_GEN) schemapatch:manifests=./crds output:dir=./crds paths=./pkg/operators/...

@# Preserve unknown fields on the CSV spec (prevents install strategy from being pruned)
$(YQ_INTERNAL) w --inplace ./crds/operators.coreos.com_clusterserviceversions.yaml spec.validation.openAPIV3Schema.properties.spec.properties.install.properties.spec.properties.deployments.items.properties.spec.properties.template.properties.metadata.x-kubernetes-preserve-unknown-fields true

##@ Tests
@# Update embedded CRD files.
@go generate ./crds/...

# Static tests.
.PHONY: test test-unit
Expand All @@ -59,4 +73,11 @@ test: test-unit ## Run the tests

TEST_PKGS:=$(shell go list ./...)
test-unit: ## Run the unit tests
$(Q)go test -short ${TEST_PKGS}
$(Q)go test -count=1 -short ${TEST_PKGS}

# Utilities.
.PHONY: controller-gen

controller-gen: vendor ## Find or download controller-gen
CONTROLLER_GEN=$(Q)go run -mod=vendor ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen

117 changes: 117 additions & 0 deletions crds/defs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package crds

// Generate embedded files from CRDs to avoid file path changes when this package is imported.
//go:generate go run github.com/go-bindata/go-bindata/v3/go-bindata -pkg crds -o zz_defs.go -ignore=.*\.go .

import (
"bytes"
"fmt"
"sync"

"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/yaml"
)

// crdFile is a descriptor of a file containing a CustomResourceDefinition.
type crdFile string

// path returns the path of the file.
func (c crdFile) path() string {
s := string(c)
return s
}

// mustUnmarshal unmarshals the file into a CRD and panics on failure.
func (c crdFile) mustUnmarshal() *apiextensionsv1.CustomResourceDefinition {
path := c.path()
data, err := Asset(path)
if err != nil {
panic(fmt.Errorf("unable to read crd file %s: %s", path, err))
}

u := &unstructured.Unstructured{}
reader := bytes.NewReader(data)
decoder := yaml.NewYAMLOrJSONDecoder(reader, 30)
if err = decoder.Decode(u); err != nil {
panic(fmt.Errorf("crd unmarshaling failed: %s", err))
}

// Step through unversioned type to support v1beta1 -> v1
unversioned := &apiextensions.CustomResourceDefinition{}
if err = scheme.Convert(u, unversioned, nil); err != nil {
panic(fmt.Errorf("failed to convert crd: %s\nto v1: %s", u, err))
}

crd := &apiextensionsv1.CustomResourceDefinition{}
if err = scheme.Convert(unversioned, crd, nil); err != nil {
panic(fmt.Errorf("failed to convert crd: %s\nto v1: %s", u, err))
}

return crd
}

var (
lock sync.Mutex

// loaded stores previously unmarshaled CustomResourceDefinitions indexed by their file descriptor.
loaded = map[crdFile]*apiextensionsv1.CustomResourceDefinition{}
// scheme provides conversions between type versions.
scheme = runtime.NewScheme()
)

func init() {
// Add conversions between CRD versions
install.Install(scheme)
}

// getCRD lazily loads and returns the CustomResourceDefinition unmarshaled from a file.
func getCRD(file crdFile) *apiextensionsv1.CustomResourceDefinition {
lock.Lock()
defer lock.Unlock()

if crd, ok := loaded[file]; ok && crd != nil {
return crd
}

// Unmarshal and memoize
crd := file.mustUnmarshal()
loaded[file] = crd

return crd
}

// TODO(njhale): codegen this.

// CatalogSource returns a copy of the CustomResourceDefinition for the latest version of the CatalogSource API.
func CatalogSource() *apiextensionsv1.CustomResourceDefinition {
return getCRD("operators.coreos.com_catalogsources.yaml").DeepCopy()
}

// ClusterServiceVersion returns a copy of the CustomResourceDefinition for the latest version of the ClusterServiceVersion API.
func ClusterServiceVersion() *apiextensionsv1.CustomResourceDefinition {
return getCRD("operators.coreos.com_clusterserviceversions.yaml").DeepCopy()
}

// InstallPlan returns a copy of the CustomResourceDefinition for the latest version of the InstallPlan API.
func InstallPlan() *apiextensionsv1.CustomResourceDefinition {
return getCRD("operators.coreos.com_installplans.yaml").DeepCopy()
}

// OperatorGroup returns a copy of the CustomResourceDefinition for the latest version of the OperatorGroup API.
func OperatorGroup() *apiextensionsv1.CustomResourceDefinition {
return getCRD("operators.coreos.com_operatorgroups.yaml").DeepCopy()
}

// Operator returns a copy of the CustomResourceDefinition for the latest version of the Operator API.
func Operator() *apiextensionsv1.CustomResourceDefinition {
return getCRD("operators.coreos.com_operators.yaml").DeepCopy()
}

// Subscription returns a copy of the CustomResourceDefinition for the latest version of the Subscription API.
func Subscription() *apiextensionsv1.CustomResourceDefinition {
return getCRD("operators.coreos.com_subscriptions.yaml").DeepCopy()
}
56 changes: 56 additions & 0 deletions crds/defs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package crds

import (
"reflect"
"testing"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

var emptyCRD = &apiextensionsv1.CustomResourceDefinition{}

func TestGetters(t *testing.T) {
tests := []struct {
description string
get func() *apiextensionsv1.CustomResourceDefinition
}{
{
description: "CatalogSource",
get: CatalogSource,
},
{
description: "ClusterServiceVersion",
get: ClusterServiceVersion,
},
{
description: "InstallPlan",
get: InstallPlan,
},
{
description: "OperatorGroup",
get: OperatorGroup,
},
{
description: "Operator",
get: Operator,
},
{
description: "Subscription",
get: Subscription,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
defer func() {
if x := recover(); x != nil {
t.Errorf("panic loading crd: %v", x)
}
}()

crd := tt.get()
if crd == nil || reflect.DeepEqual(crd, emptyCRD) {
t.Error("loaded CustomResourceDefinition is empty")
}
})
}
}
2 changes: 2 additions & 0 deletions crds/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package crds contains CustomResourceDefinition manifests for operator-framework APIs.
package crds
Loading