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

release radix-operator #1031

Merged
merged 3 commits into from
Jan 5, 2024
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
4 changes: 2 additions & 2 deletions charts/radix-operator/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v2
name: radix-operator
version: 1.27.6
appVersion: 1.47.6
version: 1.28.1
appVersion: 1.48.1
kubeVersion: ">=1.24.0"
description: Radix Operator
keywords:
Expand Down
2 changes: 0 additions & 2 deletions charts/radix-operator/templates/radixapplication.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,6 @@ spec:
- name
- port
type: object
minItems: 1
type: array
x-kubernetes-list-map-keys:
- name
Expand Down Expand Up @@ -1073,7 +1072,6 @@ spec:
type: object
required:
- name
- ports
type: object
type: array
x-kubernetes-list-map-keys:
Expand Down
4 changes: 1 addition & 3 deletions json-schema/radixapplication.json
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,6 @@
],
"type": "object"
},
"minItems": 1,
"type": "array",
"x-kubernetes-list-map-keys": [
"name"
Expand Down Expand Up @@ -1076,8 +1075,7 @@
}
},
"required": [
"name",
"ports"
"name"
],
"type": "object"
},
Expand Down
105 changes: 1 addition & 104 deletions pkg/apis/applicationconfig/applicationconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package applicationconfig

import (
"context"
"encoding/json"
stderrors "errors"
"fmt"
"reflect"
"strings"
Expand All @@ -15,11 +13,9 @@ import (
"github.com/equinor/radix-operator/pkg/apis/utils"
"github.com/equinor/radix-operator/pkg/apis/utils/branch"
radixclient "github.com/equinor/radix-operator/pkg/client/clientset/versioned"
radixTypes "github.com/equinor/radix-operator/pkg/client/clientset/versioned/typed/radix/v1"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/client-go/kubernetes"
)

Expand Down Expand Up @@ -126,7 +122,7 @@ func (app *ApplicationConfig) ApplyConfigToApplicationNamespace() error {
// It compares the actual state with the desired, and attempts to
// converge the two
func (app *ApplicationConfig) OnSync() error {
if err := app.createEnvironments(); err != nil {
if err := app.syncEnvironments(); err != nil {
log.Errorf("Failed to create namespaces for app environments %s. %v", app.config.Name, err)
return err
}
Expand All @@ -144,102 +140,3 @@ func (app *ApplicationConfig) OnSync() error {
}
return app.syncSubPipelineServiceAccounts()
}

func (app *ApplicationConfig) createEnvironments() error {
var errs []error
for _, env := range app.config.Spec.Environments {
err := app.applyEnvironment(utils.NewEnvironmentBuilder().
WithAppName(app.config.Name).
WithAppLabel().
WithEnvironmentName(env.Name).
WithRegistrationOwner(app.registration).
WithEgressConfig(env.Egress).
// Orphaned flag will be set by the environment handler but until
// reconciliation we must ensure it is false
// Update: It seems Update method does not update status object when using real k8s client, but the fake client does.
// Only an explicit call to UpdateStatus can update status object, and this is only done by the RadixEnvironment controller.
WithOrphaned(false).
BuildRE())
if err != nil {
errs = append(errs, err)
}
}
return stderrors.Join(errs...)
}

// applyEnvironment creates an environment or applies changes if it exists
func (app *ApplicationConfig) applyEnvironment(newRe *radixv1.RadixEnvironment) error {
logger := log.WithFields(log.Fields{"environment": newRe.ObjectMeta.Name})
logger.Debugf("Apply environment %s", newRe.Name)

repository := app.radixclient.RadixV1().RadixEnvironments()

// Get environment from cache, instead than for cluster
oldRe, err := app.kubeutil.GetEnvironment(newRe.Name)
if err != nil && errors.IsNotFound(err) {
// Environment does not exist yet

newRe, err = repository.Create(context.TODO(), newRe, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create RadixEnvironment object: %v", err)
}
logger.Debugf("Created RadixEnvironment: %s", newRe.Name)

} else if err != nil {
return fmt.Errorf("failed to get RadixEnvironment object: %v", err)

} else {
// Environment already exists

logger.Debugf("RadixEnvironment object %s already exists, updating the object now", oldRe.Name)
err = patchDifference(repository, oldRe, newRe, logger)
if err != nil {
return err
}
}
return nil
}

// patchDifference creates a mergepatch, comparing old and new RadixEnvironments and issues the patch to radix
func patchDifference(repository radixTypes.RadixEnvironmentInterface, oldRe *radixv1.RadixEnvironment, newRe *radixv1.RadixEnvironment, logger *log.Entry) error {
radixEnvironment := oldRe.DeepCopy()
radixEnvironment.ObjectMeta.Labels = newRe.ObjectMeta.Labels
radixEnvironment.ObjectMeta.OwnerReferences = newRe.ObjectMeta.OwnerReferences
radixEnvironment.ObjectMeta.Annotations = newRe.ObjectMeta.Annotations
radixEnvironment.ObjectMeta.Finalizers = newRe.ObjectMeta.Finalizers
radixEnvironment.ObjectMeta.DeletionTimestamp = newRe.ObjectMeta.DeletionTimestamp
radixEnvironment.Spec = newRe.Spec
radixEnvironment.Status = newRe.Status

oldReJSON, err := json.Marshal(oldRe)
if err != nil {
return fmt.Errorf("failed to marshal old RadixEnvironment object: %v", err)
}

radixEnvironmentJSON, err := json.Marshal(radixEnvironment)
if err != nil {
return fmt.Errorf("failed to marshal new RadixEnvironment object: %v", err)
}

patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldReJSON, radixEnvironmentJSON, radixv1.RadixEnvironment{})
if err != nil {
return fmt.Errorf("failed to create patch document for RadixEnvironment object: %v", err)
}

if !isEmptyPatch(patchBytes) {
// Will perform update as patching does not seem to work for this custom resource
patchedEnvironment, err := repository.Update(context.TODO(), radixEnvironment, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to patch RadixEnvironment object: %v", err)
}
logger.Debugf("Patched RadixEnvironment: %s", patchedEnvironment.Name)
} else {
logger.Debugf("No need to patch RadixEnvironment: %s ", newRe.Name)
}

return nil
}

func isEmptyPatch(patchBytes []byte) bool {
return string(patchBytes) == "{}"
}
7 changes: 5 additions & 2 deletions pkg/apis/applicationconfig/applicationconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
kubefake "k8s.io/client-go/kubernetes/fake"
secretproviderfake "sigs.k8s.io/secrets-store-csi-driver/pkg/client/clientset/versioned/fake"
Expand Down Expand Up @@ -87,7 +88,8 @@ func Test_Reconciles_Radix_Environments(t *testing.T) {
context.TODO(),
&radixv1.RadixEnvironment{
ObjectMeta: metav1.ObjectMeta{
Name: "any-app-qa",
Name: "any-app-qa",
Labels: labels.Set{kube.RadixAppLabel: "any-app"},
},
},
metav1.CreateOptions{})
Expand All @@ -97,7 +99,8 @@ func Test_Reconciles_Radix_Environments(t *testing.T) {
context.TODO(),
&radixv1.RadixEnvironment{
ObjectMeta: metav1.ObjectMeta{
Name: "any-app-prod",
Name: "any-app-prod",
Labels: labels.Set{kube.RadixAppLabel: "any-app"},
},
},
metav1.CreateOptions{})
Expand Down
84 changes: 84 additions & 0 deletions pkg/apis/applicationconfig/environment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package applicationconfig

import (
"context"
stderrors "errors"
"fmt"

radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
"github.com/equinor/radix-operator/pkg/apis/utils"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
)

func (app *ApplicationConfig) syncEnvironments() error {
var errs []error
for _, env := range app.config.Spec.Environments {
if err := app.syncEnvironment(app.buildRadixEnvironment(env)); err != nil {
errs = append(errs, err)
}
}
return stderrors.Join(errs...)
}

func (app *ApplicationConfig) buildRadixEnvironment(env radixv1.Environment) *radixv1.RadixEnvironment {
return utils.NewEnvironmentBuilder().
WithAppName(app.config.Name).
WithAppLabel().
WithEnvironmentName(env.Name).
WithRegistrationOwner(app.registration).
WithEgressConfig(env.Egress).
BuildRE()
}

// syncEnvironment creates an environment or applies changes if it exists
func (app *ApplicationConfig) syncEnvironment(radixEnvironment *radixv1.RadixEnvironment) error {
logger := log.WithFields(log.Fields{"environment": radixEnvironment.GetName()})
logger.Debugf("Apply RadixEnvironment")
if _, err := app.getRadixEnvironment(radixEnvironment.GetName()); err != nil {
if errors.IsNotFound(err) {
return app.createRadixEnvironment(radixEnvironment, logger)
}
return fmt.Errorf("failed to get RadixEnvironment: %v", err)
}
return app.updateRadixEnvironment(radixEnvironment, logger)
}

func (app *ApplicationConfig) createRadixEnvironment(radixEnvironment *radixv1.RadixEnvironment, logger *log.Entry) error {
created, err := app.radixclient.RadixV1().RadixEnvironments().Create(context.Background(), radixEnvironment, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create RadixEnvironment: %v", err)
}
logger.Debugf("Created RadixEnvironment (revision %s)", created.GetResourceVersion())
return nil
}

// updateRadixEnvironment updates a RadixEnvironment
func (app *ApplicationConfig) updateRadixEnvironment(radixEnvironment *radixv1.RadixEnvironment, logger *log.Entry) error {
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
existingRE, err := app.getRadixEnvironment(radixEnvironment.GetName())
if err != nil {
if errors.IsNotFound(err) {
return nil
}
return err
}
logger.Debugf("re-taken RadixEnvironment (revision %s)", existingRE.GetResourceVersion())

newRE := existingRE.DeepCopy()
newRE.Spec = radixEnvironment.Spec
// Will perform update as patching does not seem to work for this custom resource
updated, err := app.kubeutil.UpdateRadixEnvironment(newRE)
if err != nil {
return err
}
logger.Debugf("Updated RadixEnvironment (revision %s)", updated.GetResourceVersion())
return nil
})
}

func (app *ApplicationConfig) getRadixEnvironment(name string) (*radixv1.RadixEnvironment, error) {
return app.kubeutil.RadixClient().RadixV1().RadixEnvironments().Get(context.Background(), name, metav1.GetOptions{})
}
17 changes: 8 additions & 9 deletions pkg/apis/deployment/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) {
utils.NewDeployJobComponentBuilder().
WithName(jobName).
WithImage("job:latest").
WithPort("http", 3002).
WithPorts([]v1.ComponentPort{{Name: "http", Port: 3002}}).
WithEnvironmentVariable("a_variable", "a_value").
WithMonitoring(true).
WithResource(map[string]string{
Expand Down Expand Up @@ -482,7 +482,7 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) {
utils.NewDeployJobComponentBuilder().
WithName(jobName).
WithImage("job:latest").
WithPort("http", 3002).
WithPorts([]v1.ComponentPort{{Name: "http", Port: 3002}}).
WithEnvironmentVariable("a_variable", "a_value").
WithMonitoring(true).
WithResource(map[string]string{
Expand Down Expand Up @@ -510,7 +510,7 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) {
WithSecrets([]string{outdatedSecret, remainingSecret}).
WithAlwaysPullImageOnDeploy(false),
utils.NewDeployJobComponentBuilder().
WithName(jobName2),
WithName(jobName2).WithSchedulerPort(&schedulerPortCreate),
).
WithComponents()

Expand Down Expand Up @@ -586,7 +586,6 @@ func TestObjectSynced_MultiJob_ContainsAllElements(t *testing.T) {
services, _ := kubeclient.CoreV1().Services(envNamespace).List(context.TODO(), metav1.ListOptions{})
expectedServices := getServicesForRadixComponents(&services.Items)
var jobNames []string

if jobsExist {
jobNames = []string{jobName}
assert.Equal(t, 1, len(expectedServices), "Number of services wasn't as expected")
Expand Down Expand Up @@ -3691,7 +3690,7 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) {
WithEnvironment("dev").
WithJobComponents().
WithComponents(
utils.NewDeployComponentBuilder().WithName("job"),
utils.NewDeployComponentBuilder().WithName("job").WithPorts([]v1.ComponentPort{{Name: "http", Port: 8000}}),
)

testTheory(&theoryData{
Expand All @@ -3707,7 +3706,7 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) {
WithAppName("app").
WithEnvironment("dev").
WithJobComponents(
utils.NewDeployJobComponentBuilder().WithName("job"),
utils.NewDeployJobComponentBuilder().WithName("job").WithPorts([]v1.ComponentPort{{Name: "http", Port: 8000}}),
).
WithComponents()

Expand All @@ -3724,7 +3723,7 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) {
WithAppName("app").
WithEnvironment("dev").
WithJobComponents(
utils.NewDeployJobComponentBuilder().WithName("compute"),
utils.NewDeployJobComponentBuilder().WithName("compute").WithPorts([]v1.ComponentPort{{Name: "http", Port: 8000}}),
).
WithComponents()

Expand All @@ -3741,8 +3740,8 @@ func Test_JobScheduler_ObjectsGarbageCollected(t *testing.T) {
WithAppName("app").
WithEnvironment("prod").
WithJobComponents(
utils.NewDeployJobComponentBuilder().WithName("job"),
utils.NewDeployJobComponentBuilder().WithName("compute"),
utils.NewDeployJobComponentBuilder().WithName("job").WithPorts([]v1.ComponentPort{{Name: "http", Port: 8000}}),
utils.NewDeployJobComponentBuilder().WithName("compute").WithPorts([]v1.ComponentPort{{Name: "http", Port: 8000}}),
).
WithComponents()

Expand Down
13 changes: 13 additions & 0 deletions pkg/apis/deployment/radixcomponentname.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ func (t RadixComponentName) GetCommonDeployComponent(rd *v1.RadixDeployment) v1.
return nil
}

// CommonDeployComponentHasPorts Checks id the deploy component has regular or schedule ports
func (t RadixComponentName) CommonDeployComponentHasPorts(rd *v1.RadixDeployment) bool {
if comp := t.findInDeploymentSpecComponentList(rd); comp != nil {
return len(comp.GetPorts()) > 0
}
for _, job := range rd.Spec.Jobs {
if strings.EqualFold(job.Name, string(t)) {
return len(job.GetPorts()) > 0 || job.SchedulerPort != nil
}
}
return false
}

// ExistInDeploymentSpecComponentList checks if RadixDeployment has any component with this name
func (t RadixComponentName) ExistInDeploymentSpecComponentList(rd *v1.RadixDeployment) bool {
return t.findInDeploymentSpecComponentList(rd) != nil
Expand Down
Loading