Skip to content
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
7 changes: 1 addition & 6 deletions internal/operator-controller/applier/boxcutter.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,7 @@ func (r *SimpleRevisionGenerator) GenerateRevision(bundleFS fs.FS, ext *ocv1.Clu
},
},
Spec: ocv1.ClusterExtensionRevisionSpec{
Phases: []ocv1.ClusterExtensionRevisionPhase{
{
Name: "everything",
Objects: objs,
},
},
Phases: PhaseSort(objs),
},
}, nil
}
Expand Down
14 changes: 7 additions & 7 deletions internal/operator-controller/applier/boxcutter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func Test_SimpleRevisionGenerator_Success(t *testing.T) {
t.Log("by checking the rendered objects are present in the correct phases")
require.Equal(t, []ocv1.ClusterExtensionRevisionPhase{
{
Name: "everything",
Name: string(applier.PhaseDeploy),
Objects: []ocv1.ClusterExtensionRevisionObject{
{
Object: unstructured.Unstructured{
Expand Down Expand Up @@ -268,7 +268,7 @@ func TestBoxcutter_Apply(t *testing.T) {
UID: "test-uid",
},
}
defaultDesiredHash := "2a3d3548913494df7d4cbaef51cb6c36f6f67399cbfe2dc6a3cc49e4db0083ae"
defaultDesiredHash := "faaeb52a1cb7c968c96278bc1cd804e50d3ae9faae08807c9279a5e569933ea0"
defaultDesiredRevision := &ocv1.ClusterExtensionRevision{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ext-1",
Expand All @@ -284,7 +284,7 @@ func TestBoxcutter_Apply(t *testing.T) {
Revision: 1,
Phases: []ocv1.ClusterExtensionRevisionPhase{
{
Name: "everything",
Name: string(applier.PhaseDeploy),
Objects: []ocv1.ClusterExtensionRevisionObject{
{
Object: unstructured.Unstructured{
Expand Down Expand Up @@ -324,7 +324,7 @@ func TestBoxcutter_Apply(t *testing.T) {
Spec: ocv1.ClusterExtensionRevisionSpec{
Phases: []ocv1.ClusterExtensionRevisionPhase{
{
Name: "everything",
Name: string(applier.PhaseDeploy),
Objects: []ocv1.ClusterExtensionRevisionObject{
{
Object: unstructured.Unstructured{
Expand Down Expand Up @@ -373,7 +373,7 @@ func TestBoxcutter_Apply(t *testing.T) {
Spec: ocv1.ClusterExtensionRevisionSpec{
Phases: []ocv1.ClusterExtensionRevisionPhase{
{
Name: "everything",
Name: string(applier.PhaseDeploy),
Objects: []ocv1.ClusterExtensionRevisionObject{
{
Object: unstructured.Unstructured{
Expand Down Expand Up @@ -419,7 +419,7 @@ func TestBoxcutter_Apply(t *testing.T) {
Spec: ocv1.ClusterExtensionRevisionSpec{
Phases: []ocv1.ClusterExtensionRevisionPhase{
{
Name: "everything",
Name: string(applier.PhaseDeploy),
Objects: []ocv1.ClusterExtensionRevisionObject{
{
Object: unstructured.Unstructured{
Expand Down Expand Up @@ -460,7 +460,7 @@ func TestBoxcutter_Apply(t *testing.T) {

assert.Equal(t, "test-ext-2", newRev.Name)
assert.Equal(t, int64(2), newRev.Spec.Revision)
assert.Equal(t, "bc1c7457a476193460747e8223fff9b492f0a2f60057831fb55a88ec8c2387b2", newRev.Annotations[applier.RevisionHashAnnotation])
assert.Equal(t, "ec8213d4061a75b55cd67a009d9cdeb1bdd6f503d4b3bb7b6cfea3a5233aad43", newRev.Annotations[applier.RevisionHashAnnotation])
require.Len(t, newRev.Spec.Previous, 1)
assert.Equal(t, "test-ext-1", newRev.Spec.Previous[0].Name)
assert.Equal(t, types.UID("rev-uid-1"), newRev.Spec.Previous[0].UID)
Expand Down
136 changes: 136 additions & 0 deletions internal/operator-controller/applier/phase.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package applier

import (
"k8s.io/apimachinery/pkg/runtime/schema"

ocv1 "github.com/operator-framework/operator-controller/api/v1"
)

// The following, with modifications, is taken from:
// https://github.com/package-operator/package-operator/blob/v1.18.2/internal/packages/internal/packagekickstart/presets/phases.go
//
// Determines a phase using the objects Group Kind from a list of presets.
// Defaults to the `deploy` phase if no preset was found. Runtimes that
// depend on a custom resource to start i.e. certmanager's Certificate
// will require this.
func determinePhase(gk schema.GroupKind) Phase {
phase, ok := gkPhaseMap[gk]
if !ok {
return PhaseDeploy
}
return phase
}

// Phase represents a well-known phase.
type Phase string

const (
PhaseNamespaces Phase = "namespaces"
PhasePolicies Phase = "policies"
PhaseRBAC Phase = "rbac"
PhaseCRDs Phase = "crds"
PhaseStorage Phase = "storage"
PhaseDeploy Phase = "deploy"
PhasePublish Phase = "publish"
)

// Well known phases ordered.
var defaultPhaseOrder = []Phase{
PhaseNamespaces,
PhasePolicies,
PhaseRBAC,
PhaseCRDs,
PhaseStorage,
PhaseDeploy,
PhasePublish,
}

var (
// This will be populated from `phaseGKMap` in an init func!
gkPhaseMap = map[schema.GroupKind]Phase{}
phaseGKMap = map[Phase][]schema.GroupKind{
PhaseNamespaces: {
{Kind: "Namespace"},
},

PhasePolicies: {
{Kind: "ResourceQuota"},
{Kind: "LimitRange"},
{Kind: "PriorityClass", Group: "scheduling.k8s.io"},
{Kind: "NetworkPolicy", Group: "networking.k8s.io"},
{Kind: "HorizontalPodAutoscaler", Group: "autoscaling"},
{Kind: "PodDisruptionBudget", Group: "policy"},
},

PhaseRBAC: {
{Kind: "ServiceAccount"},
{Kind: "Role", Group: "rbac.authorization.k8s.io"},
{Kind: "RoleBinding", Group: "rbac.authorization.k8s.io"},
{Kind: "ClusterRole", Group: "rbac.authorization.k8s.io"},
{Kind: "ClusterRoleBinding", Group: "rbac.authorization.k8s.io"},
},

PhaseCRDs: {
{Kind: "CustomResourceDefinition", Group: "apiextensions.k8s.io"},
},

PhaseStorage: {
{Kind: "PersistentVolume"},
{Kind: "PersistentVolumeClaim"},
{Kind: "StorageClass", Group: "storage.k8s.io"},
},

PhaseDeploy: {
{Kind: "Deployment", Group: "apps"},
{Kind: "DaemonSet", Group: "apps"},
{Kind: "StatefulSet", Group: "apps"},
{Kind: "ReplicaSet"},
{Kind: "Pod"}, // probing complicated, may be either Completed or Available.
{Kind: "Job", Group: "batch"},
{Kind: "CronJob", Group: "batch"},
{Kind: "Service"},
{Kind: "Secret"},
{Kind: "ConfigMap"},
},

PhasePublish: {
{Kind: "Ingress", Group: "networking.k8s.io"},
{Kind: "APIService", Group: "apiregistration.k8s.io"},
{Kind: "Route", Group: "route.openshift.io"},
{Kind: "MutatingWebhookConfiguration", Group: "admissionregistration.k8s.io"},
{Kind: "ValidatingWebhookConfiguration", Group: "admissionregistration.k8s.io"},
},
}
)

func init() {
for phase, gks := range phaseGKMap {
for _, gk := range gks {
gkPhaseMap[gk] = phase
}
}
}

// PhaseSort takes an unsorted list of objects and organizes them into sorted phases.
// Each phase will be applied in order according to DefaultPhaseOrder. Objects
// within a single phase are applied simultaneously.
func PhaseSort(unsortedObjs []ocv1.ClusterExtensionRevisionObject) []ocv1.ClusterExtensionRevisionPhase {
phasesSorted := make([]ocv1.ClusterExtensionRevisionPhase, 0)
phaseMap := make(map[Phase][]ocv1.ClusterExtensionRevisionObject, 0)

for _, obj := range unsortedObjs {
phase := determinePhase(obj.Object.GroupVersionKind().GroupKind())
phaseMap[phase] = append(phaseMap[phase], obj)
}

for _, phaseName := range defaultPhaseOrder {
if objs, ok := phaseMap[phaseName]; ok {
phasesSorted = append(phasesSorted, ocv1.ClusterExtensionRevisionPhase{
Name: string(phaseName),
Objects: objs,
})
}
}

return phasesSorted
}
Loading
Loading