Skip to content

Additional changes for OpenShift certification #701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Mar 5, 2025
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ build/
_output/
bin/
bundle/
catalog/
temp/

# OpenShift pre-flight
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ ARG release

LABEL "com.oracle.coherence.application"="operator"
LABEL "com.oracle.coherence.version"="$version"
LABEL "org.opencontainers.image.version"="$version"
LABEL "org.opencontainers.image.revision"="$release"
LABEL "org.opencontainers.image.description"="The Oracle Coherece Kubernetes Operator image ($target)"
LABEL "org.opencontainers.image.source"="https://github.com/oracle/coherence-operator"
LABEL "org.opencontainers.image.authors"="To contact the authors use this link https://github.com/oracle/coherence-operator/discussions"
LABEL "org.opencontainers.image.licenses"="UPL-1.0"
LABEL "org.opencontainers.image.description"="The Oracle Coherece Kubernetes Operator allows full lifecycle management of Oracle Coherence workloads in Kubernetes."
LABEL "org.opencontainers.image.description"="The Oracle Coherence Kubernetes Operator allows full lifecycle management of Oracle Coherence workloads in Kubernetes."

LABEL "name"="Oracle Coherence Kubernetes Operator"
LABEL "vendor"="Oracle"
LABEL "version"="$version"
LABEL "release"="$release"
LABEL "maintainer"="Oracle Coherence Engieering Team"
LABEL "maintainer"="Oracle Coherence Engineering Team"
LABEL "summary"="A Kubernetes Operator for managing Oracle Coherence clusters"
LABEL "description"="The Oracle Coherece Kubernetes Operator allows full lifecycle management of Oracle Coherence workloads in Kubernetes."

Expand Down
251 changes: 186 additions & 65 deletions Makefile

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions PROJECT
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
domain: oracle.com
layout:
- go.kubebuilder.io/v4
multigroup: false
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}
projectName: coherence-operator
repo: github.com/oracle/coherence-operator
resources:
-
- api:
crdVersion: v1
namespaced: true
controller: true
domain: oracle.com
group: coherence.oracle.com
kind: Coherence
path: github.com/oracle/coherence-operator/api/v1
plural: coherence
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: oracle.com
group: coherence.oracle.com
kind: CoherenceJob
path: github.com/oracle/coherence-operator/api/v1
plural: coherencejob
version: v1
version: "3"
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}
85 changes: 54 additions & 31 deletions api/v1/coherence_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package v1

import (
"context"
"fmt"
"github.com/distribution/reference"
"github.com/go-test/deep"
Expand All @@ -31,8 +32,11 @@ var (
)

func (in *Coherence) SetupWebhookWithManager(mgr ctrl.Manager) error {
hook := &Coherence{}
return ctrl.NewWebhookManagedBy(mgr).
For(in).
WithDefaulter(hook).
WithValidator(hook).
Complete()
}

Expand All @@ -44,22 +48,28 @@ const MutatingWebHookPath = "/mutate-coherence-oracle-com-v1-coherence"

// An anonymous var to ensure that the Coherence struct implements webhook.Defaulter
// there will be a compile time error here if this fails.
var _ webhook.Defaulter = &Coherence{}
var _ webhook.CustomDefaulter = &Coherence{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (in *Coherence) Default() {
spec, _ := in.GetStatefulSetSpec()
func (in *Coherence) Default(_ context.Context, obj runtime.Object) error {
coh, ok := obj.(*Coherence)
if !ok {
return fmt.Errorf("expected a Coherence instance but got a %T", obj)
}

spec, _ := coh.GetStatefulSetSpec()
// set the default replicas if not present
if spec.Replicas == nil {
spec.SetReplicas(spec.GetReplicas())
}
SetCommonDefaults(in)
SetCommonDefaults(coh)

// apply a label with the hash of the spec - ths must be the last action here to make sure that
// any modifications to the spec field are included in the hash
if hash, applied := EnsureCoherenceHashLabel(in); applied {
if hash, applied := EnsureCoherenceHashLabel(coh); applied {
webhookLogger.Info(fmt.Sprintf("Applied %s label", LabelCoherenceHash), "hash", hash)
}
return nil
}

// SetCommonDefaults sets defaults common to both a Job and StatefulSet
Expand Down Expand Up @@ -135,31 +145,36 @@ const ValidatingWebHookPath = "/validate-coherence-oracle-com-v1-coherence"

// An anonymous var to ensure that the Coherence struct implements webhook.Validator
// there will be a compile time error here if this fails.
var _ webhook.Validator = &Coherence{}
var _ webhook.CustomValidator = &Coherence{}
var commonWebHook = CommonWebHook{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
// The optional warnings will be added to the response as warning messages.
// Return an error if the object is invalid.
func (in *Coherence) ValidateCreate() (admission.Warnings, error) {
logger := webhookLogger.WithValues("namespace", in.GetNamespace(), "name", in.GetName())
func (in *Coherence) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
coh, ok := obj.(*Coherence)
if !ok {
return nil, fmt.Errorf("expected a Coherence instance but got a %T", obj)
}

logger := webhookLogger.WithValues("namespace", coh.GetNamespace(), "name", coh.GetName())
var warnings admission.Warnings

dt := in.GetDeletionTimestamp()
dt := coh.GetDeletionTimestamp()
if dt != nil {
// the deletion timestamp is set so do nothing
logger.Info("Skipping validation for deleted resource", "deletionTimestamp", *dt)
return warnings, nil
}

webhookLogger.Info("validate create", "name", in.Name)
if err := commonWebHook.validateReplicas(in); err != nil {
webhookLogger.Info("validate create", "name", coh.Name)
if err := commonWebHook.validateReplicas(coh); err != nil {
return warnings, err
}
if err := commonWebHook.validateImages(in); err != nil {
if err := commonWebHook.validateImages(coh); err != nil {
return warnings, err
}
if err := commonWebHook.validateNodePorts(in); err != nil {
if err := commonWebHook.validateNodePorts(coh); err != nil {
return warnings, err
}
return warnings, nil
Expand All @@ -168,39 +183,47 @@ func (in *Coherence) ValidateCreate() (admission.Warnings, error) {
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
// The optional warnings will be added to the response as warning messages.
// Return an error if the object is invalid.
func (in *Coherence) ValidateUpdate(previous runtime.Object) (admission.Warnings, error) {
webhookLogger.Info("validate update", "name", in.Name)
logger := webhookLogger.WithValues("namespace", in.GetNamespace(), "name", in.GetName())
func (in *Coherence) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
cohNew, ok := newObj.(*Coherence)
if !ok {
return nil, fmt.Errorf("expected a Coherence instance for new value but got a %T", newObj)
}
cohPrev, ok := oldObj.(*Coherence)
if !ok {
return nil, fmt.Errorf("expected a Coherence instance for old value but got a %T", newObj)
}

webhookLogger.Info("validate update", "name", cohNew.Name)
logger := webhookLogger.WithValues("namespace", cohNew.GetNamespace(), "name", cohNew.GetName())
var warnings admission.Warnings

dt := in.GetDeletionTimestamp()
dt := cohNew.GetDeletionTimestamp()
if dt != nil {
// the deletion timestamp is set so do nothing
logger.Info("Skipping validation for deleted resource", "deletionTimestamp", *dt)
return warnings, nil
}

if err := commonWebHook.validateReplicas(in); err != nil {
if err := commonWebHook.validateReplicas(cohNew); err != nil {
return warnings, err
}
if err := commonWebHook.validateImages(in); err != nil {
if err := commonWebHook.validateImages(cohNew); err != nil {
return warnings, err
}
prev := previous.(*Coherence)

if err := commonWebHook.validatePersistence(in, prev); err != nil {
if err := commonWebHook.validatePersistence(cohNew, cohPrev); err != nil {
return warnings, err
}
if err := in.validateVolumeClaimTemplates(prev); err != nil {
if err := cohNew.validateVolumeClaimTemplates(cohNew, cohPrev); err != nil {
return warnings, err
}
if err := commonWebHook.validateNodePorts(in); err != nil {
if err := commonWebHook.validateNodePorts(cohNew); err != nil {
return warnings, err
}

var errorList field.ErrorList
sts := in.Spec.CreateStatefulSet(in)
stsOld := prev.Spec.CreateStatefulSet(prev)
sts := cohNew.Spec.CreateStatefulSet(cohNew)
stsOld := cohPrev.Spec.CreateStatefulSet(cohPrev)
errorList = ValidateStatefulSetUpdate(&sts, &stsOld)

if len(errorList) > 0 {
Expand All @@ -213,27 +236,27 @@ func (in *Coherence) ValidateUpdate(previous runtime.Object) (admission.Warnings
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
// The optional warnings will be added to the response as warning messages.
// Return an error if the object is invalid.
func (in *Coherence) ValidateDelete() (admission.Warnings, error) {
func (in *Coherence) ValidateDelete(context.Context, runtime.Object) (admission.Warnings, error) {
// we do not need to validate deletions
return nil, nil
}

func (in *Coherence) validateVolumeClaimTemplates(previous *Coherence) error {
if in.GetReplicas() == 0 || previous.GetReplicas() == 0 {
func (in *Coherence) validateVolumeClaimTemplates(cohNew, cohPrev *Coherence) error {
if cohNew.GetReplicas() == 0 || cohPrev.GetReplicas() == 0 {
// changes are allowed if current or previous replicas == 0
return nil
}

if len(in.Spec.VolumeClaimTemplates) == 0 && len(previous.Spec.VolumeClaimTemplates) == 0 {
if len(cohNew.Spec.VolumeClaimTemplates) == 0 && len(cohPrev.Spec.VolumeClaimTemplates) == 0 {
// no PVCs in either deployment
return nil
}

diff := deep.Equal(previous.Spec.VolumeClaimTemplates, in.Spec.VolumeClaimTemplates)
diff := deep.Equal(cohPrev.Spec.VolumeClaimTemplates, cohNew.Spec.VolumeClaimTemplates)
if len(diff) != 0 {
return fmt.Errorf("the Coherence resource \"%s\" is invalid: "+
"changes cannot be made to spec.volumeclaimtemplates unless spec.replicas == 0 or the previous"+
" instance of the resource has spec.replicas == 0", in.Name)
" instance of the resource has spec.replicas == 0", cohNew.Name)
}
return nil
}
Expand Down
Loading
Loading