Skip to content
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

Components API #280

Merged
merged 1 commit into from
Aug 17, 2020
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
65 changes: 61 additions & 4 deletions api/v1/quayregistry_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,28 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var allComponents = []string{
"postgres",
"clair",
"redis",
"storage",
}

// QuayRegistrySpec defines the desired state of QuayRegistry.
type QuayRegistrySpec struct {
// ConfigBundleSecret is the name of the Kubernetes `Secret` in the same namespace which contains the base Quay config and extra certs.
ConfigBundleSecret string `json:"configBundleSecret,omitempty"`
// ManagedComponents declare which supplemental services should be included in this Quay deployment.
ManagedComponents []ManagedComponent `json:"managedComponents,omitempty"`
// Components declare how the Operator should handle backing Quay services.
Components []Component `json:"components,omitempty"`
}

type ManagedComponent struct {
Kind string `json:"kind,omitempty"`
// Component describes how the Operator should handle a backing Quay service.
type Component struct {
// Kind is the unique name of this type of component.
Kind string `json:"kind"`
// Managed indicates whether or not the Operator is responsible for the lifecycle of this component.
// Default is true.
Managed bool `json:"managed"`
}

// QuayRegistryStatus defines the observed state of QuayRegistry.
Expand Down Expand Up @@ -57,6 +69,51 @@ type QuayRegistryList struct {
Items []QuayRegistry `json:"items"`
}

// EnsureDefaultComponents adds any `Components` which are missing from `Spec.Components`
// and returns a new `QuayRegistry` copy.
func EnsureDefaultComponents(quay *QuayRegistry) (*QuayRegistry, error) {
updatedQuay := quay.DeepCopy()
if updatedQuay.Spec.Components == nil {
updatedQuay.Spec.Components = []Component{}
}

for _, component := range allComponents {
found := false
for _, definedComponent := range quay.Spec.Components {
if component == definedComponent.Kind {
found = true
break
}
}
if !found {
updatedQuay.Spec.Components = append(updatedQuay.Spec.Components, Component{Kind: component, Managed: true})
}
}

return updatedQuay, nil
}

// ComponentsMatch returns true if both set of components are equivalent, and false otherwise.
func ComponentsMatch(firstComponents, secondComponents []Component) bool {
if len(firstComponents) != len(secondComponents) {
return false
}

for _, compA := range firstComponents {
equal := false
for _, compB := range secondComponents {
if compA.Kind == compB.Kind && compA.Managed == compB.Managed {
equal = true
break
}
}
if !equal {
return false
}
}
return true
}

func init() {
SchemeBuilder.Register(&QuayRegistry{}, &QuayRegistryList{})
}
143 changes: 143 additions & 0 deletions api/v1/quayregistry_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package v1

import (
"testing"

"github.com/stretchr/testify/assert"
)

var ensureDefaultComponentsTests = []struct {
name string
quay QuayRegistry
expected []Component
expectedErr error
}{
{
"AllComponentsProvided",
QuayRegistry{
Spec: QuayRegistrySpec{
Components: []Component{
{Kind: "postgres", Managed: true},
{Kind: "redis", Managed: true},
{Kind: "clair", Managed: true},
{Kind: "storage", Managed: true},
},
},
},
[]Component{
{Kind: "postgres", Managed: true},
{Kind: "redis", Managed: true},
{Kind: "clair", Managed: true},
{Kind: "storage", Managed: true},
},
nil,
},
{
"AllComponentsOmitted",
QuayRegistry{
Spec: QuayRegistrySpec{},
},
[]Component{
{Kind: "postgres", Managed: true},
{Kind: "redis", Managed: true},
{Kind: "clair", Managed: true},
{Kind: "storage", Managed: true},
},
nil,
},
{
"SomeComponentsProvided",
QuayRegistry{
Spec: QuayRegistrySpec{
Components: []Component{
{Kind: "postgres", Managed: false},
{Kind: "storage", Managed: false},
},
},
},
[]Component{
{Kind: "postgres", Managed: false},
{Kind: "redis", Managed: true},
{Kind: "clair", Managed: true},
{Kind: "storage", Managed: false},
},
nil,
},
}

func TestEnsureDefaultComponents(t *testing.T) {
assert := assert.New(t)

for _, test := range ensureDefaultComponentsTests {
updatedQuay, err := EnsureDefaultComponents(&test.quay)

assert.Nil(err, test.name)
assert.Equal(len(test.expected), len(updatedQuay.Spec.Components), test.name)

for _, expectedComponent := range test.expected {
assert.Contains(updatedQuay.Spec.Components, expectedComponent, test.name)
}
}
}

var componentsMatchTests = []struct {
name string
firstComponents []Component
secondComponents []Component
expected bool
}{
{
"EmptyDoNotMatch",
[]Component{},
[]Component{
{Kind: "postgres", Managed: false},
{Kind: "redis", Managed: false},
{Kind: "storage", Managed: false},
},
false,
},
{
"EmptyMatch",
[]Component{},
[]Component{},
true,
},
{
"Match",
[]Component{
{Kind: "postgres", Managed: false},
{Kind: "redis", Managed: false},
{Kind: "storage", Managed: true},
},
[]Component{
{Kind: "postgres", Managed: false},
{Kind: "redis", Managed: false},
{Kind: "storage", Managed: true},
},
true,
},
{
"DoNotMatch",
[]Component{
{Kind: "postgres", Managed: false},
{Kind: "redis", Managed: false},
{Kind: "storage", Managed: true},
},
[]Component{
{Kind: "postgres", Managed: false},
{Kind: "redis", Managed: false},
{Kind: "storage", Managed: false},
},
false,
},
}

func TestComponentsMatch(t *testing.T) {
assert := assert.New(t)

for _, test := range componentsMatchTests {
match := ComponentsMatch(test.firstComponents, test.secondComponents)

assert.Equal(test.expected, match, test.name)
}
}
14 changes: 7 additions & 7 deletions api/v1/zz_generated.deepcopy.go

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

27 changes: 19 additions & 8 deletions config/crd/bases/quay.redhat.com_quayregistries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,31 @@ spec:
spec:
description: QuayRegistrySpec defines the desired state of QuayRegistry.
properties:
configBundleSecret:
description: ConfigBundleSecret is the name of the Kubernetes `Secret`
in the same namespace which contains the base Quay config and extra
certs.
type: string
managedComponents:
description: ManagedComponents declare which supplemental services should
be included in this Quay deployment.
components:
description: Components declare how the Operator should handle backing
Quay services.
items:
description: Component describes how the Operator should handle a
backing Quay service.
properties:
kind:
description: Kind is the unique name of this type of component.
type: string
managed:
description: Managed indicates whether or not the Operator is
responsible for the lifecycle of this component. Default is
true.
type: boolean
required:
- kind
- managed
type: object
type: array
configBundleSecret:
description: ConfigBundleSecret is the name of the Kubernetes `Secret`
in the same namespace which contains the base Quay config and extra
certs.
type: string
type: object
status:
description: QuayRegistryStatus defines the observed state of QuayRegistry.
Expand Down
6 changes: 6 additions & 0 deletions config/samples/managed.quayregistry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: quay.redhat.com/v1
kind: QuayRegistry
metadata:
name: skynet
spec:
configBundleSecret: quay-config-bundle-abc123
11 changes: 0 additions & 11 deletions config/samples/quay.redhat.com_v1_quayregistry.yaml

This file was deleted.

15 changes: 15 additions & 0 deletions config/samples/unmanaged.quayregistry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: quay.redhat.com/v1
kind: QuayRegistry
metadata:
name: unmanaged
spec:
configBundleSecret: quay-config-bundle-abc123
components:
- kind: postgres
managed: false
- kind: redis
managed: false
- kind: clair
managed: false
- kind: storage
managed: false
16 changes: 15 additions & 1 deletion controllers/quayregistry_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,21 @@ func (r *QuayRegistryReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error

log.Info("successfully retrieved referenced `configBundleSecret`", "configBundleSecret", configBundle.GetName(), "resourceVersion", configBundle.GetResourceVersion())

deploymentObjects, err := kustomize.Inflate(&quay, &configBundle, &secretKeysBundle, log)
updatedQuay, err := v1.EnsureDefaultComponents(&quay)
if err != nil {
log.Error(err, "could not ensure default `spec.components`")
return ctrl.Result{}, err
}
if !v1.ComponentsMatch(quay.Spec.Components, updatedQuay.Spec.Components) {
log.Info("updating QuayRegistry `spec.components` to include defaults")
if err = r.Client.Update(ctx, updatedQuay); err != nil {
log.Error(err, "failed to update `spec.components` to include defaults")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}

deploymentObjects, err := kustomize.Inflate(updatedQuay, &configBundle, &secretKeysBundle, log)
if err != nil {
log.Error(err, "could not inflate QuayRegistry into Kubernetes objects")
return ctrl.Result{}, err
Expand Down
15 changes: 10 additions & 5 deletions controllers/quayregistry_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ func newQuayRegistry(name, namespace string) v1.QuayRegistry {
Namespace: namespace,
},
Spec: v1.QuayRegistrySpec{
ManagedComponents: []v1.ManagedComponent{
{Kind: "postgres"},
{Kind: "clair"},
{Kind: "redis"},
{Kind: "storage"},
Components: []v1.Component{
// FIXME(alecmerdler): Test omitting components and marking some as disabled/unmanaged...
{Kind: "postgres", Managed: true},
{Kind: "clair", Managed: true},
{Kind: "redis", Managed: true},
{Kind: "storage", Managed: true},
},
},
}
Expand Down Expand Up @@ -173,6 +174,10 @@ var _ = Describe("QuayRegistryReconciler", func() {
verifyOwnerRefs(persistentVolumeClaim.GetOwnerReferences())
}
})

When("the `components` field is empty", func() {
// TODO(alecmerdler)
})
})
})

Expand Down
Loading