Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
syntassodev committed Jan 25, 2024
2 parents 8e8f6bd + 99cf9df commit 3ee884b
Show file tree
Hide file tree
Showing 21 changed files with 520 additions and 166 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ jobs:
uses: CircleCI-Public/trigger-circleci-pipeline-action@v1.1.0
with:
GHA_Meta: ${{ github.event.release.tag_name }}
target-slug: ${{ secrets.CIRCLECI_TARGET_SLUG }}
target-slug: ${{ secrets.CCI_TARGET_SLUG }}
env:
CCI_TOKEN: ${{ secrets.CCI_TOKEN }}
3 changes: 3 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,7 @@ resources:
kind: PromiseRelease
path: github.com/syntasso/kratix/api/v1alpha1
version: v1alpha1
webhooks:
validation: true
webhookVersion: v1
version: "3"
4 changes: 4 additions & 0 deletions api/v1alpha1/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ func SetClientSet(cs clientset.Interface) {
func SetClient(c client.Client) {
k8sClient = c
}

func SetPromiseFetcher(pf PromiseFetcher) {
promiseFetcher = pf
}
8 changes: 8 additions & 0 deletions api/v1alpha1/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package v1alpha1

// +kubebuilder:object:generate=false
//
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . PromiseFetcher
type PromiseFetcher interface {
FromURL(string) (*Promise, error)
}
5 changes: 1 addition & 4 deletions api/v1alpha1/promise_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,14 @@ func (p *Promise) SetupWebhookWithManager(mgr ctrl.Manager, cs *clientset.Client
Complete()
}

//+kubebuilder:webhook:path=/mutate-platform-kratix-io-v1alpha1-promise,mutating=true,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promises,verbs=create;update,versions=v1alpha1,name=mpromise.kb.io,admissionReviewVersions=v1

var _ webhook.Defaulter = &Promise{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (p *Promise) Default() {
promiselog.Info("default", "name", p.Name)
}

//+kubebuilder:webhook:path=/validate-platform-kratix-io-v1alpha1-promise,mutating=false,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promises,verbs=create;update,versions=v1alpha1,name=vpromise.kb.io,admissionReviewVersions=v1

// +kubebuilder:webhook:path=/validate-platform-kratix-io-v1alpha1-promise,mutating=false,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promises,verbs=create;update,versions=v1alpha1,name=vpromise.kb.io,admissionReviewVersions=v1
var _ webhook.Validator = &Promise{}

func (p *Promise) validateCRD() error {
Expand Down
9 changes: 7 additions & 2 deletions api/v1alpha1/promiserelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
TypeHTTP = "http"
)

// PromiseReleaseSpec defines the desired state of PromiseRelease
type PromiseReleaseSpec struct {
Version string `json:"version,omitempty"`
Expand All @@ -33,13 +37,14 @@ type SourceRef struct {

// PromiseReleaseStatus defines the observed state of PromiseRelease
type PromiseReleaseStatus struct {
Installed bool `json:"installed,omitempty"`
Status string `json:"status,omitempty"`
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:resource:scope=Cluster,path=promisereleases
//+kubebuilder:printcolumn:JSONPath=".status.installed",name="Installed",type=boolean
//+kubebuilder:printcolumn:JSONPath=".status.status",name="Status",type=string
//+kubebuilder:printcolumn:JSONPath=".spec.version",name="Version",type=string

// PromiseRelease is the Schema for the promisereleases API
Expand Down
99 changes: 99 additions & 0 deletions api/v1alpha1/promiserelease_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Copyright 2021 Syntasso.
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 v1alpha1

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

const (
KratixPrefix = "kratix.io/"
PromiseVersionLabel = KratixPrefix + "promise-version"
)

var (
promiseFetcher PromiseFetcher
promisereleaselog = logf.Log.WithName("promiserelease-resource")
)

func (r *PromiseRelease) SetupWebhookWithManager(mgr ctrl.Manager, pf PromiseFetcher) error {
promiseFetcher = pf
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}

// +kubebuilder:webhook:path=/validate-platform-kratix-io-v1alpha1-promiserelease,mutating=false,failurePolicy=fail,sideEffects=None,groups=platform.kratix.io,resources=promisereleases,verbs=create;update,versions=v1alpha1,name=vpromiserelease.kb.io,admissionReviewVersions=v1
var _ webhook.Validator = &PromiseRelease{}

func (r *PromiseRelease) ValidateCreate() (admission.Warnings, error) {
promisereleaselog.Info("validate create", "name", r.Name)
if err := r.validate(); err != nil {
return nil, err
}

promise, err := promiseFetcher.FromURL(r.Spec.SourceRef.URL)
if err != nil {
return nil, fmt.Errorf("failed to fetch promise: %w", err)
}

promiseVersion, found := promise.GetLabels()[PromiseVersionLabel]
if !found {
msg := fmt.Sprintf("Warning: version label (%s) not found on promise, installation will fail", PromiseVersionLabel)
return []string{msg}, nil
}

if promiseVersion != r.Spec.Version {
msg := fmt.Sprintf("Warning: version labels do not match, found: %s, expected: %s, installation will fail", promiseVersion, r.Spec.Version)
return []string{msg}, nil
}

return nil, nil
}

func (r *PromiseRelease) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
promisereleaselog.Info("validate update", "name", r.Name)
// oldPromiseRelease, _ := old.(*PromiseRelease)
if err := r.validate(); err != nil {
return nil, err
}

return nil, nil
}

func (r *PromiseRelease) validate() error {
if r.Spec.SourceRef.Type != TypeHTTP {
return fmt.Errorf("unknown sourceRef type %q", r.Spec.SourceRef.Type)
}

if r.Spec.SourceRef.URL == "" {
return fmt.Errorf("sourceRef.url must be set")
}

return nil
}

func (r *PromiseRelease) ValidateDelete() (admission.Warnings, error) {
// promisereleaselog.Info("validate delete", "name", r.Name)
return nil, nil
}
116 changes: 116 additions & 0 deletions api/v1alpha1/promiserelease_webhook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package v1alpha1_test

import (
"fmt"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/syntasso/kratix/api/v1alpha1"
"github.com/syntasso/kratix/api/v1alpha1/v1alpha1fakes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("PromiseReleaseWebhook", func() {
var (
p *v1alpha1.Promise
pr *v1alpha1.PromiseRelease
promiseFetcher v1alpha1fakes.FakePromiseFetcher
)
BeforeEach(func() {
promiseFetcher = v1alpha1fakes.FakePromiseFetcher{}
v1alpha1.SetPromiseFetcher(&promiseFetcher)

pr = &v1alpha1.PromiseRelease{
ObjectMeta: metav1.ObjectMeta{
Name: "mycrds.group.example",
Namespace: "default",
},
Spec: v1alpha1.PromiseReleaseSpec{
Version: "v0.1.0",
SourceRef: v1alpha1.SourceRef{
Type: "http",
URL: "example.com",
},
},
}

p = &v1alpha1.Promise{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"kratix.io/promise-version": "v0.1.0",
},
},
}
})

When("source ref is unknown", func() {
It("errors on create and update", func() {
pr.Spec.SourceRef.Type = "ssh"
warnings, err := pr.ValidateCreate()
Expect(warnings).To(BeEmpty())
Expect(err).To(MatchError("unknown sourceRef type \"ssh\""))

warnings, err = pr.ValidateUpdate(pr)
Expect(warnings).To(BeEmpty())
Expect(err).To(MatchError("unknown sourceRef type \"ssh\""))
})
})

When("URL is empty", func() {
It("errors on create and update", func() {
pr.Spec.SourceRef.URL = ""
warnings, err := pr.ValidateCreate()
Expect(warnings).To(BeEmpty())
Expect(err).To(MatchError("sourceRef.url must be set"))

warnings, err = pr.ValidateUpdate(pr)
Expect(warnings).To(BeEmpty())
Expect(err).To(MatchError("sourceRef.url must be set"))
})
})

When("fetching the URL fails", func() {
It("errors on create", func() {
promiseFetcher.FromURLReturns(p, fmt.Errorf("foo"))
warnings, err := pr.ValidateCreate()
Expect(warnings).To(BeEmpty())
Expect(err).To(MatchError("failed to fetch promise: foo"))

warnings, err = pr.ValidateUpdate(pr)
Expect(warnings).To(BeEmpty())
Expect(err).NotTo(HaveOccurred())
})

It("does not error on update", func() {
//We only want to fetch it on create, its expensive to do this call
//frequently.
promiseFetcher.FromURLReturns(p, fmt.Errorf("foo"))
warnings, err := pr.ValidateUpdate(pr)
Expect(warnings).To(BeEmpty())
Expect(err).NotTo(HaveOccurred())
})
})

When("the promise is missing the label", func() {
It("emits a warning", func() {
p.Labels = map[string]string{}
promiseFetcher.FromURLReturns(p, nil)
warnings, err := pr.ValidateCreate()
Expect(err).NotTo(HaveOccurred())
Expect(warnings).To(ConsistOf("Warning: version label (kratix.io/promise-version) not found on promise, installation will fail"))
})
})

When("the promise is at a different version", func() {
It("emits a warning", func() {
p.Labels = map[string]string{
"kratix.io/promise-version": "v0.2.0",
}
promiseFetcher.FromURLReturns(p, nil)
warnings, err := pr.ValidateCreate()
Expect(err).NotTo(HaveOccurred())
Expect(warnings).To(ConsistOf("Warning: version labels do not match, found: v0.2.0, expected: v0.1.0, installation will fail"))
})
})
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3ee884b

Please sign in to comment.