From abd6270e5e1379b5a5f69e28a3c0e91c7c256185 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Wed, 13 Mar 2024 17:21:25 +0300 Subject: [PATCH 01/49] chore(dependencies): add platform server as dependency --- go.mod | 4 +++- go.sum | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3c9340e7..bd37821c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/robolaunch/robot-operator -go 1.21 +go 1.21.1 require ( github.com/go-logr/logr v1.2.3 @@ -8,6 +8,7 @@ require ( github.com/onsi/ginkgo/v2 v2.6.0 github.com/onsi/gomega v1.24.1 github.com/robolaunch/cosmodrome v0.1.0-alpha.12 + github.com/robolaunch/platform/server v0.0.0-20240313131851-88d97e7d270b gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.1 k8s.io/apimachinery v0.26.1 @@ -36,6 +37,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect diff --git a/go.sum b/go.sum index 56e6b39a..58ac6788 100644 --- a/go.sum +++ b/go.sum @@ -169,6 +169,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -256,6 +258,8 @@ github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5 github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/robolaunch/cosmodrome v0.1.0-alpha.12 h1:1ezr5n+7cB/YLv425rc6kkS0iU2ykU9TDNEPQcs8yeQ= github.com/robolaunch/cosmodrome v0.1.0-alpha.12/go.mod h1:TMDMX9Eir84mHX74QqY+qV0E1NHAnXy67yvIPMvn8ko= +github.com/robolaunch/platform/server v0.0.0-20240313131851-88d97e7d270b h1:Juw9f0glukHPbPwnCsOJRwyRZbvfAQFC0SfyAgBYWHA= +github.com/robolaunch/platform/server v0.0.0-20240313131851-88d97e7d270b/go.mod h1:SN3BW8ejEhBHDMmfC1Q20mDltK4l0s88z3S9pVVarZk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= From 94b2284135938a1bbd572e92b4a0dd373ff5575b Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Wed, 13 Mar 2024 17:21:47 +0300 Subject: [PATCH 02/49] refactor(platform): query platform versions and environments --- internal/platform/environment.go | 50 ++++++++++++++++++++++++++++++++ internal/platform/platform.go | 27 +++++++++++++++++ internal/shared.go | 7 +++++ 3 files changed, 84 insertions(+) create mode 100644 internal/platform/environment.go create mode 100644 internal/platform/platform.go diff --git a/internal/platform/environment.go b/internal/platform/environment.go new file mode 100644 index 00000000..29c52098 --- /dev/null +++ b/internal/platform/environment.go @@ -0,0 +1,50 @@ +package platform + +import ( + "errors" + "net/url" + + platformServer "github.com/robolaunch/platform/server/pkg" + platformArtifact "github.com/robolaunch/platform/server/pkg/models" + "github.com/robolaunch/robot-operator/internal" +) + +func GetVersionedPlatformEnvironments(version string) (platformArtifact.VersionedPlatformImages, error) { + + environmentsResponse := platformServer.GetVersionedPlatformImagesResponse( + map[string]string{ + "version": version, + }, + url.Values{ + "url": []string{ + internal.DEFAULT_ENVIRONMENTS_URL, + }, + }, + ) + + if !environmentsResponse.Success { + return platformArtifact.VersionedPlatformImages{}, errors.New(environmentsResponse.Message) + } + + return platformArtifact.VersionedPlatformImages{}, nil +} + +func GetVersionedPlatformToolEnvironments(version string) (platformArtifact.VersionedPlatformImages, error) { + + environmentsResponse := platformServer.GetVersionedPlatformImagesResponse( + map[string]string{ + "version": version, + }, + url.Values{ + "url": []string{ + internal.DEFAULT_TOOLS_ENVIRONMENTS_URL, + }, + }, + ) + + if !environmentsResponse.Success { + return platformArtifact.VersionedPlatformImages{}, errors.New(environmentsResponse.Message) + } + + return *environmentsResponse.Response, nil +} diff --git a/internal/platform/platform.go b/internal/platform/platform.go new file mode 100644 index 00000000..4f696ebc --- /dev/null +++ b/internal/platform/platform.go @@ -0,0 +1,27 @@ +package platform + +import ( + "errors" + "net/url" + + platformServer "github.com/robolaunch/platform/server/pkg" + platformArtifact "github.com/robolaunch/platform/server/pkg/models" + "github.com/robolaunch/robot-operator/internal" +) + +func GetPlatform() (platformArtifact.Robolaunch, error) { + + platformResponse := platformServer.GetPlatformResponse( + url.Values{ + "url": []string{ + internal.DEFAULT_PLATFORM_URL, + }, + }, + ) + + if !platformResponse.Success { + return platformArtifact.Robolaunch{}, errors.New(platformResponse.Message) + } + + return *platformResponse.Response, nil +} diff --git a/internal/shared.go b/internal/shared.go index a12dcd7c..c075e499 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -54,6 +54,13 @@ const ( ROBOT_DEV_SUITE_OWNED = "robolaunch.io/dev-suite-owned" ) +// Platform server +const ( + DEFAULT_PLATFORM_URL = "https://raw.githubusercontent.com/robolaunch/platform/main/platforms/industry-cloud-platform/platform.yaml" + DEFAULT_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/robolaunch/platform/main/platforms/industry-cloud-platform/environments.yaml" + DEFAULT_TOOLS_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/robolaunch/platform/main/platforms/internal/environments.yaml" +) + // Robot owned resources' postfixes (v1alpha1) const ( PVC_VAR_POSTFIX = "-pvc-var" From 500c041145cb65eb0118c738ae10cfa63633e896 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 12:41:21 +0300 Subject: [PATCH 03/49] refactor(platform-meta): add platform meta getter --- internal/label/instance.go | 26 ++++++++++ internal/label/platform.go | 26 ++++++++++ internal/label/tenancy.go | 49 ------------------- internal/label/timezone.go | 27 ++++++++++ internal/node/image.go | 21 +++++--- internal/node/node.go | 8 --- internal/platform/image.go | 1 + internal/resources/v1alpha2/ros2_bridge.go | 9 ++-- internal/shared.go | 4 +- pkg/api/roboscale.io/v1alpha1/shared_types.go | 6 ++- 10 files changed, 104 insertions(+), 73 deletions(-) create mode 100644 internal/label/instance.go create mode 100644 internal/label/platform.go create mode 100644 internal/label/timezone.go create mode 100644 internal/platform/image.go diff --git a/internal/label/instance.go b/internal/label/instance.go new file mode 100644 index 00000000..30b11833 --- /dev/null +++ b/internal/label/instance.go @@ -0,0 +1,26 @@ +package label + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +type InstanceType string + +const ( + InstanceTypeCloudInstance InstanceType = "CloudInstance" + InstanceTypePhysicalInstance InstanceType = "PhysicalInstance" +) + +func GetInstanceType(obj metav1.Object) InstanceType { + tenancy := GetTenancy(obj) + if tenancy.PhysicalInstance == "" { + return InstanceTypeCloudInstance + } + return InstanceTypePhysicalInstance +} + +func GetClusterName(obj metav1.Object) string { + tenancy := GetTenancy(obj) + if tenancy.PhysicalInstance == "" { + return tenancy.CloudInstance + } + return tenancy.PhysicalInstance +} diff --git a/internal/label/platform.go b/internal/label/platform.go new file mode 100644 index 00000000..577441cc --- /dev/null +++ b/internal/label/platform.go @@ -0,0 +1,26 @@ +package label + +import ( + "github.com/robolaunch/robot-operator/internal" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type PlatformMeta struct { + Version string + Domain string +} + +func GetPlatformMeta(obj metav1.Object) *PlatformMeta { + platformMeta := &PlatformMeta{} + labels := obj.GetLabels() + + if version, ok := labels[internal.PLATFORM_VERSION_LABEL_KEY]; ok { + platformMeta.Version = version + } + + if domain, ok := labels[internal.DOMAIN_LABEL_KEY]; ok { + platformMeta.Domain = domain + } + + return platformMeta +} diff --git a/internal/label/tenancy.go b/internal/label/tenancy.go index 257796ae..88a84ada 100644 --- a/internal/label/tenancy.go +++ b/internal/label/tenancy.go @@ -12,12 +12,6 @@ type Tenancy struct { CloudInstance string CloudInstanceAlias string PhysicalInstance string - Domain string -} - -type Timezone struct { - Continent string - City string } func GetTenancy(obj metav1.Object) *Tenancy { @@ -48,29 +42,9 @@ func GetTenancy(obj metav1.Object) *Tenancy { tenancy.PhysicalInstance = physicalInstance } - if domain, ok := labels[internal.DOMAIN_LABEL_KEY]; ok { - tenancy.Domain = domain - } - return tenancy } -func GetTimezone(obj metav1.Object) *Timezone { - - timezone := &Timezone{} - labels := obj.GetLabels() - - if continent, ok := labels[internal.TZ_CONTINENT_LABEL_KEY]; ok { - timezone.Continent = continent - } - - if city, ok := labels[internal.TZ_CITY_LABEL_KEY]; ok { - timezone.City = city - } - - return timezone -} - func GetTenancyMap(obj metav1.Object) map[string]string { labels := obj.GetLabels() tenancyMap := make(map[string]string) @@ -131,26 +105,3 @@ func GetTenancyMapFromTenancy(tenancy Tenancy) map[string]string { return tenancyMap } - -type InstanceType string - -const ( - InstanceTypeCloudInstance InstanceType = "CloudInstance" - InstanceTypePhysicalInstance InstanceType = "PhysicalInstance" -) - -func GetInstanceType(obj metav1.Object) InstanceType { - tenancy := GetTenancy(obj) - if tenancy.PhysicalInstance == "" { - return InstanceTypeCloudInstance - } - return InstanceTypePhysicalInstance -} - -func GetClusterName(obj metav1.Object) string { - tenancy := GetTenancy(obj) - if tenancy.PhysicalInstance == "" { - return tenancy.CloudInstance - } - return tenancy.PhysicalInstance -} diff --git a/internal/label/timezone.go b/internal/label/timezone.go new file mode 100644 index 00000000..ec514b01 --- /dev/null +++ b/internal/label/timezone.go @@ -0,0 +1,27 @@ +package label + +import ( + "github.com/robolaunch/robot-operator/internal" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Timezone struct { + Continent string + City string +} + +func GetTimezone(obj metav1.Object) *Timezone { + + timezone := &Timezone{} + labels := obj.GetLabels() + + if continent, ok := labels[internal.TZ_CONTINENT_LABEL_KEY]; ok { + timezone.Continent = continent + } + + if city, ok := labels[internal.TZ_CITY_LABEL_KEY]; ok { + timezone.City = city + } + + return timezone +} diff --git a/internal/node/image.go b/internal/node/image.go index 1afaef43..232cf214 100644 --- a/internal/node/image.go +++ b/internal/node/image.go @@ -9,6 +9,7 @@ import ( cosmodrome "github.com/robolaunch/cosmodrome/pkg/api" "github.com/robolaunch/robot-operator/internal" + "github.com/robolaunch/robot-operator/internal/label" robotv1alpha1 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha1" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" corev1 "k8s.io/api/core/v1" @@ -95,8 +96,9 @@ func GetImageForRobot(ctx context.Context, r client.Client, node corev1.Node, ro } else { - platformVersion := GetPlatformVersion(node) - imageProps, err := getImagePropsForRobot(ctx, r, platformVersion, getDistroStr(robot.Spec.RobotConfig.Distributions)) + platformMeta := label.GetPlatformMeta(&node) + + imageProps, err := getImagePropsForRobot(ctx, r, platformMeta.Version, getDistroStr(robot.Spec.RobotConfig.Distributions)) if err != nil { return "", err } @@ -125,8 +127,9 @@ func GetImageForBridge(ctx context.Context, r client.Client, node corev1.Node, r var imageBuilder strings.Builder var tagBuilder strings.Builder - platformVersion := GetPlatformVersion(node) - imageProps, err := getImagePropsForRobot(ctx, r, platformVersion, getDistroStr(robot.Spec.RobotConfig.Distributions)) + platformMeta := label.GetPlatformMeta(&node) + + imageProps, err := getImagePropsForRobot(ctx, r, platformMeta.Version, getDistroStr(robot.Spec.RobotConfig.Distributions)) if err != nil { return "", err } @@ -151,8 +154,9 @@ func GetBridgeImage(ctx context.Context, r client.Client, node corev1.Node, ros2 var imageBuilder strings.Builder var tagBuilder strings.Builder - platformVersion := GetPlatformVersion(node) - imageProps, err := getImagePropsForRobot(ctx, r, platformVersion, getDistroStr([]robotv1alpha1.ROSDistro{ros2Bridge.Spec.Distro})) + platformMeta := label.GetPlatformMeta(&node) + + imageProps, err := getImagePropsForRobot(ctx, r, platformMeta.Version, getDistroStr([]robotv1alpha1.ROSDistro{ros2Bridge.Spec.Distro})) if err != nil { return "", err } @@ -185,8 +189,9 @@ func GetImageForEnvironment(ctx context.Context, r client.Client, node corev1.No } else { - platformVersion := GetPlatformVersion(node) - imageProps, err := getImagePropsForEnvironment(ctx, r, platformVersion) + platformMeta := label.GetPlatformMeta(&node) + + imageProps, err := getImagePropsForEnvironment(ctx, r, platformMeta.Version) if err != nil { return "", err } diff --git a/internal/node/node.go b/internal/node/node.go index 1e6011ec..b176654a 100644 --- a/internal/node/node.go +++ b/internal/node/node.go @@ -1,7 +1,6 @@ package node import ( - "github.com/robolaunch/robot-operator/internal" corev1 "k8s.io/api/core/v1" ) @@ -16,10 +15,3 @@ func IsK3s(node corev1.Node) bool { } return false } - -func GetPlatformVersion(node corev1.Node) string { - if platformVersion, ok := node.Labels[internal.PLATFORM_VERSION_LABEL_KEY]; ok { - return platformVersion - } - return "" -} diff --git a/internal/platform/image.go b/internal/platform/image.go new file mode 100644 index 00000000..0d3b65ce --- /dev/null +++ b/internal/platform/image.go @@ -0,0 +1 @@ +package platform diff --git a/internal/resources/v1alpha2/ros2_bridge.go b/internal/resources/v1alpha2/ros2_bridge.go index 1da4807d..278229ab 100644 --- a/internal/resources/v1alpha2/ros2_bridge.go +++ b/internal/resources/v1alpha2/ros2_bridge.go @@ -97,10 +97,11 @@ func GetROS2BridgeService(rosbridge *robotv1alpha2.ROS2Bridge, svcNamespacedName func GetROS2BridgeIngress(ros2Bridge *robotv1alpha2.ROS2Bridge, ingressNamespacedName *types.NamespacedName) *networkingv1.Ingress { tenancy := label.GetTenancy(ros2Bridge) + platformMeta := label.GetPlatformMeta(ros2Bridge) annotations := map[string]string{ - internal.INGRESS_AUTH_URL_KEY: fmt.Sprintf(internal.INGRESS_AUTH_URL_VAL, tenancy.Team, tenancy.Domain), - internal.INGRESS_AUTH_SIGNIN_KEY: fmt.Sprintf(internal.INGRESS_AUTH_SIGNIN_VAL, tenancy.Team, tenancy.Domain), + internal.INGRESS_AUTH_URL_KEY: fmt.Sprintf(internal.INGRESS_AUTH_URL_VAL, tenancy.Team, platformMeta.Domain), + internal.INGRESS_AUTH_SIGNIN_KEY: fmt.Sprintf(internal.INGRESS_AUTH_SIGNIN_VAL, tenancy.Team, platformMeta.Domain), internal.INGRESS_AUTH_RESPONSE_HEADERS_KEY: internal.INGRESS_AUTH_RESPONSE_HEADERS_VAL, internal.INGRESS_CONFIGURATION_SNIPPET_KEY: internal.INGRESS_IDE_CONFIGURATION_SNIPPET_VAL, internal.INGRESS_CERT_MANAGER_KEY: internal.INGRESS_CERT_MANAGER_VAL, @@ -116,14 +117,14 @@ func GetROS2BridgeIngress(ros2Bridge *robotv1alpha2.ROS2Bridge, ingressNamespace TLS: []networkingv1.IngressTLS{ { Hosts: []string{ - tenancy.Team + "." + tenancy.Domain, + tenancy.Team + "." + platformMeta.Domain, }, SecretName: ros2Bridge.Spec.TLSSecretName, }, }, Rules: []networkingv1.IngressRule{ { - Host: tenancy.Team + "." + tenancy.Domain, + Host: tenancy.Team + "." + platformMeta.Domain, IngressRuleValue: networkingv1.IngressRuleValue{ HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ diff --git a/internal/shared.go b/internal/shared.go index c075e499..fac379fa 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -2,9 +2,10 @@ package internal import corev1 "k8s.io/api/core/v1" -// Platform related labels +// Platform meta labels const ( PLATFORM_VERSION_LABEL_KEY = "robolaunch.io/platform" + DOMAIN_LABEL_KEY = "robolaunch.io/domain" ) // Tenancy labels @@ -15,7 +16,6 @@ const ( CLOUD_INSTANCE_LABEL_KEY = "robolaunch.io/cloud-instance" CLOUD_INSTANCE_ALIAS_LABEL_KEY = "robolaunch.io/cloud-instance-alias" PHYSICAL_INSTANCE_LABEL_KEY = "robolaunch.io/physical-instance" - DOMAIN_LABEL_KEY = "robolaunch.io/domain" ) // Selector labels diff --git a/pkg/api/roboscale.io/v1alpha1/shared_types.go b/pkg/api/roboscale.io/v1alpha1/shared_types.go index 3716a6ea..1831d0fd 100644 --- a/pkg/api/roboscale.io/v1alpha1/shared_types.go +++ b/pkg/api/roboscale.io/v1alpha1/shared_types.go @@ -142,7 +142,8 @@ func GetRelayServerServicePath(rs RelayServer, postfix string) string { func GetServiceDNS(obj metav1.Object, prefix, postfix string) string { tenancy := label.GetTenancy(obj) - connectionStr := tenancy.Team + "." + tenancy.Domain + GetServicePath(obj, postfix) + platformMeta := label.GetPlatformMeta(obj) + connectionStr := tenancy.Team + "." + platformMeta.Domain + GetServicePath(obj, postfix) if prefix != "" { connectionStr = prefix + connectionStr @@ -167,7 +168,8 @@ func GetServicePath(obj metav1.Object, postfix string) string { func GetServiceDNSWithNodePort(obj metav1.Object, prefix, port string) string { tenancy := label.GetTenancy(obj) - connectionStr := tenancy.Team + "." + tenancy.Domain + ":" + port + platformMeta := label.GetPlatformMeta(obj) + connectionStr := tenancy.Team + "." + platformMeta.Domain + ":" + port if prefix != "" { connectionStr = prefix + connectionStr From b87ff431d170e6574f4bce167046592308747ef2 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 13:52:34 +0300 Subject: [PATCH 04/49] refactor(image): derive tools images --- internal/platform/image.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/internal/platform/image.go b/internal/platform/image.go index 0d3b65ce..a14e6fd4 100644 --- a/internal/platform/image.go +++ b/internal/platform/image.go @@ -1 +1,36 @@ package platform + +import ( + "errors" + "reflect" + + "github.com/robolaunch/platform/server/pkg/models" +) + +func GetToolsImage(platformVersion string, application string, appVersion string) (string, error) { + + vpi, err := GetVersionedPlatformToolEnvironments(platformVersion) + if err != nil { + return "", err + } + + selectedEnvironment := models.Environment{} + + if domain, ok := vpi.Domains["tools"]; ok { + for _, environment := range domain.Environments { + if environment.Application.Name == application && environment.Application.Version == appVersion { + selectedEnvironment = environment + } + } + } else { + return "", errors.New("cannot find tools domain") + } + + if reflect.DeepEqual(selectedEnvironment, models.Environment{}) { + return "", errors.New("cannot find application '" + application + "' with version '" + appVersion + "'") + } + + imageName := vpi.Repository + "/" + vpi.Organization + "/" + application + ":" + selectedEnvironment.Application.Version + "-" + selectedEnvironment.Base.Version + + return imageName, nil +} From b0e0cce979ba62df782a712026bdf479fc33f64f Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 13:56:37 +0300 Subject: [PATCH 05/49] refactor(api): add field for code editor version --- pkg/api/roboscale.io/v1alpha2/toolkit_types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index aa5e5421..77c541b7 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -128,6 +128,10 @@ type CodeEditorContainer struct { // CodeEditorSpec defines the desired state of CodeEditor. type CodeEditorSpec struct { + // App version of the code editor. + // +kubebuilder:validation:Enum=4.22.0 + // +kubebuilder:default=4.22.0 + Version string `json:"version"` // If `true`, code editor will be consumed as `root` user. Root bool `json:"root,omitempty"` // If `true`, code editor will be consumed remotely. From bae5d13de28b01ac3f19b2a6015475eab98dd19d Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 14:16:49 +0300 Subject: [PATCH 06/49] refactor(image): get image using platform server --- internal/resources/v1alpha2/code_editor.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 06756a53..56f7d166 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -8,11 +8,14 @@ import ( "github.com/robolaunch/robot-operator/internal" configure "github.com/robolaunch/robot-operator/internal/configure/v1alpha2" + "github.com/robolaunch/robot-operator/internal/label" + "github.com/robolaunch/robot-operator/internal/platform" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" ) const ( CODE_EDITOR_PORT_NAME = "code-server" + CODE_EDITOR_APP_NAME = "code-editor" ) func GetCodeEditorPersistentVolumeClaim(codeEditor *robotv1alpha2.CodeEditor, pvcNamespacedName *types.NamespacedName, key int) *corev1.PersistentVolumeClaim { @@ -32,13 +35,20 @@ func GetCodeEditorPersistentVolumeClaim(codeEditor *robotv1alpha2.CodeEditor, pv func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNamespacedName *types.NamespacedName, node corev1.Node) *appsv1.Deployment { + platformMeta := label.GetPlatformMeta(&node) + cfg := configure.PodSpecConfigInjector{} + image, err := platform.GetToolsImage(platformMeta.Version, CODE_EDITOR_APP_NAME, codeEditor.Spec.Version) + if err != nil { + return nil + } + podSpec := corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "code-editor", - Image: "docker.io/robolaunchio/code-editor:4.22.0-0.2.6-alpha.19", + Name: CODE_EDITOR_APP_NAME, + Image: image, Command: []string{"/bin/bash", "-c", "sleep infinity"}, Ports: []corev1.ContainerPort{ { @@ -78,13 +88,13 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam Spec: appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ MatchLabels: map[string]string{ - internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: "code-editor", + internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: CODE_EDITOR_APP_NAME, }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ - internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: "code-editor", + internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: CODE_EDITOR_APP_NAME, }, }, Spec: podSpec, From 2ae97c10d01d5b65b4be6943a2ac8c91f71430d8 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 14:40:42 +0300 Subject: [PATCH 07/49] refactor(image): get image properties from labels --- internal/label/image.go | 37 +++++++++++++++++++ internal/node/image.go | 24 ++++++------ internal/shared.go | 10 ++--- .../roboscale.io/v1alpha1/robot_webhook.go | 4 +- .../roboscale.io/v1alpha2/toolkit_types.go | 4 +- 5 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 internal/label/image.go diff --git a/internal/label/image.go b/internal/label/image.go new file mode 100644 index 00000000..dfb00378 --- /dev/null +++ b/internal/label/image.go @@ -0,0 +1,37 @@ +package label + +import ( + "github.com/robolaunch/robot-operator/internal" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ImageMeta struct { + Registry string + User string + Repository string + Tag string +} + +func GetImageProperties(obj metav1.Object) *ImageMeta { + + imageMeta := &ImageMeta{} + labels := obj.GetLabels() + + if registry, ok := labels[internal.IMAGE_REGISTRY_LABEL_KEY]; ok { + imageMeta.Registry = registry + } + + if user, ok := labels[internal.IMAGE_USER_LABEL_KEY]; ok { + imageMeta.User = user + } + + if repository, ok := labels[internal.IMAGE_REPOSITORY_LABEL_KEY]; ok { + imageMeta.Repository = repository + } + + if tag, ok := labels[internal.IMAGE_TAG_LABEL_KEY]; ok { + imageMeta.Tag = tag + } + + return imageMeta +} diff --git a/internal/node/image.go b/internal/node/image.go index 232cf214..f19ecf99 100644 --- a/internal/node/image.go +++ b/internal/node/image.go @@ -57,10 +57,10 @@ type ReadyRobotProperties struct { func GetReadyRobotProperties(robot robotv1alpha1.Robot) ReadyRobotProperties { labels := robot.GetLabels() - if registry, hasRegistry := labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY]; hasRegistry { - if user, hasUser := labels[internal.ROBOT_IMAGE_USER_LABEL_KEY]; hasUser { - if repository, hasRepository := labels[internal.ROBOT_IMAGE_REPOSITORY_LABEL_KEY]; hasRepository { - if tag, hasTag := labels[internal.ROBOT_IMAGE_TAG_LABEL_KEY]; hasTag { + if registry, hasRegistry := labels[internal.IMAGE_REGISTRY_LABEL_KEY]; hasRegistry { + if user, hasUser := labels[internal.IMAGE_USER_LABEL_KEY]; hasUser { + if repository, hasRepository := labels[internal.IMAGE_REPOSITORY_LABEL_KEY]; hasRepository { + if tag, hasTag := labels[internal.IMAGE_TAG_LABEL_KEY]; hasTag { return ReadyRobotProperties{ Enabled: true, Image: registry + "/" + user + "/" + repository + ":" + tag, @@ -103,9 +103,9 @@ func GetImageForRobot(ctx context.Context, r client.Client, node corev1.Node, ro return "", err } - registry, hasRegistry := robot.Labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY] + registry, hasRegistry := robot.Labels[internal.IMAGE_REGISTRY_LABEL_KEY] if !hasRegistry { - return "", errors.New("registry is not found in label with key " + internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY) + return "", errors.New("registry is not found in label with key " + internal.IMAGE_REGISTRY_LABEL_KEY) } organization := "robolaunchio" @@ -134,9 +134,9 @@ func GetImageForBridge(ctx context.Context, r client.Client, node corev1.Node, r return "", err } - registry, hasRegistry := robot.Labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY] + registry, hasRegistry := robot.Labels[internal.IMAGE_REGISTRY_LABEL_KEY] if !hasRegistry { - return "", errors.New("registry is not found in label with key " + internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY) + return "", errors.New("registry is not found in label with key " + internal.IMAGE_REGISTRY_LABEL_KEY) } organization := "robolaunchio" @@ -161,9 +161,9 @@ func GetBridgeImage(ctx context.Context, r client.Client, node corev1.Node, ros2 return "", err } - registry, hasRegistry := ros2Bridge.Labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY] + registry, hasRegistry := ros2Bridge.Labels[internal.IMAGE_REGISTRY_LABEL_KEY] if !hasRegistry { - return "", errors.New("registry is not found in label with key " + internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY) + return "", errors.New("registry is not found in label with key " + internal.IMAGE_REGISTRY_LABEL_KEY) } organization := "robolaunchio" @@ -196,9 +196,9 @@ func GetImageForEnvironment(ctx context.Context, r client.Client, node corev1.No return "", err } - registry, hasRegistry := robot.Labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY] + registry, hasRegistry := robot.Labels[internal.IMAGE_REGISTRY_LABEL_KEY] if !hasRegistry { - return "", errors.New("registry is not found in label with key " + internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY) + return "", errors.New("registry is not found in label with key " + internal.IMAGE_REGISTRY_LABEL_KEY) } organization := imageProps.Organization diff --git a/internal/shared.go b/internal/shared.go index fac379fa..552fc2a1 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -30,12 +30,12 @@ const ( TZ_CITY_LABEL_KEY = "robolaunch.io/tz-city" ) -// Ready robot label +// Image labels const ( - ROBOT_IMAGE_REGISTRY_LABEL_KEY = "robolaunch.io/robot-image-registry" - ROBOT_IMAGE_USER_LABEL_KEY = "robolaunch.io/robot-image-user" - ROBOT_IMAGE_REPOSITORY_LABEL_KEY = "robolaunch.io/robot-image-repository" - ROBOT_IMAGE_TAG_LABEL_KEY = "robolaunch.io/robot-image-tag" + IMAGE_REGISTRY_LABEL_KEY = "robolaunch.io/robot-image-registry" + IMAGE_USER_LABEL_KEY = "robolaunch.io/robot-image-user" + IMAGE_REPOSITORY_LABEL_KEY = "robolaunch.io/robot-image-repository" + IMAGE_TAG_LABEL_KEY = "robolaunch.io/robot-image-tag" ) // Target resource labels diff --git a/pkg/api/roboscale.io/v1alpha1/robot_webhook.go b/pkg/api/roboscale.io/v1alpha1/robot_webhook.go index 4b86a0d7..2d8c021e 100644 --- a/pkg/api/roboscale.io/v1alpha1/robot_webhook.go +++ b/pkg/api/roboscale.io/v1alpha1/robot_webhook.go @@ -273,8 +273,8 @@ func (r *Robot) checkAdditionalConfigs() error { } func (r *Robot) setDefaultLabels() { - if _, ok := r.Labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY]; !ok { - r.Labels[internal.ROBOT_IMAGE_REGISTRY_LABEL_KEY] = "docker.io" + if _, ok := r.Labels[internal.IMAGE_REGISTRY_LABEL_KEY]; !ok { + r.Labels[internal.IMAGE_REGISTRY_LABEL_KEY] = "docker.io" } } diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index 77c541b7..66c80711 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -129,8 +129,8 @@ type CodeEditorContainer struct { // CodeEditorSpec defines the desired state of CodeEditor. type CodeEditorSpec struct { // App version of the code editor. - // +kubebuilder:validation:Enum=4.22.0 - // +kubebuilder:default=4.22.0 + // +kubebuilder:validation:Enum="4.22.0" + // +kubebuilder:default="4.22.0" Version string `json:"version"` // If `true`, code editor will be consumed as `root` user. Root bool `json:"root,omitempty"` From c066b334f2d8e598b40992646f10116d9ae0f599 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 14:45:05 +0300 Subject: [PATCH 08/49] refactor(image): use registry info in labels when selecting image --- internal/label/image.go | 2 +- internal/platform/image.go | 8 ++++++-- internal/resources/v1alpha2/code_editor.go | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/label/image.go b/internal/label/image.go index dfb00378..324e25b6 100644 --- a/internal/label/image.go +++ b/internal/label/image.go @@ -12,7 +12,7 @@ type ImageMeta struct { Tag string } -func GetImageProperties(obj metav1.Object) *ImageMeta { +func GetImageMeta(obj metav1.Object) *ImageMeta { imageMeta := &ImageMeta{} labels := obj.GetLabels() diff --git a/internal/platform/image.go b/internal/platform/image.go index a14e6fd4..547bc763 100644 --- a/internal/platform/image.go +++ b/internal/platform/image.go @@ -5,9 +5,13 @@ import ( "reflect" "github.com/robolaunch/platform/server/pkg/models" + "github.com/robolaunch/robot-operator/internal/label" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func GetToolsImage(platformVersion string, application string, appVersion string) (string, error) { +func GetToolsImage(obj metav1.Object, platformVersion string, application string, appVersion string) (string, error) { + + imageMeta := label.GetImageMeta(obj) vpi, err := GetVersionedPlatformToolEnvironments(platformVersion) if err != nil { @@ -30,7 +34,7 @@ func GetToolsImage(platformVersion string, application string, appVersion string return "", errors.New("cannot find application '" + application + "' with version '" + appVersion + "'") } - imageName := vpi.Repository + "/" + vpi.Organization + "/" + application + ":" + selectedEnvironment.Application.Version + "-" + selectedEnvironment.Base.Version + imageName := imageMeta.Registry + "/" + vpi.Organization + "/" + application + ":" + selectedEnvironment.Application.Version + "-" + selectedEnvironment.Base.Version return imageName, nil } diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 56f7d166..4f978c21 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -39,7 +39,7 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam cfg := configure.PodSpecConfigInjector{} - image, err := platform.GetToolsImage(platformMeta.Version, CODE_EDITOR_APP_NAME, codeEditor.Spec.Version) + image, err := platform.GetToolsImage(codeEditor, platformMeta.Version, CODE_EDITOR_APP_NAME, codeEditor.Spec.Version) if err != nil { return nil } From 5566213b88d8b9aec0936487520da66e1612273b Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:12:44 +0300 Subject: [PATCH 09/49] refactor(remote): add remote config injector --- internal/configure/v1alpha2/configure.go | 1 + internal/configure/v1alpha2/remote.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 internal/configure/v1alpha2/remote.go diff --git a/internal/configure/v1alpha2/configure.go b/internal/configure/v1alpha2/configure.go index 499baa90..f20c8dd3 100644 --- a/internal/configure/v1alpha2/configure.go +++ b/internal/configure/v1alpha2/configure.go @@ -1,3 +1,4 @@ package configure type PodSpecConfigInjector struct{} +type ServiceSpecConfigInjector struct{} diff --git a/internal/configure/v1alpha2/remote.go b/internal/configure/v1alpha2/remote.go new file mode 100644 index 00000000..6328a5fb --- /dev/null +++ b/internal/configure/v1alpha2/remote.go @@ -0,0 +1,22 @@ +package configure + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (cfg *PodSpecConfigInjector) InjectRemoteConfigurations(podSpec *corev1.PodSpec, obj metav1.Object) *corev1.PodSpec { + + podSpec.Hostname = obj.GetName() + podSpec.Subdomain = obj.GetName() + + return podSpec +} + +func (cfg *ServiceSpecConfigInjector) InjectRemoteConfigurations(serviceSpec *corev1.ServiceSpec) *corev1.ServiceSpec { + + serviceSpec.Type = "" + serviceSpec.ClusterIP = "None" + + return serviceSpec +} From 38e4b442f8f555b7a0886a45b5032ce4131acbb0 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:13:10 +0300 Subject: [PATCH 10/49] refactor(deployment): inject remote config if .spec.remote is true --- internal/resources/v1alpha2/code_editor.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 4f978c21..24bc8ee6 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -1,6 +1,8 @@ package v1alpha2_resources import ( + "strconv" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -10,12 +12,13 @@ import ( configure "github.com/robolaunch/robot-operator/internal/configure/v1alpha2" "github.com/robolaunch/robot-operator/internal/label" "github.com/robolaunch/robot-operator/internal/platform" + "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha1" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" ) const ( - CODE_EDITOR_PORT_NAME = "code-server" CODE_EDITOR_APP_NAME = "code-editor" + CODE_EDITOR_PORT_NAME = "code-server" ) func GetCodeEditorPersistentVolumeClaim(codeEditor *robotv1alpha2.CodeEditor, pvcNamespacedName *types.NamespacedName, key int) *corev1.PersistentVolumeClaim { @@ -50,6 +53,13 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam Name: CODE_EDITOR_APP_NAME, Image: image, Command: []string{"/bin/bash", "-c", "sleep infinity"}, + Env: []corev1.EnvVar{ + internal.Env("CODE_SERVER_PORT", strconv.FormatInt(int64(codeEditor.Spec.Port), 10)), + internal.Env("FILE_BROWSER_PORT", strconv.Itoa(internal.FILE_BROWSER_PORT)), + internal.Env("FILE_BROWSER_SERVICE", CODE_EDITOR_PORT_NAME), + internal.Env("FILE_BROWSER_BASE_URL", v1alpha1.GetServicePath(codeEditor, "/file-browser/ide")), + internal.Env("TERM", "xterm-256color"), + }, Ports: []corev1.ContainerPort{ { Name: CODE_EDITOR_PORT_NAME, @@ -79,6 +89,10 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam cfg.InjectLinuxUserAndGroup(&podSpec) } + if codeEditor.Spec.Remote { + cfg.InjectRemoteConfigurations(&podSpec, codeEditor) + } + deployment := appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: deploymentNamespacedName.Name, From ecc64745a9cdc57f2c5c73abf7b7f8ab4e995d10 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:27:37 +0300 Subject: [PATCH 11/49] refactor(api): add service type field for code editor --- pkg/api/roboscale.io/v1alpha2/toolkit_types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index 66c80711..4018ecf6 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -141,6 +141,10 @@ type CodeEditorSpec struct { // Port that code editor will use inside the container. // +kubebuilder:default=9000 Port int32 `json:"port"` + // Service type of CodeEditor. `ClusterIP` and `NodePort` is supported. + // +kubebuilder:validation:Enum=ClusterIP;NodePort + // +kubebuilder:default="ClusterIP" + ServiceType corev1.ServiceType `json:"serviceType,omitempty"` // Volume templates for ROS 2 workload. // For each volume template, operator will create a PersistentVolumeClaim // that can be mounted to the ROS 2 workload. From fa4941df0824e0ccabad2af3dc0b8a3c374852a4 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:34:44 +0300 Subject: [PATCH 12/49] refactor(service): define code editor service --- internal/resources/v1alpha2/code_editor.go | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 24bc8ee6..5e46a29f 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -7,6 +7,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" "github.com/robolaunch/robot-operator/internal" configure "github.com/robolaunch/robot-operator/internal/configure/v1alpha2" @@ -21,6 +22,12 @@ const ( CODE_EDITOR_PORT_NAME = "code-server" ) +func getCodeEditorSelector(codeEditor robotv1alpha2.CodeEditor) map[string]string { + return map[string]string{ + CODE_EDITOR_APP_NAME: codeEditor.GetName(), + } +} + func GetCodeEditorPersistentVolumeClaim(codeEditor *robotv1alpha2.CodeEditor, pvcNamespacedName *types.NamespacedName, key int) *corev1.PersistentVolumeClaim { pvc := corev1.PersistentVolumeClaim{ @@ -118,3 +125,45 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam return &deployment } + +func GetCodeEditorService(codeEditor *robotv1alpha2.CodeEditor, svcNamespacedName *types.NamespacedName) *corev1.Service { + + cfg := configure.ServiceSpecConfigInjector{} + + serviceSpec := corev1.ServiceSpec{ + Type: codeEditor.Spec.ServiceType, + Selector: getCodeEditorSelector(*codeEditor), + Ports: []corev1.ServicePort{ + { + Name: CODE_EDITOR_PORT_NAME, + Port: codeEditor.Spec.Port, + TargetPort: intstr.IntOrString{ + IntVal: codeEditor.Spec.Port, + }, + Protocol: corev1.ProtocolTCP, + }, + { + Name: internal.FILE_BROWSER_PORT_NAME, + Port: internal.FILE_BROWSER_PORT, + TargetPort: intstr.IntOrString{ + IntVal: internal.FILE_BROWSER_PORT, + }, + Protocol: corev1.ProtocolTCP, + }, + }, + } + + service := corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: svcNamespacedName.Name, + Namespace: svcNamespacedName.Namespace, + }, + Spec: serviceSpec, + } + + if codeEditor.Spec.Remote { + cfg.InjectRemoteConfigurations(&serviceSpec) + } + + return &service +} From fc03ec37589c182e76d976c4ea05d6c55f7ab881 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:40:43 +0300 Subject: [PATCH 13/49] refactor(api): add service status --- pkg/api/roboscale.io/v1alpha2/shared_types.go | 7 +++++++ pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go | 7 +++++++ pkg/api/roboscale.io/v1alpha2/toolkit_types.go | 2 ++ 3 files changed, 16 insertions(+) diff --git a/pkg/api/roboscale.io/v1alpha2/shared_types.go b/pkg/api/roboscale.io/v1alpha2/shared_types.go index f8984dae..1026f393 100644 --- a/pkg/api/roboscale.io/v1alpha2/shared_types.go +++ b/pkg/api/roboscale.io/v1alpha2/shared_types.go @@ -48,3 +48,10 @@ type OwnedDeploymentStatus struct { // Container statuses. ContainerStatuses []corev1.ContainerStatus `json:"containerStatuses,omitempty"` } + +type OwnedServiceStatus struct { + // Generic status for any owned resource. + Resource OwnedResourceStatus `json:"resource,omitempty"` + // Connection URL. + URLs map[string]string `json:"urls,omitempty"` +} diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go b/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go index 242f91e8..e941280c 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go @@ -49,3 +49,10 @@ func (codeEditor *CodeEditor) GetDeploymentMetadata() *types.NamespacedName { Namespace: codeEditor.Namespace, } } + +func (codeEditor *CodeEditor) GetServiceMetadata() *types.NamespacedName { + return &types.NamespacedName{ + Name: codeEditor.Name + internal.SERVICE_POSTFIX, + Namespace: codeEditor.Namespace, + } +} diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index 4018ecf6..d502a184 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -163,4 +163,6 @@ type CodeEditorStatus struct { ExternalVolumeStatuses []ExternalVolumeStatus `json:"externalVolumeStatuses,omitempty"` // Status of code editor deployment. DeploymentStatus OwnedDeploymentStatus `json:"deploymentStatus,omitempty"` + // Status of code editor service. + ServiceStatus OwnedServiceStatus `json:"serviceStatus,omitempty"` } From f9a3ba634898ebe084923f44143c57dd3bf3d9b6 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:45:48 +0300 Subject: [PATCH 14/49] refactor(service): create code editor service --- internal/shared.go | 1 + pkg/api/roboscale.io/v1alpha2/phases.go | 1 + .../v1alpha2/toolkit/code_editor/check.go | 19 ++++++++++++++++ .../code_editor/codeeditor_controller.go | 12 ++++++++++ .../v1alpha2/toolkit/code_editor/create.go | 20 +++++++++++++++++ .../v1alpha2/toolkit/code_editor/handle.go | 22 +++++++++++++++++++ 6 files changed, 75 insertions(+) diff --git a/internal/shared.go b/internal/shared.go index 552fc2a1..8f6bdaf6 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -82,6 +82,7 @@ const ( PVC_POSTFIX = "-pvc" STATEFULSET_POSTFIX = "" DEPLOYMENT_POSTFIX = "" + SERVICE_POSTFIX = "" ) // WorkspaceManager owned resources' postfixes diff --git a/pkg/api/roboscale.io/v1alpha2/phases.go b/pkg/api/roboscale.io/v1alpha2/phases.go index b23557a7..7784ed31 100644 --- a/pkg/api/roboscale.io/v1alpha2/phases.go +++ b/pkg/api/roboscale.io/v1alpha2/phases.go @@ -26,5 +26,6 @@ type CodeEditorPhase string const ( CodeEditorPhaseCreatingPVCs CodeEditorPhase = "CreatingPVCs" CodeEditorPhaseCreatingDeployment CodeEditorPhase = "CreatingDeployment" + CodeEditorPhaseCreatingService CodeEditorPhase = "CreatingService" CodeEditorPhaseReady CodeEditorPhase = "Ready" ) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 61c7049c..6a5be5e6 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -76,3 +76,22 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins return nil } + +func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + serviceQuery := &corev1.Service{} + err := r.Get(ctx, *instance.GetServiceMetadata(), serviceQuery) + if err != nil && errors.IsNotFound(err) { + instance.Status.ServiceStatus = robotv1alpha2.OwnedServiceStatus{} + } else if err != nil { + return err + } else { + + // make service dynamic + + instance.Status.ServiceStatus.Resource.Created = true + reference.SetReference(&instance.Status.ServiceStatus.Resource.Reference, serviceQuery.TypeMeta, serviceQuery.ObjectMeta) + } + + return nil +} diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index 3eb22eb6..89d660af 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -44,6 +44,7 @@ type CodeEditorReconciler struct { //+kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete var logger logr.Logger @@ -110,6 +111,11 @@ func (r *CodeEditorReconciler) reconcileCheckStatus(ctx context.Context, instanc return robotErr.CheckCreatingOrWaitingError(result, err) } + err = r.reconcileHandleService(ctx, instance) + if err != nil { + return robotErr.CheckCreatingOrWaitingError(result, err) + } + return nil } @@ -130,6 +136,11 @@ func (r *CodeEditorReconciler) reconcileCheckResources(ctx context.Context, inst return err } + err = r.reconcileCheckService(ctx, instance) + if err != nil { + return err + } + return nil } @@ -139,5 +150,6 @@ func (r *CodeEditorReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&robotv1alpha2.CodeEditor{}). Owns(&corev1.PersistentVolumeClaim{}). Owns(&appsv1.Deployment{}). + Owns(&corev1.Service{}). Complete(r) } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/create.go b/pkg/controllers/v1alpha2/toolkit/code_editor/create.go index 2992302e..1e4c27d7 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/create.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/create.go @@ -53,3 +53,23 @@ func (r *CodeEditorReconciler) createDeployment(ctx context.Context, instance *r logger.Info("STATUS: Deployment " + instance.GetDeploymentMetadata().Name + " is created.") return nil } + +func (r *CodeEditorReconciler) createService(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + service := v1alpha2_resources.GetCodeEditorService(instance, instance.GetServiceMetadata()) + + err := ctrl.SetControllerReference(instance, service, r.Scheme) + if err != nil { + return err + } + + err = r.Create(ctx, service) + if err != nil && errors.IsAlreadyExists(err) { + return nil + } else if err != nil { + return err + } + + logger.Info("STATUS: Service " + instance.GetServiceMetadata().Name + " is created.") + return nil +} diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go b/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go index 78cc7a98..e760af33 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go @@ -63,3 +63,25 @@ func (r *CodeEditorReconciler) reconcileHandleDeployment(ctx context.Context, in return nil } + +func (r *CodeEditorReconciler) reconcileHandleService(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + if !instance.Status.ServiceStatus.Resource.Created { + + instance.Status.Phase = robotv1alpha2.CodeEditorPhaseCreatingService + err := r.createService(ctx, instance) + if err != nil { + return err + } + + instance.Status.ServiceStatus.Resource.Created = true + + return &robotErr.CreatingResourceError{ + ResourceKind: "Service", + ResourceName: instance.GetServiceMetadata().Name, + ResourceNamespace: instance.GetServiceMetadata().Namespace, + } + } + + return nil +} From e476e8986ab82b6a577744dfb917f203db014314 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 17:59:48 +0300 Subject: [PATCH 15/49] refactor(selector): add code editor pod service a selector for service --- internal/resources/v1alpha2/code_editor.go | 7 +++++- .../v1alpha2/zz_generated.deepcopy.go | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 5e46a29f..ef130319 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -54,6 +54,11 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam return nil } + labels := getCodeEditorSelector(*codeEditor) + for k, v := range codeEditor.Labels { + labels[k] = v + } + podSpec := corev1.PodSpec{ Containers: []corev1.Container{ { @@ -104,7 +109,7 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam ObjectMeta: metav1.ObjectMeta{ Name: deploymentNamespacedName.Name, Namespace: deploymentNamespacedName.Namespace, - Labels: codeEditor.Labels, + Labels: labels, }, Spec: appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ diff --git a/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go b/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go index e663b3cc..04f02809 100644 --- a/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go @@ -154,6 +154,7 @@ func (in *CodeEditorStatus) DeepCopyInto(out *CodeEditorStatus) { copy(*out, *in) } in.DeploymentStatus.DeepCopyInto(&out.DeploymentStatus) + in.ServiceStatus.DeepCopyInto(&out.ServiceStatus) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CodeEditorStatus. @@ -259,6 +260,29 @@ func (in *OwnedResourceStatus) DeepCopy() *OwnedResourceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OwnedServiceStatus) DeepCopyInto(out *OwnedServiceStatus) { + *out = *in + out.Resource = in.Resource + if in.URLs != nil { + in, out := &in.URLs, &out.URLs + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnedServiceStatus. +func (in *OwnedServiceStatus) DeepCopy() *OwnedServiceStatus { + if in == nil { + return nil + } + out := new(OwnedServiceStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OwnedStatefulSetStatus) DeepCopyInto(out *OwnedStatefulSetStatus) { *out = *in From 123de8c1faa813abf5dd14e8229cf6b971f0b3f6 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Thu, 14 Mar 2024 18:00:19 +0300 Subject: [PATCH 16/49] build(manifests): :whale: update internal manifests --- .../bases/robot.roboscale.io_codeeditors.yaml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/config/crd/bases/robot.roboscale.io_codeeditors.yaml b/config/crd/bases/robot.roboscale.io_codeeditors.yaml index 6eed9a65..6598691b 100644 --- a/config/crd/bases/robot.roboscale.io_codeeditors.yaml +++ b/config/crd/bases/robot.roboscale.io_codeeditors.yaml @@ -1816,6 +1816,20 @@ spec: root: description: If `true`, code editor will be consumed as `root` user. type: boolean + serviceType: + default: ClusterIP + description: Service type of CodeEditor. `ClusterIP` and `NodePort` + is supported. + enum: + - ClusterIP + - NodePort + type: string + version: + default: 4.22.0 + description: App version of the code editor. + enum: + - 4.22.0 + type: string volumeClaimTemplates: description: Volume templates for ROS 2 workload. For each volume template, operator will create a PersistentVolumeClaim that can @@ -2048,6 +2062,7 @@ spec: type: array required: - port + - version type: object status: description: Most recently observed status of the CodeEditor. @@ -2515,6 +2530,65 @@ spec: type: object type: object type: array + serviceStatus: + description: Status of code editor service. + properties: + resource: + description: Generic status for any owned resource. + properties: + created: + description: Shows if the owned resource is created. + type: boolean + phase: + description: Phase of the owned resource. + type: string + reference: + description: Reference to the owned resource. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that + triggered the event) or if no container name is specified + "spec.containers[2]" (container with index 2 in this + pod). This syntax is chosen only to have some well-defined + way of referencing a part of an object. TODO: this design + is not final and this field is subject to change in + the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - created + type: object + urls: + additionalProperties: + type: string + description: Connection URL. + type: object + type: object type: object type: object served: true From e3aae1afefb0cb9d1adf6571e5825623caa79a8d Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Fri, 15 Mar 2024 16:04:37 +0300 Subject: [PATCH 17/49] refactor(selector): set pod & service selectors --- internal/resources/v1alpha2/code_editor.go | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index ef130319..91b30e8e 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -24,7 +24,7 @@ const ( func getCodeEditorSelector(codeEditor robotv1alpha2.CodeEditor) map[string]string { return map[string]string{ - CODE_EDITOR_APP_NAME: codeEditor.GetName(), + internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: codeEditor.Name, } } @@ -54,11 +54,6 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam return nil } - labels := getCodeEditorSelector(*codeEditor) - for k, v := range codeEditor.Labels { - labels[k] = v - } - podSpec := corev1.PodSpec{ Containers: []corev1.Container{ { @@ -109,19 +104,15 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam ObjectMeta: metav1.ObjectMeta{ Name: deploymentNamespacedName.Name, Namespace: deploymentNamespacedName.Namespace, - Labels: labels, + Labels: codeEditor.Labels, }, Spec: appsv1.DeploymentSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: CODE_EDITOR_APP_NAME, - }, + MatchLabels: getCodeEditorSelector(*codeEditor), }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: CODE_EDITOR_APP_NAME, - }, + Labels: getCodeEditorSelector(*codeEditor), }, Spec: podSpec, }, From b94b2a1e70b4e65524f41c67b5566fe5874530bd Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Fri, 15 Mar 2024 16:09:33 +0300 Subject: [PATCH 18/49] refactor(cmd): generate cmd for code editor --- internal/resources/v1alpha2/code_editor.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 91b30e8e..a76bd2b6 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -1,7 +1,9 @@ package v1alpha2_resources import ( + "path/filepath" "strconv" + "strings" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -54,12 +56,15 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam return nil } + var cmdBuilder strings.Builder + cmdBuilder.WriteString("supervisord -c " + filepath.Join("/etc", "robolaunch", "services", "code-server.conf")) + podSpec := corev1.PodSpec{ Containers: []corev1.Container{ { Name: CODE_EDITOR_APP_NAME, Image: image, - Command: []string{"/bin/bash", "-c", "sleep infinity"}, + Command: internal.Bash(cmdBuilder.String()), Env: []corev1.EnvVar{ internal.Env("CODE_SERVER_PORT", strconv.FormatInt(int64(codeEditor.Spec.Port), 10)), internal.Env("FILE_BROWSER_PORT", strconv.Itoa(internal.FILE_BROWSER_PORT)), From 59d8a2d4bbf41087952398c90610fd9086e296de Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Fri, 15 Mar 2024 17:59:30 +0300 Subject: [PATCH 19/49] refactor(deployment): update deployment when remote config or version changed --- internal/resources/v1alpha2/code_editor.go | 4 --- .../roboscale.io/v1alpha2/toolkit_types.go | 3 -- .../v1alpha2/toolkit/code_editor/check.go | 24 ++++++++++++- .../v1alpha2/toolkit/code_editor/update.go | 35 +++++++++++++++++++ 4 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 pkg/controllers/v1alpha2/toolkit/code_editor/update.go diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index a76bd2b6..9c3ced74 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -97,10 +97,6 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam cfg.InjectVolumeConfiguration(&podSpec, codeEditor.Status.PVCStatuses) cfg.InjectExternalVolumeConfiguration(&podSpec, codeEditor.Status.ExternalVolumeStatuses) - if !codeEditor.Spec.Root { - cfg.InjectLinuxUserAndGroup(&podSpec) - } - if codeEditor.Spec.Remote { cfg.InjectRemoteConfigurations(&podSpec, codeEditor) } diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index d502a184..d4cb89e6 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -129,11 +129,8 @@ type CodeEditorContainer struct { // CodeEditorSpec defines the desired state of CodeEditor. type CodeEditorSpec struct { // App version of the code editor. - // +kubebuilder:validation:Enum="4.22.0" // +kubebuilder:default="4.22.0" Version string `json:"version"` - // If `true`, code editor will be consumed as `root` user. - Root bool `json:"root,omitempty"` // If `true`, code editor will be consumed remotely. Remote bool `json:"remote,omitempty"` // Configurational parameters for code editor container. diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 6a5be5e6..0374119a 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -2,8 +2,12 @@ package code_editor import ( "context" + "reflect" + "github.com/robolaunch/robot-operator/internal/label" + "github.com/robolaunch/robot-operator/internal/platform" "github.com/robolaunch/robot-operator/internal/reference" + v1alpha2_resources "github.com/robolaunch/robot-operator/internal/resources/v1alpha2" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -68,7 +72,25 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins return err } else { - // make deployment dynamic + platformMeta := label.GetPlatformMeta(instance) + + desiredImage, err := platform.GetToolsImage(instance, platformMeta.Version, v1alpha2_resources.CODE_EDITOR_APP_NAME, instance.Spec.Version) + if err != nil { + return err + } + + actualImage := deploymentQuery.Spec.Template.Spec.Containers[0].Image + + remoteConfigSynced := (instance.Spec.Remote && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Hostname, instance.Name) && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Subdomain, instance.Name)) || + (!instance.Spec.Remote && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Hostname, "") && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Subdomain, "")) + + if !reflect.DeepEqual(desiredImage, actualImage) || + !remoteConfigSynced { + err := r.updateDeployment(ctx, instance) + if err != nil { + return err + } + } instance.Status.DeploymentStatus.Resource.Created = true reference.SetReference(&instance.Status.DeploymentStatus.Resource.Reference, deploymentQuery.TypeMeta, deploymentQuery.ObjectMeta) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go new file mode 100644 index 00000000..babddec8 --- /dev/null +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go @@ -0,0 +1,35 @@ +package code_editor + +import ( + "context" + + v1alpha2_resources "github.com/robolaunch/robot-operator/internal/resources/v1alpha2" + robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" + "k8s.io/apimachinery/pkg/api/errors" + ctrl "sigs.k8s.io/controller-runtime" +) + +func (r *CodeEditorReconciler) updateDeployment(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + node, err := r.reconcileGetNode(ctx, instance) + if err != nil { + return err + } + + deployment := v1alpha2_resources.GetCodeEditorDeployment(instance, instance.GetDeploymentMetadata(), *node) + + err = ctrl.SetControllerReference(instance, deployment, r.Scheme) + if err != nil { + return err + } + + err = r.Update(ctx, deployment) + if err != nil && errors.IsAlreadyExists(err) { + return nil + } else if err != nil { + return err + } + + logger.Info("STATUS: Deployment " + deployment.Name + " is updated.") + return nil +} From 08e6558d0eb30e56d467fa8ba6111a41f55b2e52 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Fri, 15 Mar 2024 18:07:01 +0300 Subject: [PATCH 20/49] refactor(deployment): implement version validator webhook --- internal/resources/v1alpha2/code_editor.go | 15 ++++------- internal/shared.go | 7 +++++ .../roboscale.io/v1alpha2/toolkit_webhook.go | 26 +++++++++++++++++++ .../v1alpha2/toolkit/code_editor/check.go | 4 +-- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 9c3ced74..dfcdb0bd 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -19,11 +19,6 @@ import ( robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" ) -const ( - CODE_EDITOR_APP_NAME = "code-editor" - CODE_EDITOR_PORT_NAME = "code-server" -) - func getCodeEditorSelector(codeEditor robotv1alpha2.CodeEditor) map[string]string { return map[string]string{ internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY: codeEditor.Name, @@ -51,7 +46,7 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam cfg := configure.PodSpecConfigInjector{} - image, err := platform.GetToolsImage(codeEditor, platformMeta.Version, CODE_EDITOR_APP_NAME, codeEditor.Spec.Version) + image, err := platform.GetToolsImage(codeEditor, platformMeta.Version, internal.CODE_EDITOR_APP_NAME, codeEditor.Spec.Version) if err != nil { return nil } @@ -62,19 +57,19 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam podSpec := corev1.PodSpec{ Containers: []corev1.Container{ { - Name: CODE_EDITOR_APP_NAME, + Name: internal.CODE_EDITOR_APP_NAME, Image: image, Command: internal.Bash(cmdBuilder.String()), Env: []corev1.EnvVar{ internal.Env("CODE_SERVER_PORT", strconv.FormatInt(int64(codeEditor.Spec.Port), 10)), internal.Env("FILE_BROWSER_PORT", strconv.Itoa(internal.FILE_BROWSER_PORT)), - internal.Env("FILE_BROWSER_SERVICE", CODE_EDITOR_PORT_NAME), + internal.Env("FILE_BROWSER_SERVICE", internal.CODE_EDITOR_PORT_NAME), internal.Env("FILE_BROWSER_BASE_URL", v1alpha1.GetServicePath(codeEditor, "/file-browser/ide")), internal.Env("TERM", "xterm-256color"), }, Ports: []corev1.ContainerPort{ { - Name: CODE_EDITOR_PORT_NAME, + Name: internal.CODE_EDITOR_PORT_NAME, ContainerPort: codeEditor.Spec.Port, Protocol: corev1.ProtocolTCP, }, @@ -132,7 +127,7 @@ func GetCodeEditorService(codeEditor *robotv1alpha2.CodeEditor, svcNamespacedNam Selector: getCodeEditorSelector(*codeEditor), Ports: []corev1.ServicePort{ { - Name: CODE_EDITOR_PORT_NAME, + Name: internal.CODE_EDITOR_PORT_NAME, Port: codeEditor.Spec.Port, TargetPort: intstr.IntOrString{ IntVal: codeEditor.Spec.Port, diff --git a/internal/shared.go b/internal/shared.go index 8f6bdaf6..591fc3cc 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -61,6 +61,13 @@ const ( DEFAULT_TOOLS_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/robolaunch/platform/main/platforms/internal/environments.yaml" ) +// CodeEditor + +const ( + CODE_EDITOR_APP_NAME = "code-editor" + CODE_EDITOR_PORT_NAME = "code-server" +) + // Robot owned resources' postfixes (v1alpha1) const ( PVC_VAR_POSTFIX = "-pvc-var" diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_webhook.go b/pkg/api/roboscale.io/v1alpha2/toolkit_webhook.go index 196f745e..4027b4aa 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_webhook.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_webhook.go @@ -20,6 +20,9 @@ import ( "errors" "reflect" + "github.com/robolaunch/robot-operator/internal" + "github.com/robolaunch/robot-operator/internal/label" + "github.com/robolaunch/robot-operator/internal/platform" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -105,12 +108,24 @@ var _ webhook.Validator = &CodeEditor{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *CodeEditor) ValidateCreate() error { codeeditorlog.Info("validate create", "name", r.Name) + + err := r.validateVersion() + if err != nil { + return err + } + return nil } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *CodeEditor) ValidateUpdate(old runtime.Object) error { codeeditorlog.Info("validate update", "name", r.Name) + + err := r.validateVersion() + if err != nil { + return err + } + return nil } @@ -119,3 +134,14 @@ func (r *CodeEditor) ValidateDelete() error { codeeditorlog.Info("validate delete", "name", r.Name) return nil } + +func (r *CodeEditor) validateVersion() error { + platformMeta := label.GetPlatformMeta(r) + + _, err := platform.GetToolsImage(r, platformMeta.Version, internal.CODE_EDITOR_APP_NAME, r.Spec.Version) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 0374119a..6d081908 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -4,10 +4,10 @@ import ( "context" "reflect" + "github.com/robolaunch/robot-operator/internal" "github.com/robolaunch/robot-operator/internal/label" "github.com/robolaunch/robot-operator/internal/platform" "github.com/robolaunch/robot-operator/internal/reference" - v1alpha2_resources "github.com/robolaunch/robot-operator/internal/resources/v1alpha2" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -74,7 +74,7 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins platformMeta := label.GetPlatformMeta(instance) - desiredImage, err := platform.GetToolsImage(instance, platformMeta.Version, v1alpha2_resources.CODE_EDITOR_APP_NAME, instance.Spec.Version) + desiredImage, err := platform.GetToolsImage(instance, platformMeta.Version, internal.CODE_EDITOR_APP_NAME, instance.Spec.Version) if err != nil { return err } From 89e999d2606c6173421a59de73b4712bed10fff2 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Fri, 15 Mar 2024 18:16:43 +0300 Subject: [PATCH 21/49] refactor(deployment): sync volumemounts --- pkg/controllers/v1alpha2/toolkit/code_editor/check.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 6d081908..0c40659f 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -84,8 +84,11 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins remoteConfigSynced := (instance.Spec.Remote && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Hostname, instance.Name) && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Subdomain, instance.Name)) || (!instance.Spec.Remote && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Hostname, "") && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Subdomain, "")) + volumeMountsSynced := reflect.DeepEqual(instance.Spec.Container.VolumeMounts, deploymentQuery.Spec.Template.Spec.Containers[0].VolumeMounts) + if !reflect.DeepEqual(desiredImage, actualImage) || - !remoteConfigSynced { + !remoteConfigSynced || + !volumeMountsSynced { err := r.updateDeployment(ctx, instance) if err != nil { return err From cb328d6f5ab27afd4d9de674583d7746207d2a8d Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Fri, 15 Mar 2024 18:16:55 +0300 Subject: [PATCH 22/49] build(manifests): :whale: update internal manifests --- config/crd/bases/robot.roboscale.io_codeeditors.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config/crd/bases/robot.roboscale.io_codeeditors.yaml b/config/crd/bases/robot.roboscale.io_codeeditors.yaml index 6598691b..54a8659c 100644 --- a/config/crd/bases/robot.roboscale.io_codeeditors.yaml +++ b/config/crd/bases/robot.roboscale.io_codeeditors.yaml @@ -1813,9 +1813,6 @@ spec: remote: description: If `true`, code editor will be consumed remotely. type: boolean - root: - description: If `true`, code editor will be consumed as `root` user. - type: boolean serviceType: default: ClusterIP description: Service type of CodeEditor. `ClusterIP` and `NodePort` @@ -1827,8 +1824,6 @@ spec: version: default: 4.22.0 description: App version of the code editor. - enum: - - 4.22.0 type: string volumeClaimTemplates: description: Volume templates for ROS 2 workload. For each volume From 8d299da4c1a7136b50135c6876bd11fdefdf1ab8 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 10:28:54 +0300 Subject: [PATCH 23/49] fix(protocol): switch protocol field from v1.Protocol to string --- internal/resources/v1alpha1/discovery_server.go | 4 ++-- pkg/api/roboscale.io/v1alpha1/robot_types.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/resources/v1alpha1/discovery_server.go b/internal/resources/v1alpha1/discovery_server.go index 98cdfe25..2cd965aa 100644 --- a/internal/resources/v1alpha1/discovery_server.go +++ b/internal/resources/v1alpha1/discovery_server.go @@ -37,7 +37,7 @@ func GetDiscoveryServerPod(discoveryServer *robotv1alpha1.DiscoveryServer, podNa { Name: discoveryServerPortName, ContainerPort: int32(discoveryServerPortNumber), - Protocol: discoveryServer.Spec.Protocol, + Protocol: corev1.Protocol(discoveryServer.Spec.Protocol), }, }, }, @@ -79,7 +79,7 @@ func GetDiscoveryServerService(discoveryServer *robotv1alpha1.DiscoveryServer, s TargetPort: intstr.IntOrString{ IntVal: int32(discoveryServerPortNumber), }, - Protocol: discoveryServer.Spec.Protocol, + Protocol: corev1.Protocol(discoveryServer.Spec.Protocol), Name: discoveryServerPortName, }, }, diff --git a/pkg/api/roboscale.io/v1alpha1/robot_types.go b/pkg/api/roboscale.io/v1alpha1/robot_types.go index 403cce47..1a42dcc8 100644 --- a/pkg/api/roboscale.io/v1alpha1/robot_types.go +++ b/pkg/api/roboscale.io/v1alpha1/robot_types.go @@ -415,7 +415,8 @@ const ( // DiscoveryServerSpec defines the desired state of DiscoveryServer. type DiscoveryServerSpec struct { - Protocol corev1.Protocol `json:"protocol,omitempty"` + // +kubebuilder:default=TCP + Protocol string `json:"protocol,omitempty"` // ROS domain ID for robot. See https://docs.ros.org/en/foxy/Concepts/About-Domain-ID.html. // +kubebuilder:validation:Minimum=0 // +kubebuilder:validation:Maximum=101 From fc264cd0a88b7fc443020f652f5cc7c62ff0decc Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 10:34:50 +0300 Subject: [PATCH 24/49] build(manifests): :whale: update internal manifests --- CHANGELOG.md | 13 +--- config/manager/kustomization.yaml | 2 +- docs/_coverpage.md | 2 +- hack/deploy/chart/robot-operator/Chart.yaml | 4 +- .../templates/codeeditor-crd.yaml | 75 ++++++++++++++++++- .../templates/metrics-service.yaml | 2 +- .../templates/webhook-service.yaml | 2 +- hack/deploy/chart/robot-operator/values.yaml | 2 +- hack/deploy/manifests/robot_operator.yaml | 64 +++++++++++++++- 9 files changed, 143 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c38ead7..6c74ec15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## [Unreleased] +### Fix +- **protocol:** switch protocol field from v1.Protocol to string + ## [v0.2.7-alpha.6] - 2024-03-06 @@ -11,12 +14,6 @@ - **typo:** fix field reference - -## [v0.2.7-alpha.4-udp-test-2] - 2024-03-04 - - -## [v0.2.7-alpha.5] - 2024-03-04 - ## [v0.2.7-alpha.4] - 2024-02-15 @@ -441,9 +438,7 @@ [Unreleased]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.6...HEAD -[v0.2.7-alpha.6]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.4-udp-test-2...v0.2.7-alpha.6 -[v0.2.7-alpha.4-udp-test-2]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.5...v0.2.7-alpha.4-udp-test-2 -[v0.2.7-alpha.5]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.4...v0.2.7-alpha.5 +[v0.2.7-alpha.6]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.4...v0.2.7-alpha.6 [v0.2.7-alpha.4]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.1...v0.2.7-alpha.4 [v0.2.7-alpha.1]: https://github.com/robolaunch/robot-operator/compare/v0.2.6-alpha.19...v0.2.7-alpha.1 [v0.2.6-alpha.19]: https://github.com/robolaunch/robot-operator/compare/v0.2.6-alpha.18...v0.2.6-alpha.19 diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 95dfb6ed..17e62fbe 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: robolaunchio/robot-controller-manager - newTag: v0.2.7-alpha.6.1 + newTag: v0.2.7-alpha.6.2 diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 04379284..acc3762a 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -4,6 +4,6 @@ ![](https://raw.githubusercontent.com/robolaunch/trademark/main/logos/svg/rocket.svg) -# Robot Operator 0.2.7-alpha.6.1 +# Robot Operator 0.2.7-alpha.6.2 robolaunch Kubernetes Robot Operator manages lifecycle of ROS 2 based robots and enables defining, deploying and distributing robots declaratively. \ No newline at end of file diff --git a/hack/deploy/chart/robot-operator/Chart.yaml b/hack/deploy/chart/robot-operator/Chart.yaml index b3b19948..c3ac1f74 100644 --- a/hack/deploy/chart/robot-operator/Chart.yaml +++ b/hack/deploy/chart/robot-operator/Chart.yaml @@ -13,9 +13,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.2.7-alpha.6.1 +version: 0.2.7-alpha.6.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.2.7-alpha.6.1" +appVersion: "v0.2.7-alpha.6.2" diff --git a/hack/deploy/chart/robot-operator/templates/codeeditor-crd.yaml b/hack/deploy/chart/robot-operator/templates/codeeditor-crd.yaml index 154fe391..c9e2afcc 100644 --- a/hack/deploy/chart/robot-operator/templates/codeeditor-crd.yaml +++ b/hack/deploy/chart/robot-operator/templates/codeeditor-crd.yaml @@ -1781,9 +1781,18 @@ spec: remote: description: If `true`, code editor will be consumed remotely. type: boolean - root: - description: If `true`, code editor will be consumed as `root` user. - type: boolean + serviceType: + default: ClusterIP + description: Service type of CodeEditor. `ClusterIP` and `NodePort` + is supported. + enum: + - ClusterIP + - NodePort + type: string + version: + default: 4.22.0 + description: App version of the code editor. + type: string volumeClaimTemplates: description: Volume templates for ROS 2 workload. For each volume template, operator will create a PersistentVolumeClaim that can be mounted to @@ -2014,6 +2023,7 @@ spec: type: array required: - port + - version type: object status: description: Most recently observed status of the CodeEditor. @@ -2478,6 +2488,65 @@ spec: type: object type: object type: array + serviceStatus: + description: Status of code editor service. + properties: + resource: + description: Generic status for any owned resource. + properties: + created: + description: Shows if the owned resource is created. + type: boolean + phase: + description: Phase of the owned resource. + type: string + reference: + description: Reference to the owned resource. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container + within a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that + triggered the event) or if no container name is specified + "spec.containers[2]" (container with index 2 in this pod). + This syntax is chosen only to have some well-defined way + of referencing a part of an object. TODO: this design + is not final and this field is subject to change in the + future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - created + type: object + urls: + additionalProperties: + type: string + description: Connection URL. + type: object + type: object type: object type: object served: true diff --git a/hack/deploy/chart/robot-operator/templates/metrics-service.yaml b/hack/deploy/chart/robot-operator/templates/metrics-service.yaml index b106621e..3f0f5bfc 100644 --- a/hack/deploy/chart/robot-operator/templates/metrics-service.yaml +++ b/hack/deploy/chart/robot-operator/templates/metrics-service.yaml @@ -14,4 +14,4 @@ spec: control-plane: controller-manager {{- include "robot-operator.selectorLabels" . | nindent 4 }} ports: - {{- .Values.metricsService.ports | toYaml | nindent 2 }} \ No newline at end of file + {{- .Values.metricsService.ports | toYaml | nindent 2 -}} \ No newline at end of file diff --git a/hack/deploy/chart/robot-operator/templates/webhook-service.yaml b/hack/deploy/chart/robot-operator/templates/webhook-service.yaml index 61ab9ec1..6ffc253b 100644 --- a/hack/deploy/chart/robot-operator/templates/webhook-service.yaml +++ b/hack/deploy/chart/robot-operator/templates/webhook-service.yaml @@ -13,4 +13,4 @@ spec: control-plane: controller-manager {{- include "robot-operator.selectorLabels" . | nindent 4 }} ports: - {{- .Values.webhookService.ports | toYaml | nindent 2 }} \ No newline at end of file + {{- .Values.webhookService.ports | toYaml | nindent 2 -}} \ No newline at end of file diff --git a/hack/deploy/chart/robot-operator/values.yaml b/hack/deploy/chart/robot-operator/values.yaml index 6ae8eda7..ce4cb6a2 100644 --- a/hack/deploy/chart/robot-operator/values.yaml +++ b/hack/deploy/chart/robot-operator/values.yaml @@ -32,7 +32,7 @@ controllerManager: - ALL image: repository: robolaunchio/robot-controller-manager - tag: v0.2.7-alpha.6.1 + tag: v0.2.7-alpha.6.2 resources: limits: cpu: 500m diff --git a/hack/deploy/manifests/robot_operator.yaml b/hack/deploy/manifests/robot_operator.yaml index 9f3158f0..c0a8f33e 100644 --- a/hack/deploy/manifests/robot_operator.yaml +++ b/hack/deploy/manifests/robot_operator.yaml @@ -1518,9 +1518,17 @@ spec: remote: description: If `true`, code editor will be consumed remotely. type: boolean - root: - description: If `true`, code editor will be consumed as `root` user. - type: boolean + serviceType: + default: ClusterIP + description: Service type of CodeEditor. `ClusterIP` and `NodePort` is supported. + enum: + - ClusterIP + - NodePort + type: string + version: + default: 4.22.0 + description: App version of the code editor. + type: string volumeClaimTemplates: description: Volume templates for ROS 2 workload. For each volume template, operator will create a PersistentVolumeClaim that can be mounted to the ROS 2 workload. items: @@ -1657,6 +1665,7 @@ spec: type: array required: - port + - version type: object status: description: Most recently observed status of the CodeEditor. @@ -2027,6 +2036,53 @@ spec: type: object type: object type: array + serviceStatus: + description: Status of code editor service. + properties: + resource: + description: Generic status for any owned resource. + properties: + created: + description: Shows if the owned resource is created. + type: boolean + phase: + description: Phase of the owned resource. + type: string + reference: + description: Reference to the owned resource. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: "spec.containers{name}" (where "name" refers to the name of the container that triggered the event) or if no container name is specified "spec.containers[2]" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object. TODO: this design is not final and this field is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - created + type: object + urls: + additionalProperties: + type: string + description: Connection URL. + type: object + type: object type: object type: object served: true @@ -11259,7 +11315,7 @@ spec: - --leader-elect command: - /manager - image: robolaunchio/robot-controller-manager:v0.2.7-alpha.6.1 + image: robolaunchio/robot-controller-manager:v0.2.7-alpha.6.2 livenessProbe: httpGet: path: /healthz From 1d27b0092f1fbc1b64daefb596dba9157cca91c4 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 11:53:48 +0300 Subject: [PATCH 25/49] refactor(webhooks): default ds protocol --- pkg/api/roboscale.io/v1alpha1/robot_types.go | 2 +- pkg/api/roboscale.io/v1alpha1/robot_webhook.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/api/roboscale.io/v1alpha1/robot_types.go b/pkg/api/roboscale.io/v1alpha1/robot_types.go index 1a42dcc8..88a5a0e6 100644 --- a/pkg/api/roboscale.io/v1alpha1/robot_types.go +++ b/pkg/api/roboscale.io/v1alpha1/robot_types.go @@ -415,7 +415,7 @@ const ( // DiscoveryServerSpec defines the desired state of DiscoveryServer. type DiscoveryServerSpec struct { - // +kubebuilder:default=TCP + // +kubebuilder:validation:Enum=TCP;UDP Protocol string `json:"protocol,omitempty"` // ROS domain ID for robot. See https://docs.ros.org/en/foxy/Concepts/About-Domain-ID.html. // +kubebuilder:validation:Minimum=0 diff --git a/pkg/api/roboscale.io/v1alpha1/robot_webhook.go b/pkg/api/roboscale.io/v1alpha1/robot_webhook.go index 2d8c021e..5c7f32a3 100644 --- a/pkg/api/roboscale.io/v1alpha1/robot_webhook.go +++ b/pkg/api/roboscale.io/v1alpha1/robot_webhook.go @@ -39,6 +39,7 @@ func (r *Robot) Default() { _ = r.setRepositoryInfo() r.setWorkspacesPath() r.setDiscoveryServerDomainID() + r.setDiscoveryServerProtocol() } //+kubebuilder:webhook:path=/validate-robot-roboscale-io-v1alpha1-robot,mutating=false,failurePolicy=fail,sideEffects=None,groups=robot.roboscale.io,resources=robots,verbs=create;update,versions=v1alpha1,name=vrobot.kb.io,admissionReviewVersions=v1 @@ -336,6 +337,12 @@ func (r *Robot) setDiscoveryServerDomainID() { } } +func (r *Robot) setDiscoveryServerProtocol() { + if reflect.DeepEqual(r.Spec.Type, TypeRobot) { + r.Spec.RobotConfig.DiscoveryServerTemplate.Protocol = "UDP" + } +} + // ******************************** // DiscoveryServer webhooks // ******************************** From 3155caef239a6ee5fd1e24d790a60b251757b496 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 11:53:56 +0300 Subject: [PATCH 26/49] build(manifests): :whale: update internal manifests --- CHANGELOG.md | 6 +++++- .../robot.roboscale.io_discoveryservers.yaml | 4 +++- .../robot.roboscale.io_robotartifacts.yaml | 4 +++- .../crd/bases/robot.roboscale.io_robots.yaml | 4 +++- .../robot.roboscale.io_ros2workloads.yaml | 4 +++- config/manager/kustomization.yaml | 2 +- docs/_coverpage.md | 2 +- hack/deploy/chart/robot-operator/Chart.yaml | 4 ++-- .../templates/discoveryserver-crd.yaml | 4 +++- .../robot-operator/templates/robot-crd.yaml | 4 +++- .../templates/robotartifact-crd.yaml | 4 +++- .../templates/ros2workload-crd.yaml | 4 +++- hack/deploy/chart/robot-operator/values.yaml | 2 +- hack/deploy/manifests/robot_operator.yaml | 18 +++++++++++++----- 14 files changed, 47 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c74ec15..e8ea499b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## [Unreleased] + + +## [v0.2.7-alpha.6.2] - 2024-03-18 ### Fix - **protocol:** switch protocol field from v1.Protocol to string @@ -437,7 +440,8 @@ - Merge pull request [#24](https://github.com/robolaunch/robot-operator/issues/24) from robolaunch/23-allow-multiple-launches -[Unreleased]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.6...HEAD +[Unreleased]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.6.2...HEAD +[v0.2.7-alpha.6.2]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.6...v0.2.7-alpha.6.2 [v0.2.7-alpha.6]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.4...v0.2.7-alpha.6 [v0.2.7-alpha.4]: https://github.com/robolaunch/robot-operator/compare/v0.2.7-alpha.1...v0.2.7-alpha.4 [v0.2.7-alpha.1]: https://github.com/robolaunch/robot-operator/compare/v0.2.6-alpha.19...v0.2.7-alpha.1 diff --git a/config/crd/bases/robot.roboscale.io_discoveryservers.yaml b/config/crd/bases/robot.roboscale.io_discoveryservers.yaml index 3255dba8..f2887ca1 100644 --- a/config/crd/bases/robot.roboscale.io_discoveryservers.yaml +++ b/config/crd/bases/robot.roboscale.io_discoveryservers.yaml @@ -75,7 +75,9 @@ spec: Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` diff --git a/config/crd/bases/robot.roboscale.io_robotartifacts.yaml b/config/crd/bases/robot.roboscale.io_robotartifacts.yaml index 5eca87b5..56577c80 100644 --- a/config/crd/bases/robot.roboscale.io_robotartifacts.yaml +++ b/config/crd/bases/robot.roboscale.io_robotartifacts.yaml @@ -120,7 +120,9 @@ spec: DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used diff --git a/config/crd/bases/robot.roboscale.io_robots.yaml b/config/crd/bases/robot.roboscale.io_robots.yaml index 7b33c653..c8d326cb 100644 --- a/config/crd/bases/robot.roboscale.io_robots.yaml +++ b/config/crd/bases/robot.roboscale.io_robots.yaml @@ -141,7 +141,9 @@ spec: DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used diff --git a/config/crd/bases/robot.roboscale.io_ros2workloads.yaml b/config/crd/bases/robot.roboscale.io_ros2workloads.yaml index 44b0d023..6de29fcf 100644 --- a/config/crd/bases/robot.roboscale.io_ros2workloads.yaml +++ b/config/crd/bases/robot.roboscale.io_ros2workloads.yaml @@ -55,7 +55,9 @@ spec: Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 17e62fbe..fbffece1 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: robolaunchio/robot-controller-manager - newTag: v0.2.7-alpha.6.2 + newTag: v0.2.7-alpha.6.3 diff --git a/docs/_coverpage.md b/docs/_coverpage.md index acc3762a..693ee0cb 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -4,6 +4,6 @@ ![](https://raw.githubusercontent.com/robolaunch/trademark/main/logos/svg/rocket.svg) -# Robot Operator 0.2.7-alpha.6.2 +# Robot Operator 0.2.7-alpha.6.3 robolaunch Kubernetes Robot Operator manages lifecycle of ROS 2 based robots and enables defining, deploying and distributing robots declaratively. \ No newline at end of file diff --git a/hack/deploy/chart/robot-operator/Chart.yaml b/hack/deploy/chart/robot-operator/Chart.yaml index c3ac1f74..066d3d39 100644 --- a/hack/deploy/chart/robot-operator/Chart.yaml +++ b/hack/deploy/chart/robot-operator/Chart.yaml @@ -13,9 +13,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.2.7-alpha.6.2 +version: 0.2.7-alpha.6.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v0.2.7-alpha.6.2" +appVersion: "v0.2.7-alpha.6.3" diff --git a/hack/deploy/chart/robot-operator/templates/discoveryserver-crd.yaml b/hack/deploy/chart/robot-operator/templates/discoveryserver-crd.yaml index e5768233..887e4b9e 100644 --- a/hack/deploy/chart/robot-operator/templates/discoveryserver-crd.yaml +++ b/hack/deploy/chart/robot-operator/templates/discoveryserver-crd.yaml @@ -75,7 +75,9 @@ spec: hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` diff --git a/hack/deploy/chart/robot-operator/templates/robot-crd.yaml b/hack/deploy/chart/robot-operator/templates/robot-crd.yaml index 4649b433..5c9605fe 100644 --- a/hack/deploy/chart/robot-operator/templates/robot-crd.yaml +++ b/hack/deploy/chart/robot-operator/templates/robot-crd.yaml @@ -153,7 +153,9 @@ spec: DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used diff --git a/hack/deploy/chart/robot-operator/templates/robotartifact-crd.yaml b/hack/deploy/chart/robot-operator/templates/robotartifact-crd.yaml index ec9ca6f5..43102fbf 100644 --- a/hack/deploy/chart/robot-operator/templates/robotartifact-crd.yaml +++ b/hack/deploy/chart/robot-operator/templates/robotartifact-crd.yaml @@ -120,7 +120,9 @@ spec: DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used diff --git a/hack/deploy/chart/robot-operator/templates/ros2workload-crd.yaml b/hack/deploy/chart/robot-operator/templates/ros2workload-crd.yaml index 7998a706..c475ab9c 100644 --- a/hack/deploy/chart/robot-operator/templates/ros2workload-crd.yaml +++ b/hack/deploy/chart/robot-operator/templates/ros2workload-crd.yaml @@ -55,7 +55,9 @@ spec: Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` diff --git a/hack/deploy/chart/robot-operator/values.yaml b/hack/deploy/chart/robot-operator/values.yaml index ce4cb6a2..ca2bca55 100644 --- a/hack/deploy/chart/robot-operator/values.yaml +++ b/hack/deploy/chart/robot-operator/values.yaml @@ -32,7 +32,7 @@ controllerManager: - ALL image: repository: robolaunchio/robot-controller-manager - tag: v0.2.7-alpha.6.2 + tag: v0.2.7-alpha.6.3 resources: limits: cpu: 500m diff --git a/hack/deploy/manifests/robot_operator.yaml b/hack/deploy/manifests/robot_operator.yaml index c0a8f33e..0b7d5fdd 100644 --- a/hack/deploy/manifests/robot_operator.yaml +++ b/hack/deploy/manifests/robot_operator.yaml @@ -2156,7 +2156,9 @@ spec: description: If instance type is `Server`, it can be an arbitrary value. If instance type is `Client`, it should be the same with Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` is `Client`. Referenced object can be previously provisioned in another cluster. In that case, cluster's name can be specified in `.spec.cluster` field. @@ -4071,7 +4073,9 @@ spec: description: If instance type is `Server`, it can be an arbitrary value. If instance type is `Client`, it should be the same with Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` is `Client`. Referenced object can be previously provisioned in another cluster. In that case, cluster's name can be specified in `.spec.cluster` field. @@ -5418,7 +5422,9 @@ spec: description: If instance type is `Server`, it can be an arbitrary value. If instance type is `Client`, it should be the same with Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` is `Client`. Referenced object can be previously provisioned in another cluster. In that case, cluster's name can be specified in `.spec.cluster` field. @@ -8079,7 +8085,9 @@ spec: description: If instance type is `Server`, it can be an arbitrary value. If instance type is `Client`, it should be the same with Server's hostname. Used for getting Server's IP over DNS. type: string protocol: - default: TCP + enum: + - TCP + - UDP type: string reference: description: Reference to the `Server` instance. It is used if `.spec.type` is `Client`. Referenced object can be previously provisioned in another cluster. In that case, cluster's name can be specified in `.spec.cluster` field. @@ -11315,7 +11323,7 @@ spec: - --leader-elect command: - /manager - image: robolaunchio/robot-controller-manager:v0.2.7-alpha.6.2 + image: robolaunchio/robot-controller-manager:v0.2.7-alpha.6.3 livenessProbe: httpGet: path: /healthz From a7449e5d7c14d438a53d0a009e7edaed09a4db4a Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 16:01:47 +0300 Subject: [PATCH 27/49] refactor(ros2workload): delete unused pvcs --- .../production/ros2_workload/register.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/controllers/v1alpha2/production/ros2_workload/register.go b/pkg/controllers/v1alpha2/production/ros2_workload/register.go index 70a75fc6..fa4e3c0b 100644 --- a/pkg/controllers/v1alpha2/production/ros2_workload/register.go +++ b/pkg/controllers/v1alpha2/production/ros2_workload/register.go @@ -13,6 +13,22 @@ func (r *ROS2WorkloadReconciler) registerPVCs(ctx context.Context, instance *rob pvcStatuses := []robotv1alpha2.OwnedPVCStatus{} if len(instance.Spec.VolumeClaimTemplates) != len(instance.Status.PVCStatuses) { + + if len(instance.Status.PVCStatuses) > len(instance.Spec.VolumeClaimTemplates) { + for key := len(instance.Spec.VolumeClaimTemplates); key < len(instance.Status.PVCStatuses); key++ { + pvc := corev1.PersistentVolumeClaim{} + err := r.Get(ctx, *instance.GetPersistentVolumeClaimMetadata(key), &pvc) + if err != nil { + return err + } + + err = r.Delete(ctx, &pvc) + if err != nil { + return err + } + } + } + for key := range instance.Spec.VolumeClaimTemplates { pvcStatus := robotv1alpha2.OwnedPVCStatus{ Resource: robotv1alpha2.OwnedResourceStatus{ From a0b1980cb71afb562316cd468662f85c2528d51b Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 16:24:48 +0300 Subject: [PATCH 28/49] refactor(codeeditor): delete unused pvcs --- .../code_editor/codeeditor_controller.go | 26 +++++++++++++------ .../v1alpha2/toolkit/code_editor/register.go | 26 +++++++++++++++++-- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index 89d660af..0b309606 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -61,11 +61,14 @@ func (r *CodeEditorReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - r.reconcileRegisterResources(instance) + err = r.reconcileRegisterResources(ctx, instance) + if err != nil { + return result, err + } err = r.reconcileUpdateInstanceStatus(ctx, instance) if err != nil { - return ctrl.Result{}, err + return result, err } err = r.reconcileCheckStatus(ctx, instance, &result) @@ -75,26 +78,33 @@ func (r *CodeEditorReconciler) Reconcile(ctx context.Context, req ctrl.Request) err = r.reconcileUpdateInstanceStatus(ctx, instance) if err != nil { - return ctrl.Result{}, err + return result, err } err = r.reconcileCheckResources(ctx, instance) if err != nil { - return ctrl.Result{}, err + return result, err } err = r.reconcileUpdateInstanceStatus(ctx, instance) if err != nil { - return ctrl.Result{}, err + return result, err } return result, nil } -func (r *CodeEditorReconciler) reconcileRegisterResources(instance *robotv1alpha2.CodeEditor) error { +func (r *CodeEditorReconciler) reconcileRegisterResources(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { - r.registerPVCs(instance) - r.registerExternalVolumes(instance) + err := r.registerPVCs(ctx, instance) + if err != nil { + return err + } + + err = r.registerExternalVolumes(ctx, instance) + if err != nil { + return err + } return nil } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/register.go b/pkg/controllers/v1alpha2/toolkit/code_editor/register.go index 3c7815d8..aa68a33c 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/register.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/register.go @@ -1,15 +1,33 @@ package code_editor import ( + "context" + robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" corev1 "k8s.io/api/core/v1" ) -func (r *CodeEditorReconciler) registerPVCs(instance *robotv1alpha2.CodeEditor) { +func (r *CodeEditorReconciler) registerPVCs(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { pvcStatuses := []robotv1alpha2.OwnedPVCStatus{} if len(instance.Spec.VolumeClaimTemplates) != len(instance.Status.PVCStatuses) { + + if len(instance.Status.PVCStatuses) > len(instance.Spec.VolumeClaimTemplates) { + for key := len(instance.Spec.VolumeClaimTemplates); key < len(instance.Status.PVCStatuses); key++ { + pvc := corev1.PersistentVolumeClaim{} + err := r.Get(ctx, *instance.GetPersistentVolumeClaimMetadata(key), &pvc) + if err != nil { + return err + } + + err = r.Delete(ctx, &pvc) + if err != nil { + return err + } + } + } + for key := range instance.Spec.VolumeClaimTemplates { pvcStatus := robotv1alpha2.OwnedPVCStatus{ Resource: robotv1alpha2.OwnedResourceStatus{ @@ -23,9 +41,11 @@ func (r *CodeEditorReconciler) registerPVCs(instance *robotv1alpha2.CodeEditor) } instance.Status.PVCStatuses = pvcStatuses } + + return nil } -func (r *CodeEditorReconciler) registerExternalVolumes(instance *robotv1alpha2.CodeEditor) { +func (r *CodeEditorReconciler) registerExternalVolumes(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { evStatuses := []robotv1alpha2.ExternalVolumeStatus{} @@ -38,4 +58,6 @@ func (r *CodeEditorReconciler) registerExternalVolumes(instance *robotv1alpha2.C } instance.Status.ExternalVolumeStatuses = evStatuses } + + return nil } From 373eea753eeb221a91f12dc25ba1939bfc8e0a34 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 18:23:13 +0300 Subject: [PATCH 29/49] refactor(pvc): update workload when volume config is changed --- pkg/api/roboscale.io/v1alpha2/toolkit_types.go | 2 ++ pkg/controllers/v1alpha2/toolkit/code_editor/check.go | 4 +++- pkg/controllers/v1alpha2/toolkit/code_editor/register.go | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index d4cb89e6..41f50af8 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -162,4 +162,6 @@ type CodeEditorStatus struct { DeploymentStatus OwnedDeploymentStatus `json:"deploymentStatus,omitempty"` // Status of code editor service. ServiceStatus OwnedServiceStatus `json:"serviceStatus,omitempty"` + // Field to indicate if the workload should be restarted. + WorkloadUpdateNeeded bool `json:"workloadUpdateNeeded,omitempty"` } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 0c40659f..7c8ee9d8 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -88,11 +88,13 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins if !reflect.DeepEqual(desiredImage, actualImage) || !remoteConfigSynced || - !volumeMountsSynced { + !volumeMountsSynced || + instance.Status.WorkloadUpdateNeeded { err := r.updateDeployment(ctx, instance) if err != nil { return err } + instance.Status.WorkloadUpdateNeeded = false } instance.Status.DeploymentStatus.Resource.Created = true diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/register.go b/pkg/controllers/v1alpha2/toolkit/code_editor/register.go index aa68a33c..8a7c3e33 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/register.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/register.go @@ -40,6 +40,7 @@ func (r *CodeEditorReconciler) registerPVCs(ctx context.Context, instance *robot pvcStatuses = append(pvcStatuses, pvcStatus) } instance.Status.PVCStatuses = pvcStatuses + instance.Status.WorkloadUpdateNeeded = true } return nil @@ -57,6 +58,7 @@ func (r *CodeEditorReconciler) registerExternalVolumes(ctx context.Context, inst evStatuses = append(evStatuses, evStatus) } instance.Status.ExternalVolumeStatuses = evStatuses + instance.Status.WorkloadUpdateNeeded = true } return nil From 5171c7f43931b537d17b2589d9c2b95cbc58f6bb Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 18:42:59 +0300 Subject: [PATCH 30/49] refactor(port): update workload when the code editor port is changed --- .../v1alpha2/toolkit/code_editor/check.go | 31 ++++++++++++++++++- .../v1alpha2/toolkit/code_editor/update.go | 20 ++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 7c8ee9d8..01c14ffb 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -86,9 +86,23 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins volumeMountsSynced := reflect.DeepEqual(instance.Spec.Container.VolumeMounts, deploymentQuery.Spec.Template.Spec.Containers[0].VolumeMounts) + desiredPort := instance.Spec.Port + var actualPort int32 + if len(deploymentQuery.Spec.Template.Spec.Containers) > 0 { + cont := deploymentQuery.Spec.Template.Spec.Containers[0] + for _, cPort := range cont.Ports { + if cPort.Name == internal.CODE_EDITOR_PORT_NAME { + actualPort = cPort.ContainerPort + } + } + } + + portSynced := desiredPort == actualPort + if !reflect.DeepEqual(desiredImage, actualImage) || !remoteConfigSynced || !volumeMountsSynced || + !portSynced || instance.Status.WorkloadUpdateNeeded { err := r.updateDeployment(ctx, instance) if err != nil { @@ -114,7 +128,22 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan return err } else { - // make service dynamic + desiredPort := instance.Spec.Port + var actualPort int32 + for _, sPort := range serviceQuery.Spec.Ports { + if sPort.Name == internal.CODE_EDITOR_PORT_NAME { + actualPort = sPort.Port + } + } + + portSynced := desiredPort == actualPort + + if !portSynced { + err := r.updateService(ctx, instance) + if err != nil { + return err + } + } instance.Status.ServiceStatus.Resource.Created = true reference.SetReference(&instance.Status.ServiceStatus.Resource.Reference, serviceQuery.TypeMeta, serviceQuery.ObjectMeta) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go index babddec8..58e258c1 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go @@ -33,3 +33,23 @@ func (r *CodeEditorReconciler) updateDeployment(ctx context.Context, instance *r logger.Info("STATUS: Deployment " + deployment.Name + " is updated.") return nil } + +func (r *CodeEditorReconciler) updateService(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + service := v1alpha2_resources.GetCodeEditorService(instance, instance.GetServiceMetadata()) + + err := ctrl.SetControllerReference(instance, service, r.Scheme) + if err != nil { + return err + } + + err = r.Update(ctx, service) + if err != nil && errors.IsAlreadyExists(err) { + return nil + } else if err != nil { + return err + } + + logger.Info("STATUS: Service " + service.Name + " is updated.") + return nil +} From 95d245ba94dcae4b58f3d891fd0236dfc094b7ab Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 18:51:07 +0300 Subject: [PATCH 31/49] refactor(service): update service when the service type is changed --- pkg/controllers/v1alpha2/toolkit/code_editor/check.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 01c14ffb..7ebd88ad 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -138,7 +138,10 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan portSynced := desiredPort == actualPort - if !portSynced { + serviceTypeSynced := instance.Spec.Remote || (!instance.Spec.Remote && reflect.DeepEqual(instance.Spec.ServiceType, serviceQuery.Spec.Type)) + + if !portSynced || + !serviceTypeSynced { err := r.updateService(ctx, instance) if err != nil { return err From 319934e4dab11eb95c93cd1ea03a96f5259ea4ff Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Mon, 18 Mar 2024 18:52:39 +0300 Subject: [PATCH 32/49] build(manifests): :whale: update internal manifests --- config/crd/bases/robot.roboscale.io_codeeditors.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/crd/bases/robot.roboscale.io_codeeditors.yaml b/config/crd/bases/robot.roboscale.io_codeeditors.yaml index 54a8659c..51afa8ec 100644 --- a/config/crd/bases/robot.roboscale.io_codeeditors.yaml +++ b/config/crd/bases/robot.roboscale.io_codeeditors.yaml @@ -2584,6 +2584,9 @@ spec: description: Connection URL. type: object type: object + workloadUpdateNeeded: + description: Field to indicate if the workload should be restarted. + type: boolean type: object type: object served: true From 78e0573a8a6349c0fdee1ca8ff04e32ab66f5617 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 11:17:36 +0300 Subject: [PATCH 33/49] fix(update): remove already exists condition from update to prevent redundant reconciles --- .../v1alpha2/production/ros2_workload/update.go | 13 +++---------- .../v1alpha2/toolkit/code_editor/update.go | 9 ++------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/pkg/controllers/v1alpha2/production/ros2_workload/update.go b/pkg/controllers/v1alpha2/production/ros2_workload/update.go index d866e8ed..ff89d50d 100644 --- a/pkg/controllers/v1alpha2/production/ros2_workload/update.go +++ b/pkg/controllers/v1alpha2/production/ros2_workload/update.go @@ -4,7 +4,6 @@ import ( "context" v1alpha2_resources "github.com/robolaunch/robot-operator/internal/resources/v1alpha2" - "k8s.io/apimachinery/pkg/api/errors" ctrl "sigs.k8s.io/controller-runtime" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" @@ -20,9 +19,7 @@ func (r *ROS2WorkloadReconciler) updateDiscoveryServer(ctx context.Context, inst } err = r.Update(ctx, discoveryServer) - if err != nil && errors.IsAlreadyExists(err) { - return nil - } else if err != nil { + if err != nil { return err } @@ -40,9 +37,7 @@ func (r *ROS2WorkloadReconciler) updateROS2Bridge(ctx context.Context, instance } err = r.Update(ctx, ros2Bridge) - if err != nil && errors.IsAlreadyExists(err) { - return nil - } else if err != nil { + if err != nil { return err } @@ -65,9 +60,7 @@ func (r *ROS2WorkloadReconciler) updateStatefulSet(ctx context.Context, instance } err = r.Update(ctx, statefulSet) - if err != nil && errors.IsAlreadyExists(err) { - return nil - } else if err != nil { + if err != nil { return err } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go index 58e258c1..3a8b7a78 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go @@ -5,7 +5,6 @@ import ( v1alpha2_resources "github.com/robolaunch/robot-operator/internal/resources/v1alpha2" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" - "k8s.io/apimachinery/pkg/api/errors" ctrl "sigs.k8s.io/controller-runtime" ) @@ -24,9 +23,7 @@ func (r *CodeEditorReconciler) updateDeployment(ctx context.Context, instance *r } err = r.Update(ctx, deployment) - if err != nil && errors.IsAlreadyExists(err) { - return nil - } else if err != nil { + if err != nil { return err } @@ -44,9 +41,7 @@ func (r *CodeEditorReconciler) updateService(ctx context.Context, instance *robo } err = r.Update(ctx, service) - if err != nil && errors.IsAlreadyExists(err) { - return nil - } else if err != nil { + if err != nil { return err } From 885d298a3892a824ffa85981614026bceda118f9 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 12:29:49 +0300 Subject: [PATCH 34/49] refactor(remote): make remote config dynamic --- internal/resources/v1alpha2/code_editor.go | 8 ++++---- .../v1alpha2/toolkit/code_editor/check.go | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index dfcdb0bd..82fc9a19 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -145,6 +145,10 @@ func GetCodeEditorService(codeEditor *robotv1alpha2.CodeEditor, svcNamespacedNam }, } + if codeEditor.Spec.Remote { + cfg.InjectRemoteConfigurations(&serviceSpec) + } + service := corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: svcNamespacedName.Name, @@ -153,9 +157,5 @@ func GetCodeEditorService(codeEditor *robotv1alpha2.CodeEditor, svcNamespacedNam Spec: serviceSpec, } - if codeEditor.Spec.Remote { - cfg.InjectRemoteConfigurations(&serviceSpec) - } - return &service } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 7ebd88ad..2e00a850 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -97,7 +97,7 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins } } - portSynced := desiredPort == actualPort + portSynced := reflect.DeepEqual(desiredPort, actualPort) if !reflect.DeepEqual(desiredImage, actualImage) || !remoteConfigSynced || @@ -136,10 +136,22 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan } } - portSynced := desiredPort == actualPort + remoteConfigSynced := (instance.Spec.Remote && reflect.DeepEqual(serviceQuery.Spec.ClusterIP, corev1.ClusterIPNone)) || + (!instance.Spec.Remote && reflect.DeepEqual(serviceQuery.Spec.Type, instance.Spec.ServiceType) && !reflect.DeepEqual(serviceQuery.Spec.ClusterIP, corev1.ClusterIPNone)) + + portSynced := reflect.DeepEqual(desiredPort, actualPort) serviceTypeSynced := instance.Spec.Remote || (!instance.Spec.Remote && reflect.DeepEqual(instance.Spec.ServiceType, serviceQuery.Spec.Type)) + if !remoteConfigSynced { + err := r.Delete(ctx, serviceQuery) + if err != nil { + return err + } + instance.Status.ServiceStatus = robotv1alpha2.OwnedServiceStatus{} + return nil + } + if !portSynced || !serviceTypeSynced { err := r.updateService(ctx, instance) From 37edb17ac1fe688311a3b2723cf1470dac23962b Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 14:13:55 +0300 Subject: [PATCH 35/49] refactor(api): add ingress related fields --- internal/shared.go | 1 + pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go | 7 +++++++ pkg/api/roboscale.io/v1alpha2/toolkit_types.go | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/internal/shared.go b/internal/shared.go index 591fc3cc..dfd9a7ca 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -90,6 +90,7 @@ const ( STATEFULSET_POSTFIX = "" DEPLOYMENT_POSTFIX = "" SERVICE_POSTFIX = "" + INGRESS_POSTFIX = "" ) // WorkspaceManager owned resources' postfixes diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go b/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go index e941280c..736be395 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_helpers.go @@ -56,3 +56,10 @@ func (codeEditor *CodeEditor) GetServiceMetadata() *types.NamespacedName { Namespace: codeEditor.Namespace, } } + +func (codeEditor *CodeEditor) GetIngressMetadata() *types.NamespacedName { + return &types.NamespacedName{ + Name: codeEditor.Name + internal.INGRESS_POSTFIX, + Namespace: codeEditor.Namespace, + } +} diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index 41f50af8..8f722151 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -142,6 +142,8 @@ type CodeEditorSpec struct { // +kubebuilder:validation:Enum=ClusterIP;NodePort // +kubebuilder:default="ClusterIP" ServiceType corev1.ServiceType `json:"serviceType,omitempty"` + // CodeEditor will create an Ingress resource if `true`. + Ingress bool `json:"ingress,omitempty"` // Volume templates for ROS 2 workload. // For each volume template, operator will create a PersistentVolumeClaim // that can be mounted to the ROS 2 workload. @@ -162,6 +164,8 @@ type CodeEditorStatus struct { DeploymentStatus OwnedDeploymentStatus `json:"deploymentStatus,omitempty"` // Status of code editor service. ServiceStatus OwnedServiceStatus `json:"serviceStatus,omitempty"` + // Status of CodeEditor Ingress. + IngressStatus OwnedResourceStatus `json:"ingressStatus,omitempty"` // Field to indicate if the workload should be restarted. WorkloadUpdateNeeded bool `json:"workloadUpdateNeeded,omitempty"` } From adbedcb5323e255be53284fb90dac8bdca8b2ab1 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 14:23:05 +0300 Subject: [PATCH 36/49] refactor(api): add field for tls secret name --- pkg/api/roboscale.io/v1alpha2/toolkit_types.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go index 8f722151..bb8289f9 100644 --- a/pkg/api/roboscale.io/v1alpha2/toolkit_types.go +++ b/pkg/api/roboscale.io/v1alpha2/toolkit_types.go @@ -144,6 +144,8 @@ type CodeEditorSpec struct { ServiceType corev1.ServiceType `json:"serviceType,omitempty"` // CodeEditor will create an Ingress resource if `true`. Ingress bool `json:"ingress,omitempty"` + // Name of the TLS secret for ingress resource. + TLSSecretName string `json:"tlsSecretName,omitempty"` // Volume templates for ROS 2 workload. // For each volume template, operator will create a PersistentVolumeClaim // that can be mounted to the ROS 2 workload. From 06dcd0bbdc8e5836eb01898c065f7e5c2a2ebc2d Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 14:24:11 +0300 Subject: [PATCH 37/49] refactor(ingress): define ingress for code editor --- internal/resources/v1alpha2/code_editor.go | 74 +++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 82fc9a19..83b70dcb 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -1,12 +1,14 @@ package v1alpha2_resources import ( + "fmt" "path/filepath" "strconv" "strings" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" @@ -15,7 +17,7 @@ import ( configure "github.com/robolaunch/robot-operator/internal/configure/v1alpha2" "github.com/robolaunch/robot-operator/internal/label" "github.com/robolaunch/robot-operator/internal/platform" - "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha1" + robotv1alpha1 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha1" robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" ) @@ -64,7 +66,7 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam internal.Env("CODE_SERVER_PORT", strconv.FormatInt(int64(codeEditor.Spec.Port), 10)), internal.Env("FILE_BROWSER_PORT", strconv.Itoa(internal.FILE_BROWSER_PORT)), internal.Env("FILE_BROWSER_SERVICE", internal.CODE_EDITOR_PORT_NAME), - internal.Env("FILE_BROWSER_BASE_URL", v1alpha1.GetServicePath(codeEditor, "/file-browser/ide")), + internal.Env("FILE_BROWSER_BASE_URL", robotv1alpha1.GetServicePath(codeEditor, "/file-browser/ide")), internal.Env("TERM", "xterm-256color"), }, Ports: []corev1.ContainerPort{ @@ -153,9 +155,77 @@ func GetCodeEditorService(codeEditor *robotv1alpha2.CodeEditor, svcNamespacedNam ObjectMeta: metav1.ObjectMeta{ Name: svcNamespacedName.Name, Namespace: svcNamespacedName.Namespace, + Labels: codeEditor.Labels, }, Spec: serviceSpec, } return &service } + +func GetCodeEditorIngress(codeEditor *robotv1alpha2.CodeEditor, ingressNamespacedName *types.NamespacedName) *networkingv1.Ingress { + + tenancy := label.GetTenancy(codeEditor) + platformMeta := label.GetPlatformMeta(codeEditor) + + annotations := map[string]string{ + internal.INGRESS_AUTH_URL_KEY: fmt.Sprintf(internal.INGRESS_AUTH_URL_VAL, tenancy.Team, platformMeta.Domain), + internal.INGRESS_AUTH_SIGNIN_KEY: fmt.Sprintf(internal.INGRESS_AUTH_SIGNIN_VAL, tenancy.Team, platformMeta.Domain), + internal.INGRESS_AUTH_RESPONSE_HEADERS_KEY: internal.INGRESS_AUTH_RESPONSE_HEADERS_VAL, + internal.INGRESS_CONFIGURATION_SNIPPET_KEY: internal.INGRESS_IDE_CONFIGURATION_SNIPPET_VAL, + internal.INGRESS_CERT_MANAGER_KEY: internal.INGRESS_CERT_MANAGER_VAL, + internal.INGRESS_NGINX_PROXY_BUFFER_SIZE_KEY: internal.INGRESS_NGINX_PROXY_BUFFER_SIZE_VAL, + internal.INGRESS_NGINX_REWRITE_TARGET_KEY: internal.INGRESS_NGINX_REWRITE_TARGET_VAL, + internal.INGRESS_PROXY_READ_TIMEOUT_KEY: internal.INGRESS_PROXY_READ_TIMEOUT_VAL, + } + + pathTypePrefix := networkingv1.PathTypePrefix + ingressClassNameNginx := "nginx" + + ingressSpec := networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{ + tenancy.Team + "." + platformMeta.Domain, + }, + SecretName: codeEditor.Spec.TLSSecretName, + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: tenancy.Team + "." + platformMeta.Domain, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: robotv1alpha1.GetServicePath(codeEditor, "/ide") + "(/|$)(.*)", + PathType: &pathTypePrefix, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: codeEditor.GetServiceMetadata().Name, + Port: networkingv1.ServiceBackendPort{ + Number: codeEditor.Spec.Port, + }, + }, + }, + }, + }, + }, + }, + }, + }, + IngressClassName: &ingressClassNameNginx, + } + + ingress := &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: ingressNamespacedName.Name, + Namespace: ingressNamespacedName.Namespace, + Labels: codeEditor.Labels, + Annotations: annotations, + }, + Spec: ingressSpec, + } + + return ingress +} From 0d22e05d9c6036c34b77fb4cdc1ec14d8528ef53 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 14:32:28 +0300 Subject: [PATCH 38/49] refactor(ingress): create ingress in reconciliation --- pkg/api/roboscale.io/v1alpha2/phases.go | 1 + .../v1alpha2/toolkit/code_editor/check.go | 20 +++++++++++++++++ .../code_editor/codeeditor_controller.go | 10 +++++++++ .../v1alpha2/toolkit/code_editor/create.go | 20 +++++++++++++++++ .../v1alpha2/toolkit/code_editor/handle.go | 22 +++++++++++++++++++ 5 files changed, 73 insertions(+) diff --git a/pkg/api/roboscale.io/v1alpha2/phases.go b/pkg/api/roboscale.io/v1alpha2/phases.go index 7784ed31..3aa9c20f 100644 --- a/pkg/api/roboscale.io/v1alpha2/phases.go +++ b/pkg/api/roboscale.io/v1alpha2/phases.go @@ -27,5 +27,6 @@ const ( CodeEditorPhaseCreatingPVCs CodeEditorPhase = "CreatingPVCs" CodeEditorPhaseCreatingDeployment CodeEditorPhase = "CreatingDeployment" CodeEditorPhaseCreatingService CodeEditorPhase = "CreatingService" + CodeEditorPhaseCreatingIngress CodeEditorPhase = "CreatingIngress" CodeEditorPhaseReady CodeEditorPhase = "Ready" ) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 2e00a850..25722028 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -11,6 +11,7 @@ import ( robotv1alpha2 "github.com/robolaunch/robot-operator/pkg/api/roboscale.io/v1alpha2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ) @@ -166,3 +167,22 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan return nil } + +func (r *CodeEditorReconciler) reconcileCheckIngress(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + ingressQuery := &networkingv1.Ingress{} + err := r.Get(ctx, *instance.GetIngressMetadata(), ingressQuery) + if err != nil && errors.IsNotFound(err) { + instance.Status.IngressStatus = robotv1alpha2.OwnedResourceStatus{} + } else if err != nil { + return err + } else { + + // make resource dynamic + + instance.Status.IngressStatus.Created = true + reference.SetReference(&instance.Status.IngressStatus.Reference, ingressQuery.TypeMeta, ingressQuery.ObjectMeta) + } + + return nil +} diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index 0b309606..087e5a75 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -126,6 +126,11 @@ func (r *CodeEditorReconciler) reconcileCheckStatus(ctx context.Context, instanc return robotErr.CheckCreatingOrWaitingError(result, err) } + err = r.reconcileHandleIngress(ctx, instance) + if err != nil { + return robotErr.CheckCreatingOrWaitingError(result, err) + } + return nil } @@ -151,6 +156,11 @@ func (r *CodeEditorReconciler) reconcileCheckResources(ctx context.Context, inst return err } + err = r.reconcileCheckIngress(ctx, instance) + if err != nil { + return err + } + return nil } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/create.go b/pkg/controllers/v1alpha2/toolkit/code_editor/create.go index 1e4c27d7..e71c0d47 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/create.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/create.go @@ -73,3 +73,23 @@ func (r *CodeEditorReconciler) createService(ctx context.Context, instance *robo logger.Info("STATUS: Service " + instance.GetServiceMetadata().Name + " is created.") return nil } + +func (r *CodeEditorReconciler) createIngress(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + ingress := v1alpha2_resources.GetCodeEditorIngress(instance, instance.GetIngressMetadata()) + + err := ctrl.SetControllerReference(instance, ingress, r.Scheme) + if err != nil { + return err + } + + err = r.Create(ctx, ingress) + if err != nil && errors.IsAlreadyExists(err) { + return nil + } else if err != nil { + return err + } + + logger.Info("STATUS: Ingress " + instance.GetIngressMetadata().Name + " is created.") + return nil +} diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go b/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go index e760af33..448c0314 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go @@ -85,3 +85,25 @@ func (r *CodeEditorReconciler) reconcileHandleService(ctx context.Context, insta return nil } + +func (r *CodeEditorReconciler) reconcileHandleIngress(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + if instance.Spec.Ingress && !instance.Status.IngressStatus.Created { + + instance.Status.Phase = robotv1alpha2.CodeEditorPhaseCreatingIngress + err := r.createIngress(ctx, instance) + if err != nil { + return err + } + + instance.Status.IngressStatus.Created = true + + return &robotErr.CreatingResourceError{ + ResourceKind: "Ingress", + ResourceName: instance.GetIngressMetadata().Name, + ResourceNamespace: instance.GetIngressMetadata().Namespace, + } + } + + return nil +} From 6ec0d615df2b2c4628cf4c5779147261e36730a8 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 14:33:54 +0300 Subject: [PATCH 39/49] chore(rbac): add rbac markers for ingress --- .../v1alpha2/toolkit/code_editor/codeeditor_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index 087e5a75..db478fb6 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -22,6 +22,7 @@ import ( robotErr "github.com/robolaunch/robot-operator/internal/error" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" @@ -45,6 +46,7 @@ type CodeEditorReconciler struct { //+kubebuilder:rbac:groups=core,resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete var logger logr.Logger @@ -171,5 +173,6 @@ func (r *CodeEditorReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.PersistentVolumeClaim{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). + Owns(&networkingv1.Ingress{}). Complete(r) } From 9547052a70d520c61cc68d3efa03779e0ae25825 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 17:20:35 +0300 Subject: [PATCH 40/49] refactor(dns): add service dns getters --- pkg/api/roboscale.io/v1alpha2/shared_types.go | 40 +++++++++++++++++++ .../v1alpha2/zz_generated.deepcopy.go | 1 + 2 files changed, 41 insertions(+) diff --git a/pkg/api/roboscale.io/v1alpha2/shared_types.go b/pkg/api/roboscale.io/v1alpha2/shared_types.go index 1026f393..ef75aeb3 100644 --- a/pkg/api/roboscale.io/v1alpha2/shared_types.go +++ b/pkg/api/roboscale.io/v1alpha2/shared_types.go @@ -1,8 +1,10 @@ package v1alpha2 import ( + "github.com/robolaunch/robot-operator/internal/label" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Generic status for any owned resource. @@ -55,3 +57,41 @@ type OwnedServiceStatus struct { // Connection URL. URLs map[string]string `json:"urls,omitempty"` } + +func GetServiceDNS(obj metav1.Object, prefix, postfix string) string { + tenancy := label.GetTenancy(obj) + platformMeta := label.GetPlatformMeta(obj) + connectionStr := tenancy.Team + "." + platformMeta.Domain + GetServicePath(obj, postfix) + + if prefix != "" { + connectionStr = prefix + connectionStr + } + + return connectionStr +} + +func GetServiceDNSWithNodePort(obj metav1.Object, prefix, port string) string { + tenancy := label.GetTenancy(obj) + platformMeta := label.GetPlatformMeta(obj) + connectionStr := tenancy.Team + "." + platformMeta.Domain + ":" + port + + if prefix != "" { + connectionStr = prefix + connectionStr + } + + return connectionStr +} + +func GetServicePath(obj metav1.Object, postfix string) string { + tenancy := label.GetTenancy(obj) + connectionStr := "/" + tenancy.Region + + "/" + tenancy.CloudInstanceAlias + + "/" + obj.GetNamespace() + + "/" + obj.GetName() + + if postfix != "" { + connectionStr = connectionStr + postfix + } + + return connectionStr +} diff --git a/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go b/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go index 04f02809..98898c47 100644 --- a/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/api/roboscale.io/v1alpha2/zz_generated.deepcopy.go @@ -155,6 +155,7 @@ func (in *CodeEditorStatus) DeepCopyInto(out *CodeEditorStatus) { } in.DeploymentStatus.DeepCopyInto(&out.DeploymentStatus) in.ServiceStatus.DeepCopyInto(&out.ServiceStatus) + out.IngressStatus = in.IngressStatus } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CodeEditorStatus. From 1c0e165b327e83deced0e0ae98208d450490e1c0 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 17:22:09 +0300 Subject: [PATCH 41/49] refactor(dns): update filebrowser service path --- internal/resources/v1alpha2/code_editor.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 83b70dcb..635c000d 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -66,7 +66,7 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam internal.Env("CODE_SERVER_PORT", strconv.FormatInt(int64(codeEditor.Spec.Port), 10)), internal.Env("FILE_BROWSER_PORT", strconv.Itoa(internal.FILE_BROWSER_PORT)), internal.Env("FILE_BROWSER_SERVICE", internal.CODE_EDITOR_PORT_NAME), - internal.Env("FILE_BROWSER_BASE_URL", robotv1alpha1.GetServicePath(codeEditor, "/file-browser/ide")), + internal.Env("FILE_BROWSER_BASE_URL", robotv1alpha1.GetServicePath(codeEditor, "/"+internal.FILE_BROWSER_PORT_NAME)), internal.Env("TERM", "xterm-256color"), }, Ports: []corev1.ContainerPort{ @@ -198,7 +198,7 @@ func GetCodeEditorIngress(codeEditor *robotv1alpha2.CodeEditor, ingressNamespace HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { - Path: robotv1alpha1.GetServicePath(codeEditor, "/ide") + "(/|$)(.*)", + Path: robotv1alpha1.GetServicePath(codeEditor, "/"+internal.CODE_EDITOR_APP_NAME) + "(/|$)(.*)", PathType: &pathTypePrefix, Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ @@ -209,6 +209,18 @@ func GetCodeEditorIngress(codeEditor *robotv1alpha2.CodeEditor, ingressNamespace }, }, }, + { + Path: robotv1alpha1.GetServicePath(codeEditor, "/"+internal.FILE_BROWSER_PORT_NAME) + "(/|$)(.*)", + PathType: &pathTypePrefix, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: codeEditor.GetServiceMetadata().Name, + Port: networkingv1.ServiceBackendPort{ + Number: internal.FILE_BROWSER_PORT, + }, + }, + }, + }, }, }, }, From 7a15c57e20a169e237fe567043a3dfa59eeeed57 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 17:40:38 +0300 Subject: [PATCH 42/49] refactor(ingress): make ingress dynamically configurable --- internal/resources/v1alpha2/code_editor.go | 2 +- internal/shared.go | 2 +- .../v1alpha2/toolkit/code_editor/check.go | 112 ++++++++++++++++-- .../v1alpha2/toolkit/code_editor/update.go | 18 +++ 4 files changed, 122 insertions(+), 12 deletions(-) diff --git a/internal/resources/v1alpha2/code_editor.go b/internal/resources/v1alpha2/code_editor.go index 635c000d..2604e8e8 100644 --- a/internal/resources/v1alpha2/code_editor.go +++ b/internal/resources/v1alpha2/code_editor.go @@ -65,7 +65,7 @@ func GetCodeEditorDeployment(codeEditor *robotv1alpha2.CodeEditor, deploymentNam Env: []corev1.EnvVar{ internal.Env("CODE_SERVER_PORT", strconv.FormatInt(int64(codeEditor.Spec.Port), 10)), internal.Env("FILE_BROWSER_PORT", strconv.Itoa(internal.FILE_BROWSER_PORT)), - internal.Env("FILE_BROWSER_SERVICE", internal.CODE_EDITOR_PORT_NAME), + internal.Env("FILE_BROWSER_SERVICE", "code-server"), internal.Env("FILE_BROWSER_BASE_URL", robotv1alpha1.GetServicePath(codeEditor, "/"+internal.FILE_BROWSER_PORT_NAME)), internal.Env("TERM", "xterm-256color"), }, diff --git a/internal/shared.go b/internal/shared.go index dfd9a7ca..2aac2598 100644 --- a/internal/shared.go +++ b/internal/shared.go @@ -65,7 +65,7 @@ const ( const ( CODE_EDITOR_APP_NAME = "code-editor" - CODE_EDITOR_PORT_NAME = "code-server" + CODE_EDITOR_PORT_NAME = "code-editor" ) // Robot owned resources' postfixes (v1alpha1) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 25722028..f045a503 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -2,7 +2,9 @@ package code_editor import ( "context" + "errors" "reflect" + "strconv" "github.com/robolaunch/robot-operator/internal" "github.com/robolaunch/robot-operator/internal/label" @@ -12,7 +14,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/api/errors" + k8sErr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ) @@ -22,7 +24,7 @@ func (r *CodeEditorReconciler) reconcileCheckPVCs(ctx context.Context, instance pvcQuery := &corev1.PersistentVolumeClaim{} err := r.Get(ctx, *instance.GetPersistentVolumeClaimMetadata(key), pvcQuery) - if err != nil && errors.IsNotFound(err) { + if err != nil && k8sErr.IsNotFound(err) { pvcStatus.Resource.Created = false } else if err != nil { return err @@ -48,7 +50,7 @@ func (r *CodeEditorReconciler) reconcileCheckExternalVolumes(ctx context.Context Namespace: instance.Namespace, Name: evStatus.Name, }, pvcQuery) - if err != nil && errors.IsNotFound(err) { + if err != nil && k8sErr.IsNotFound(err) { evStatus.Exists = false } else if err != nil { return err @@ -67,7 +69,7 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins deploymentQuery := &appsv1.Deployment{} err := r.Get(ctx, *instance.GetDeploymentMetadata(), deploymentQuery) - if err != nil && errors.IsNotFound(err) { + if err != nil && k8sErr.IsNotFound(err) { instance.Status.DeploymentStatus = robotv1alpha2.OwnedDeploymentStatus{} } else if err != nil { return err @@ -123,12 +125,15 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan serviceQuery := &corev1.Service{} err := r.Get(ctx, *instance.GetServiceMetadata(), serviceQuery) - if err != nil && errors.IsNotFound(err) { + if err != nil && k8sErr.IsNotFound(err) { instance.Status.ServiceStatus = robotv1alpha2.OwnedServiceStatus{} } else if err != nil { return err } else { + remoteConfigSynced := (instance.Spec.Remote && reflect.DeepEqual(serviceQuery.Spec.ClusterIP, corev1.ClusterIPNone)) || + (!instance.Spec.Remote && reflect.DeepEqual(serviceQuery.Spec.Type, instance.Spec.ServiceType) && !reflect.DeepEqual(serviceQuery.Spec.ClusterIP, corev1.ClusterIPNone)) + desiredPort := instance.Spec.Port var actualPort int32 for _, sPort := range serviceQuery.Spec.Ports { @@ -137,9 +142,6 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan } } - remoteConfigSynced := (instance.Spec.Remote && reflect.DeepEqual(serviceQuery.Spec.ClusterIP, corev1.ClusterIPNone)) || - (!instance.Spec.Remote && reflect.DeepEqual(serviceQuery.Spec.Type, instance.Spec.ServiceType) && !reflect.DeepEqual(serviceQuery.Spec.ClusterIP, corev1.ClusterIPNone)) - portSynced := reflect.DeepEqual(desiredPort, actualPort) serviceTypeSynced := instance.Spec.Remote || (!instance.Spec.Remote && reflect.DeepEqual(instance.Spec.ServiceType, serviceQuery.Spec.Type)) @@ -161,6 +163,55 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan } } + // set url(s) + + urls := make(map[string]string) + + if instance.Spec.Remote { + + // this part will be populated after implementing relay mechanism + + } else { + + if instance.Spec.Ingress { + + urls = map[string]string{ + internal.CODE_EDITOR_PORT_NAME: robotv1alpha2.GetServiceDNS(instance, "https://", "/"+internal.CODE_EDITOR_APP_NAME), + internal.FILE_BROWSER_PORT_NAME: robotv1alpha2.GetServiceDNS(instance, "https://", "/"+internal.FILE_BROWSER_PORT_NAME), + } + + } else { + + if instance.Spec.ServiceType == corev1.ServiceTypeNodePort { + + if len(serviceQuery.Spec.Ports) == 2 { + + var ceNodePort int32 + var fbNodePort int32 + for _, sPort := range serviceQuery.Spec.Ports { + if sPort.Name == internal.CODE_EDITOR_PORT_NAME { + ceNodePort = sPort.NodePort + } + if sPort.Name == internal.FILE_BROWSER_PORT_NAME { + fbNodePort = sPort.NodePort + } + } + + urls = map[string]string{ + internal.CODE_EDITOR_PORT_NAME: robotv1alpha2.GetServiceDNSWithNodePort(instance, "http://", strconv.Itoa(int(ceNodePort))), + internal.FILE_BROWSER_PORT_NAME: robotv1alpha2.GetServiceDNSWithNodePort(instance, "http://", strconv.Itoa(int(fbNodePort))), + } + } else { + return errors.New("wrong number of ports in service") + } + + } + + } + + } + + instance.Status.ServiceStatus.URLs = urls instance.Status.ServiceStatus.Resource.Created = true reference.SetReference(&instance.Status.ServiceStatus.Resource.Reference, serviceQuery.TypeMeta, serviceQuery.ObjectMeta) } @@ -172,13 +223,54 @@ func (r *CodeEditorReconciler) reconcileCheckIngress(ctx context.Context, instan ingressQuery := &networkingv1.Ingress{} err := r.Get(ctx, *instance.GetIngressMetadata(), ingressQuery) - if err != nil && errors.IsNotFound(err) { + if err != nil && k8sErr.IsNotFound(err) { instance.Status.IngressStatus = robotv1alpha2.OwnedResourceStatus{} } else if err != nil { return err } else { - // make resource dynamic + desiredPort := instance.Spec.Port + var actualPort int32 + if len(ingressQuery.Spec.Rules) > 0 { + rule := ingressQuery.Spec.Rules[0] + if len(rule.HTTP.Paths) > 0 { + codeEditorPath := rule.HTTP.Paths[0] + actualPort = codeEditorPath.Backend.Service.Port.Number + } else { + return errors.New("wrong number of ingress paths") + } + } else { + return errors.New("wrong number of ingress rules") + } + + portSynced := reflect.DeepEqual(desiredPort, actualPort) + + actualSecretName := "" + if len(ingressQuery.Spec.TLS) > 0 { + tls := ingressQuery.Spec.TLS[0] + actualSecretName = tls.SecretName + } else { + return errors.New("ingress host is not found") + } + + secretNameSynced := reflect.DeepEqual(instance.Spec.TLSSecretName, actualSecretName) + + if !portSynced || + !secretNameSynced { + err := r.updateIngress(ctx, instance) + if err != nil { + return err + } + } + + if !instance.Spec.Ingress { + err := r.Delete(ctx, ingressQuery) + if err != nil { + return err + } + + instance.Status.IngressStatus = robotv1alpha2.OwnedResourceStatus{} + } instance.Status.IngressStatus.Created = true reference.SetReference(&instance.Status.IngressStatus.Reference, ingressQuery.TypeMeta, ingressQuery.ObjectMeta) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go index 3a8b7a78..655e4ac3 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go @@ -48,3 +48,21 @@ func (r *CodeEditorReconciler) updateService(ctx context.Context, instance *robo logger.Info("STATUS: Service " + service.Name + " is updated.") return nil } + +func (r *CodeEditorReconciler) updateIngress(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + ingress := v1alpha2_resources.GetCodeEditorIngress(instance, instance.GetIngressMetadata()) + + err := ctrl.SetControllerReference(instance, ingress, r.Scheme) + if err != nil { + return err + } + + err = r.Update(ctx, ingress) + if err != nil { + return err + } + + logger.Info("STATUS: Ingress " + ingress.Name + " is updated.") + return nil +} From 560010b46348597b75c0f2bd09adf540df3b2f82 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 17:43:14 +0300 Subject: [PATCH 43/49] build(manifests): :whale: update internal manifests --- .../bases/robot.roboscale.io_codeeditors.yaml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/config/crd/bases/robot.roboscale.io_codeeditors.yaml b/config/crd/bases/robot.roboscale.io_codeeditors.yaml index 51afa8ec..49126954 100644 --- a/config/crd/bases/robot.roboscale.io_codeeditors.yaml +++ b/config/crd/bases/robot.roboscale.io_codeeditors.yaml @@ -1805,6 +1805,9 @@ spec: - name type: object type: array + ingress: + description: CodeEditor will create an Ingress resource if `true`. + type: boolean port: default: 9000 description: Port that code editor will use inside the container. @@ -1821,6 +1824,9 @@ spec: - ClusterIP - NodePort type: string + tlsSecretName: + description: Name of the TLS secret for ingress resource. + type: string version: default: 4.22.0 description: App version of the code editor. @@ -2374,6 +2380,55 @@ spec: type: string type: object type: array + ingressStatus: + description: Status of CodeEditor Ingress. + properties: + created: + description: Shows if the owned resource is created. + type: boolean + phase: + description: Phase of the owned resource. + type: string + reference: + description: Reference to the owned resource. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: 'If referring to a piece of an object instead + of an entire object, this string should contain a valid + JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within + a pod, this would take on a value like: "spec.containers{name}" + (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" + (container with index 2 in this pod). This syntax is chosen + only to have some well-defined way of referencing a part + of an object. TODO: this design is not final and this field + is subject to change in the future.' + type: string + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + namespace: + description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' + type: string + resourceVersion: + description: 'Specific resourceVersion to which this reference + is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' + type: string + uid: + description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - created + type: object phase: description: Phase of CodeEditor. It sums the general status of code editor. From 4d40a0db0c201a51e7e30dd0964ac386c39be9df Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 18:03:32 +0300 Subject: [PATCH 44/49] refactor(events): add event recorder to codeeditor reconciler --- main.go | 5 +++-- .../v1alpha2/toolkit/code_editor/codeeditor_controller.go | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 66217c3f..fc507ee4 100644 --- a/main.go +++ b/main.go @@ -365,8 +365,9 @@ func startToolkitCRDsAndWebhooks(mgr manager.Manager) { // ROS2Bridge controller & webhook if err := (&codeEditor.CodeEditorReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("code-editor"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "CodeEditor") os.Exit(1) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index db478fb6..706b5a66 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -25,6 +25,7 @@ import ( networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -36,7 +37,8 @@ import ( // CodeEditorReconciler reconciles a CodeEditor object type CodeEditorReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + Recorder record.EventRecorder } //+kubebuilder:rbac:groups=robot.roboscale.io,resources=codeeditors,verbs=get;list;watch;create;update;patch;delete @@ -47,6 +49,7 @@ type CodeEditorReconciler struct { //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;update;patch;delete var logger logr.Logger From ead703a815efd7e0168906463311ab123b1c469b Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Tue, 19 Mar 2024 18:10:19 +0300 Subject: [PATCH 45/49] build(manifests): :whale: update internal manifests --- config/rbac/role.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 98ceb84a..b6c6e76f 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -53,6 +53,18 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: From 270e7fc25c65b31216d62ff0fec2a24d76008b13 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Wed, 20 Mar 2024 12:48:31 +0300 Subject: [PATCH 46/49] refactor(events): create events in resource updates --- pkg/controllers/v1alpha2/toolkit/code_editor/check.go | 9 ++++++++- pkg/controllers/v1alpha2/toolkit/code_editor/create.go | 8 ++++++++ pkg/controllers/v1alpha2/toolkit/code_editor/update.go | 8 +++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index f045a503..d1678705 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -151,6 +151,7 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan if err != nil { return err } + r.Recorder.Event(instance, "Normal", "Deleted", "Service '"+instance.GetServiceMetadata().Name+"' is deleted to sync resources.") instance.Status.ServiceStatus = robotv1alpha2.OwnedServiceStatus{} return nil } @@ -165,7 +166,7 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan // set url(s) - urls := make(map[string]string) + urls := map[string]string{} if instance.Spec.Remote { @@ -211,6 +212,11 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan } + // TODO: Wait for pod to be in 'Running' state. + if codeEditorURL, ok := urls[internal.CODE_EDITOR_PORT_NAME]; ok && !reflect.DeepEqual(instance.Status.ServiceStatus.URLs, urls) { + r.Recorder.Event(instance, "Normal", "Ready", "CodeEditor is accessible over the URL '"+codeEditorURL+"'.") + } + instance.Status.ServiceStatus.URLs = urls instance.Status.ServiceStatus.Resource.Created = true reference.SetReference(&instance.Status.ServiceStatus.Resource.Reference, serviceQuery.TypeMeta, serviceQuery.ObjectMeta) @@ -268,6 +274,7 @@ func (r *CodeEditorReconciler) reconcileCheckIngress(ctx context.Context, instan if err != nil { return err } + r.Recorder.Event(instance, "Normal", "Deleted", "Ingress '"+instance.GetIngressMetadata().Name+"' is deleted to sync resources.") instance.Status.IngressStatus = robotv1alpha2.OwnedResourceStatus{} } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/create.go b/pkg/controllers/v1alpha2/toolkit/code_editor/create.go index e71c0d47..169210ec 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/create.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/create.go @@ -25,6 +25,8 @@ func (r *CodeEditorReconciler) createPersistentVolumeClaim(ctx context.Context, return err } + r.Recorder.Event(instance, "Normal", "Created", "PersistentVolumeClaim '"+instance.GetPersistentVolumeClaimMetadata(key).Name+"' is created.") + logger.Info("STATUS: PVC " + instance.GetPersistentVolumeClaimMetadata(key).Name + " is created.") return nil } @@ -50,6 +52,8 @@ func (r *CodeEditorReconciler) createDeployment(ctx context.Context, instance *r return err } + r.Recorder.Event(instance, "Normal", "Created", "Deployment '"+instance.GetDeploymentMetadata().Name+"' is created.") + logger.Info("STATUS: Deployment " + instance.GetDeploymentMetadata().Name + " is created.") return nil } @@ -70,6 +74,8 @@ func (r *CodeEditorReconciler) createService(ctx context.Context, instance *robo return err } + r.Recorder.Event(instance, "Normal", "Created", "Service '"+instance.GetDeploymentMetadata().Name+"' is created.") + logger.Info("STATUS: Service " + instance.GetServiceMetadata().Name + " is created.") return nil } @@ -90,6 +96,8 @@ func (r *CodeEditorReconciler) createIngress(ctx context.Context, instance *robo return err } + r.Recorder.Event(instance, "Normal", "Created", "Ingress '"+instance.GetDeploymentMetadata().Name+"' is created.") + logger.Info("STATUS: Ingress " + instance.GetIngressMetadata().Name + " is created.") return nil } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go index 655e4ac3..c4da573a 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/update.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/update.go @@ -27,6 +27,8 @@ func (r *CodeEditorReconciler) updateDeployment(ctx context.Context, instance *r return err } + r.Recorder.Event(instance, "Normal", "Updated", "Deployment '"+instance.GetDeploymentMetadata().Name+"' is updated to sync resources.") + logger.Info("STATUS: Deployment " + deployment.Name + " is updated.") return nil } @@ -45,6 +47,8 @@ func (r *CodeEditorReconciler) updateService(ctx context.Context, instance *robo return err } + r.Recorder.Event(instance, "Normal", "Updated", "Service '"+instance.GetServiceMetadata().Name+"' is updated to sync resources.") + logger.Info("STATUS: Service " + service.Name + " is updated.") return nil } @@ -63,6 +67,8 @@ func (r *CodeEditorReconciler) updateIngress(ctx context.Context, instance *robo return err } - logger.Info("STATUS: Ingress " + ingress.Name + " is updated.") + r.Recorder.Event(instance, "Normal", "Updated", "Ingress '"+instance.GetIngressMetadata().Name+"' is updated to sync resources.") + + logger.Info("STATUS: Ingress " + instance.GetIngressMetadata().Name + " is updated.") return nil } From 44860d8c540eb3aebc943c2f01ba039b9c9a87aa Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Wed, 20 Mar 2024 14:34:40 +0300 Subject: [PATCH 47/49] refactor(deployment): set container statuses --- pkg/api/roboscale.io/v1alpha2/phases.go | 7 ++--- .../v1alpha2/toolkit/code_editor/check.go | 26 +++++++++++++++++++ .../v1alpha2/toolkit/code_editor/handle.go | 4 --- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pkg/api/roboscale.io/v1alpha2/phases.go b/pkg/api/roboscale.io/v1alpha2/phases.go index 3aa9c20f..90607750 100644 --- a/pkg/api/roboscale.io/v1alpha2/phases.go +++ b/pkg/api/roboscale.io/v1alpha2/phases.go @@ -24,9 +24,6 @@ const ( type CodeEditorPhase string const ( - CodeEditorPhaseCreatingPVCs CodeEditorPhase = "CreatingPVCs" - CodeEditorPhaseCreatingDeployment CodeEditorPhase = "CreatingDeployment" - CodeEditorPhaseCreatingService CodeEditorPhase = "CreatingService" - CodeEditorPhaseCreatingIngress CodeEditorPhase = "CreatingIngress" - CodeEditorPhaseReady CodeEditorPhase = "Ready" + CodeEditorPhaseConfiguringResources CodeEditorPhase = "ConfiguringResources" + CodeEditorPhaseReady CodeEditorPhase = "Ready" ) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index d1678705..35c406e4 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -15,7 +15,10 @@ import ( corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" k8sErr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" ) func (r *CodeEditorReconciler) reconcileCheckPVCs(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { @@ -114,6 +117,29 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins instance.Status.WorkloadUpdateNeeded = false } + // update container statuses + newReq, err := labels.NewRequirement(internal.CODE_EDITOR_CONTAINER_SELECTOR_LABEL_KEY, selection.In, []string{instance.Name}) + if err != nil { + return err + } + podSelector := labels.NewSelector().Add([]labels.Requirement{*newReq}...) + + podList := corev1.PodList{} + err = r.List(ctx, &podList, &client.ListOptions{ + LabelSelector: podSelector, + }) + if err != nil && k8sErr.IsNotFound(err) { + instance.Status.DeploymentStatus.ContainerStatuses = []corev1.ContainerStatus{} + } else if err != nil { + return err + } else { + containerStatuses := []corev1.ContainerStatus{} + for _, pod := range podList.Items { + containerStatuses = append(containerStatuses, pod.Status.ContainerStatuses...) + } + instance.Status.DeploymentStatus.ContainerStatuses = containerStatuses + } + instance.Status.DeploymentStatus.Resource.Created = true reference.SetReference(&instance.Status.DeploymentStatus.Resource.Reference, deploymentQuery.TypeMeta, deploymentQuery.ObjectMeta) } diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go b/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go index 448c0314..8441ee3c 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/handle.go @@ -13,7 +13,6 @@ func (r *CodeEditorReconciler) reconcileHandlePVCs(ctx context.Context, instance for key, pvcStatus := range instance.Status.PVCStatuses { if !pvcStatus.Resource.Created { - instance.Status.Phase = robotv1alpha2.CodeEditorPhaseCreatingPVCs err := r.createPersistentVolumeClaim(ctx, instance, key) if err != nil { return err @@ -46,7 +45,6 @@ func (r *CodeEditorReconciler) reconcileHandleDeployment(ctx context.Context, in if volumesReady && !instance.Status.DeploymentStatus.Resource.Created { - instance.Status.Phase = robotv1alpha2.CodeEditorPhaseCreatingDeployment err := r.createDeployment(ctx, instance) if err != nil { return err @@ -68,7 +66,6 @@ func (r *CodeEditorReconciler) reconcileHandleService(ctx context.Context, insta if !instance.Status.ServiceStatus.Resource.Created { - instance.Status.Phase = robotv1alpha2.CodeEditorPhaseCreatingService err := r.createService(ctx, instance) if err != nil { return err @@ -90,7 +87,6 @@ func (r *CodeEditorReconciler) reconcileHandleIngress(ctx context.Context, insta if instance.Spec.Ingress && !instance.Status.IngressStatus.Created { - instance.Status.Phase = robotv1alpha2.CodeEditorPhaseCreatingIngress err := r.createIngress(ctx, instance) if err != nil { return err From 730fe878abc85fa60439ede8866d14eb37dc4b74 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Wed, 20 Mar 2024 16:36:33 +0300 Subject: [PATCH 48/49] refactor(codeeditor): calculate object state --- .../v1alpha2/toolkit/code_editor/check.go | 7 +++--- .../code_editor/codeeditor_controller.go | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 35c406e4..39638ca8 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -238,9 +238,10 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan } - // TODO: Wait for pod to be in 'Running' state. - if codeEditorURL, ok := urls[internal.CODE_EDITOR_PORT_NAME]; ok && !reflect.DeepEqual(instance.Status.ServiceStatus.URLs, urls) { - r.Recorder.Event(instance, "Normal", "Ready", "CodeEditor is accessible over the URL '"+codeEditorURL+"'.") + if codeEditorURL, ok := urls[internal.CODE_EDITOR_PORT_NAME]; ok { + if lastCodeEditorURL, ok := instance.Status.ServiceStatus.URLs[internal.CODE_EDITOR_PORT_NAME]; (ok && !reflect.DeepEqual(lastCodeEditorURL, codeEditorURL)) || !ok { + r.Recorder.Event(instance, "Normal", "Ready", "CodeEditor is accessible over the URL '"+codeEditorURL+"'.") + } } instance.Status.ServiceStatus.URLs = urls diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index 706b5a66..e786a466 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -166,6 +166,31 @@ func (r *CodeEditorReconciler) reconcileCheckResources(ctx context.Context, inst return err } + err = r.reconcileCalculatePhase(ctx, instance) + if err != nil { + return err + } + + return nil +} + +func (r *CodeEditorReconciler) reconcileCalculatePhase(ctx context.Context, instance *robotv1alpha2.CodeEditor) error { + + containersReady := true + if len(instance.Status.DeploymentStatus.ContainerStatuses) > 0 { + for _, cStatus := range instance.Status.DeploymentStatus.ContainerStatuses { + containersReady = containersReady && cStatus.Ready + } + } else { + containersReady = false + } + + if containersReady && instance.Status.ServiceStatus.Resource.Created && (instance.Spec.Ingress == instance.Status.IngressStatus.Created) { + instance.Status.Phase = robotv1alpha2.CodeEditorPhaseReady + } else { + instance.Status.Phase = robotv1alpha2.CodeEditorPhaseConfiguringResources + } + return nil } From 0ac02de4b03c5af18439c3484d44c1baefc4a963 Mon Sep 17 00:00:00 2001 From: tunahanertekin Date: Wed, 20 Mar 2024 17:00:07 +0300 Subject: [PATCH 49/49] refactor(event): generate url event in phase calculation --- pkg/controllers/v1alpha2/toolkit/code_editor/check.go | 10 +++------- .../toolkit/code_editor/codeeditor_controller.go | 4 ++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go index 39638ca8..2c335168 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/check.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/check.go @@ -87,6 +87,8 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins actualImage := deploymentQuery.Spec.Template.Spec.Containers[0].Image + imageSynced := reflect.DeepEqual(desiredImage, actualImage) + remoteConfigSynced := (instance.Spec.Remote && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Hostname, instance.Name) && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Subdomain, instance.Name)) || (!instance.Spec.Remote && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Hostname, "") && reflect.DeepEqual(deploymentQuery.Spec.Template.Spec.Subdomain, "")) @@ -105,7 +107,7 @@ func (r *CodeEditorReconciler) reconcileCheckDeployment(ctx context.Context, ins portSynced := reflect.DeepEqual(desiredPort, actualPort) - if !reflect.DeepEqual(desiredImage, actualImage) || + if !imageSynced || !remoteConfigSynced || !volumeMountsSynced || !portSynced || @@ -238,12 +240,6 @@ func (r *CodeEditorReconciler) reconcileCheckService(ctx context.Context, instan } - if codeEditorURL, ok := urls[internal.CODE_EDITOR_PORT_NAME]; ok { - if lastCodeEditorURL, ok := instance.Status.ServiceStatus.URLs[internal.CODE_EDITOR_PORT_NAME]; (ok && !reflect.DeepEqual(lastCodeEditorURL, codeEditorURL)) || !ok { - r.Recorder.Event(instance, "Normal", "Ready", "CodeEditor is accessible over the URL '"+codeEditorURL+"'.") - } - } - instance.Status.ServiceStatus.URLs = urls instance.Status.ServiceStatus.Resource.Created = true reference.SetReference(&instance.Status.ServiceStatus.Resource.Reference, serviceQuery.TypeMeta, serviceQuery.ObjectMeta) diff --git a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go index e786a466..febfd29f 100644 --- a/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go +++ b/pkg/controllers/v1alpha2/toolkit/code_editor/codeeditor_controller.go @@ -19,6 +19,7 @@ package code_editor import ( "context" + "github.com/robolaunch/robot-operator/internal" robotErr "github.com/robolaunch/robot-operator/internal/error" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -186,6 +187,9 @@ func (r *CodeEditorReconciler) reconcileCalculatePhase(ctx context.Context, inst } if containersReady && instance.Status.ServiceStatus.Resource.Created && (instance.Spec.Ingress == instance.Status.IngressStatus.Created) { + if codeEditorURL, ok := instance.Status.ServiceStatus.URLs[internal.CODE_EDITOR_PORT_NAME]; ok && instance.Status.Phase != robotv1alpha2.CodeEditorPhaseReady { + r.Recorder.Event(instance, "Normal", "Ready", "CodeEditor is accessible over the URL '"+codeEditorURL+"'.") + } instance.Status.Phase = robotv1alpha2.CodeEditorPhaseReady } else { instance.Status.Phase = robotv1alpha2.CodeEditorPhaseConfiguringResources