Skip to content

Commit

Permalink
Adds support for conditional CRDs (#506)
Browse files Browse the repository at this point in the history
* [ci skip] Adds support for conditional CRDs

* [ci skip] Adds support for conditional CRDs

* test

* codegen

* updates
  • Loading branch information
chunter0 authored Oct 9, 2023
1 parent 8e5473d commit d53ac92
Show file tree
Hide file tree
Showing 19 changed files with 236 additions and 73 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ generated-code: install-tools update-licenses
$(DEPSGOBIN)/goimports -w .
go mod tidy

generate-changelog:
@ci/changelog.sh

#----------------------------------------------------------------------------------
# Test
#----------------------------------------------------------------------------------
Expand All @@ -80,6 +83,9 @@ run-tests:
-keepGoing
$(DEPSGOBIN)/goimports -w .

run-test:
PATH=$(DEPSGOBIN):$$PATH ginkgo $(GINKGO_FLAGS) $(TEST_PKG)

#----------------------------------------------------------------------------------
# Third Party License Management
#----------------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions changelog/v0.34.7/conditional-crds.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
changelog:
- type: NEW_FEATURE
issueLink: https://github.com/solo-io/skv2/issues/505
description: >
Adds support for conditional CRDs. This allows users to specify a condition that must be true for the CRD to be installed.
23 changes: 23 additions & 0 deletions ci/changelog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -e

CURRENT_VERSION=$(git describe --tags --always --abbrev=0)
OLD_VERSION=$(echo -n $CURRENT_VERSION | sed -E 's/.*[^0-9]([0-9]+)$/\1/')
NEW_VERSION=$((OLD_VERSION + 1))
NEXT_VERSION=$(echo -n $CURRENT_VERSION | sed -E "s/$OLD_VERSION$/$NEW_VERSION/")
BRANCH_NAME=$(git symbolic-ref -q HEAD | sed 's#^.*/##')
DESCRIPTION=${DESCRIPTION:=""}
SKIP_CI=${SKIP_CI:-"false"}

mkdir -p "changelog/$NEXT_VERSION"

cat <<EOF > "changelog/$NEXT_VERSION/$BRANCH_NAME.yaml"
changelog:
- type: NON_USER_FACING
issueLink: ${ISSUE_LINK}
description: >
"${DESCRIPTION}"
skipCI: "${SKIP_CI}"
EOF
echo Created "changelog/$NEXT_VERSION/$BRANCH_NAME.yaml"
15 changes: 14 additions & 1 deletion codegen/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ type Command struct {
// the name of the flag to pass the list of enabled alpha-level crds
// used in codegen/templates/manifests/crd.yamltmpl
EnabledAlphaApiFlagName string

// Optional: If true, skip rendering the CRD manifest
SkipCrdsManifest bool
}

// function to execute skv2 code gen from another repository
Expand Down Expand Up @@ -272,8 +275,18 @@ func (c Command) generateGroups(
return err
}
}
renderOpts := render.RenderOptions{
AppName: c.AppName,
ManifestRoot: c.ManifestRoot,
ProtoDir: c.ProtoDir,
EnabledAlphaApiFlagName: c.EnabledAlphaApiFlagName,
ProtoOpts: protoOpts,
Groups: grps,
GroupOptions: groupOptions,
SkipCrdsManifest: c.SkipCrdsManifest,
}

manifests, err := render.RenderManifests(c.AppName, c.ManifestRoot, c.ProtoDir, c.EnabledAlphaApiFlagName, protoOpts, groupOptions, grps)
manifests, err := render.RenderManifests(renderOpts)
if err != nil {
return err
}
Expand Down
32 changes: 32 additions & 0 deletions codegen/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,38 @@ var _ = Describe("Cmd", func() {
Expect(deployment).To(ContainSubstring(`{{ index $glooAgent "ports" "grpc" }}`))
Expect(deployment).To(ContainSubstring("{{ $Values.glooMgmtServer.statsPort }}"))
})
It("generates conditional crds", func() {
cmd := &Command{
Groups: []Group{{
GroupVersion: schema.GroupVersion{
Group: "things.test.io",
Version: "v1",
},
Resources: []Resource{{
Kind: "Paint",
Spec: Field{Type: Type{Name: "PaintSpec"}},
Status: &Field{Type: Type{Name: "PaintStatus"}},
// Stored: true,
CustomEnableCondition: "$.Values.installValue",
}},
RenderManifests: true,
}},
SkipCrdsManifest: true, // Use templates folder to install crds conditionally
Chart: &Chart{
Values: map[string]interface{}{
"installValue": true,
},
},
ManifestRoot: "codegen/test/chart/conditional-crds",
}
Expect(cmd.Execute()).NotTo(HaveOccurred(), "failed to execute command")

crdFilePath := filepath.Join(util.GetModuleRoot(), cmd.ManifestRoot, "/templates/things.test.io_crds.yaml")

bytes, err := os.ReadFile(crdFilePath)
Expect(err).NotTo(HaveOccurred())
Expect(string(bytes)).To(ContainSubstring("{{- if $.Values.installValue }}"))
})
It("generates controller code and manifests for a proto file", func() {
cmd := &Command{
Groups: []Group{
Expand Down
16 changes: 13 additions & 3 deletions codegen/kuberesource/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
// Create CRDs for a group
func CustomResourceDefinitions(
groups []*model.Group,
) (objects []apiextv1.CustomResourceDefinition, err error) {
) (objects []GlooCustomResourceDefinition, err error) {
resourcesByKind := make(map[string][]model.Resource)
skipHashByKind := make(map[string]bool)
for _, group := range groups {
Expand Down Expand Up @@ -174,11 +174,20 @@ func validateCRDResources(resources []model.Resource) error {
return nil
}

type GlooCustomResourceDefinition struct {
*apiextv1.CustomResourceDefinition

// Optional: if specified from the resource, this crd definition will be wrapped in the given conditional
//
// E.g: `and (.Values.customValueA) (.Values.customValueB)`
CustomEnableCondition string `json:"-" yaml:"-"`
}

func CustomResourceDefinition(
resources []model.Resource,
validationSchemas map[string]*apiextv1.CustomResourceValidation,
withoutSpecHash bool,
) (*apiextv1.CustomResourceDefinition, error) {
) (*GlooCustomResourceDefinition, error) {

err := validateCRDResources(resources)
if err != nil {
Expand All @@ -189,6 +198,7 @@ func CustomResourceDefinition(
kind := resources[0].Kind
kindLowerPlural := strings.ToLower(stringutils.Pluralize(kind))
kindLower := strings.ToLower(kind)
customEnableCondition := resources[0].CustomEnableCondition

scope := apiextv1.NamespaceScoped
if resources[0].ClusterScoped {
Expand Down Expand Up @@ -252,5 +262,5 @@ func CustomResourceDefinition(
// Setting PreserveUnknownFields to false ensures that objects with unknown fields are rejected.
crd.Spec.PreserveUnknownFields = false
}
return crd, nil
return &GlooCustomResourceDefinition{CustomResourceDefinition: crd, CustomEnableCondition: customEnableCondition}, nil
}
5 changes: 5 additions & 0 deletions codegen/model/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ type Resource struct {
// Set to false by default
// See https://kubernetes.io/docs/reference/kubernetes-api/extend-resources/custom-resource-definition-v1/#CustomResourceDefinitionSpec
Deprecated bool

// Optional: if specified, this crd resource definition will be wrapped in the given conditional
//
// E.g: `and (.Values.customValueA) (.Values.customValueB)`
CustomEnableCondition string
}

type Field struct {
Expand Down
6 changes: 3 additions & 3 deletions codegen/render/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"text/template"

"github.com/invopop/jsonschema"
"github.com/solo-io/skv2/codegen/kuberesource"
"github.com/solo-io/skv2/codegen/model/values"
"github.com/solo-io/skv2/codegen/util/stringutils"
"google.golang.org/protobuf/types/known/structpb"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
Expand Down Expand Up @@ -134,11 +134,11 @@ func makeTemplateFuncs(customFuncs template.FuncMap) template.FuncMap {
"toListItem": toListItem,
"opVar": opVar,

"render_outer_conditional_crd_template": func(crd apiextv1.CustomResourceDefinition, currentVersion string, skips map[string]bool) bool {
"render_outer_conditional_crd_template": func(crd kuberesource.GlooCustomResourceDefinition, currentVersion string, skips map[string]bool) bool {
return len(crd.Spec.Versions) < 2 && strings.Contains(currentVersion, "alpha") && !skips[crd.Spec.Group+"/"+currentVersion]
},

"render_inner_conditional_crd_template": func(crd apiextv1.CustomResourceDefinition, currentVersion string, skips map[string]bool) bool {
"render_inner_conditional_crd_template": func(crd kuberesource.GlooCustomResourceDefinition, currentVersion string, skips map[string]bool) bool {
return len(crd.Spec.Versions) > 1 && strings.Contains(currentVersion, "alpha") && !skips[crd.Spec.Group+"/"+currentVersion]
},
}
Expand Down
55 changes: 34 additions & 21 deletions codegen/render/manifests_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,37 @@ type ManifestsRenderer struct {
// the name of the flag to pass the list of enabled alpha-level crds
// used in codegen/templates/manifests/crd.yamltmpl
EnabledAlphaApiFlagName string

// Optional: If true, skip rendering the CRD manifest
skipCrdsManifest bool
}

type templateArgs struct {
Crds []apiextv1.CustomResourceDefinition
Crds []kuberesource.GlooCustomResourceDefinition
ShouldSkip map[string]bool
EnabledAlphaApiFlagName string
}

func RenderManifests(
appName, manifestDir, protoDir, enabledAlphaApiFlagName string,
protoOpts protoutil.Options,
groupOptions model.GroupOptions,
grps []*Group,
) ([]OutFile, error) {
type RenderOptions struct {
AppName string
ManifestRoot string
ProtoDir string
EnabledAlphaApiFlagName string
ProtoOpts protoutil.Options
Groups []*Group
GroupOptions model.GroupOptions
SkipCrdsManifest bool
}

func RenderManifests(opts RenderOptions) ([]OutFile, error) {
defaultManifestsRenderer := ManifestsRenderer{
AppName: appName,
ManifestDir: manifestDir,
ProtoDir: protoDir,
EnabledAlphaApiFlagName: enabledAlphaApiFlagName,
AppName: opts.AppName,
ManifestDir: opts.ManifestRoot,
ProtoDir: opts.ProtoDir,
EnabledAlphaApiFlagName: opts.EnabledAlphaApiFlagName,
skipCrdsManifest: opts.SkipCrdsManifest,
}
return defaultManifestsRenderer.RenderManifests(grps, protoOpts, groupOptions)
return defaultManifestsRenderer.RenderManifests(opts.Groups, opts.ProtoOpts, opts.GroupOptions)
}

func (r ManifestsRenderer) RenderManifests(grps []*Group, protoOpts protoutil.Options, groupOptions model.GroupOptions) ([]OutFile, error) {
Expand Down Expand Up @@ -100,13 +110,16 @@ func (r ManifestsRenderer) RenderManifests(grps []*Group, protoOpts protoutil.Op
if err != nil {
return nil, err
}
out, err := r.renderCRDManifest(r.AppName, groupName, crds)
if err != nil {
return nil, err

if !r.skipCrdsManifest {
out, err := r.renderCRDManifest(r.AppName, groupName, crds)
if err != nil {
return nil, err
}
renderedFiles = append(renderedFiles, out)
}
renderedFiles = append(renderedFiles, out)

out, err = r.renderTemplatedCRDManifest(r.AppName, groupName, crds, grandfatheredGroups)
out, err := r.renderTemplatedCRDManifest(r.AppName, groupName, crds, grandfatheredGroups)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -257,7 +270,7 @@ func SetVersionForObject(obj metav1.Object, version string) {
}

// TODO (dmitri-d): this can be removed once we migrate to use platform charts exclusively
func (r ManifestsRenderer) renderCRDManifest(appName, groupName string, objs []apiextv1.CustomResourceDefinition) (OutFile, error) {
func (r ManifestsRenderer) renderCRDManifest(appName, groupName string, objs []kuberesource.GlooCustomResourceDefinition) (OutFile, error) {
outFile := OutFile{
Path: r.ManifestDir + "/crds/" + groupName + "_" + "crds.yaml",
}
Expand All @@ -276,7 +289,7 @@ func (r ManifestsRenderer) renderCRDManifest(appName, groupName string, objs []a
}

func (r ManifestsRenderer) renderTemplatedCRDManifest(appName, groupName string,
objs []apiextv1.CustomResourceDefinition,
objs []kuberesource.GlooCustomResourceDefinition,
grandfatheredGroups map[string]bool) (OutFile, error) {

renderer := DefaultTemplateRenderer
Expand Down Expand Up @@ -310,7 +323,7 @@ func (r ManifestsRenderer) renderTemplatedCRDManifest(appName, groupName string,
return files[0], nil
}

func (r ManifestsRenderer) canRenderCRDTemplate(objs []apiextv1.CustomResourceDefinition, grandfatheredGroups map[string]bool) error {
func (r ManifestsRenderer) canRenderCRDTemplate(objs []kuberesource.GlooCustomResourceDefinition, grandfatheredGroups map[string]bool) error {
for _, obj := range objs {
for _, v := range obj.Spec.Versions {
if strings.Contains(v.Name, "alpha") && !grandfatheredGroups[obj.Spec.Group+"/"+v.Name] && r.EnabledAlphaApiFlagName == "" {
Expand All @@ -321,7 +334,7 @@ func (r ManifestsRenderer) canRenderCRDTemplate(objs []apiextv1.CustomResourceDe
return nil
}

func (r ManifestsRenderer) createCrds(appName string, groups []*Group) ([]apiextv1.CustomResourceDefinition, error) {
func (r ManifestsRenderer) createCrds(appName string, groups []*Group) ([]kuberesource.GlooCustomResourceDefinition, error) {
objs, err := kuberesource.CustomResourceDefinitions(groups)
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit d53ac92

Please sign in to comment.