Skip to content

Commit

Permalink
Merge pull request #280 from alecmerdler/components
Browse files Browse the repository at this point in the history
Components API
  • Loading branch information
alecmerdler authored Aug 17, 2020
2 parents 66cb4c8 + 509c5d0 commit 23c673f
Show file tree
Hide file tree
Showing 15 changed files with 407 additions and 100 deletions.
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

0 comments on commit 23c673f

Please sign in to comment.