From 461535a4400ddf2ad51a6980da8c8a19a6bb178f Mon Sep 17 00:00:00 2001 From: Sebastian Florek Date: Fri, 7 Feb 2025 13:41:23 +0100 Subject: [PATCH] extend cluster iso images information --- api/v1alpha2/cloudconfig_types.go | 42 +++++++++++++++++ api/v1alpha2/osartifact_types.go | 8 +++- charts/osartifact/Chart.yaml | 2 +- charts/osartifact/templates/osartifact.yaml | 3 ++ charts/osartifact/values.yaml | 3 ++ controllers/osartifact_controller.go | 52 ++++++++++++++++++--- pkg/client/console.go | 22 ++++++++- 7 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 api/v1alpha2/cloudconfig_types.go diff --git a/api/v1alpha2/cloudconfig_types.go b/api/v1alpha2/cloudconfig_types.go new file mode 100644 index 0000000..46ff4c5 --- /dev/null +++ b/api/v1alpha2/cloudconfig_types.go @@ -0,0 +1,42 @@ +package v1alpha2 + +type CloudConfig struct { + Users Users `json:"users,omitempty"` + Plural Plural `json:"plural,omitempty"` +} + +type Users []User + +func (in Users) FirstUser() *User { + for _, user := range in { + return &user + } + + return nil +} + +type User struct { + Name string `json:"name"` + Passwd string `json:"passwd"` +} + +func (in *User) GetName() *string { + if in == nil { + return nil + } + + return &in.Name +} + +func (in *User) GetPasswd() *string { + if in == nil { + return nil + } + + return &in.Passwd +} + +type Plural struct { + Token string `json:"token"` + URL string `json:"url"` +} diff --git a/api/v1alpha2/osartifact_types.go b/api/v1alpha2/osartifact_types.go index 662b92e..c4fc3a1 100644 --- a/api/v1alpha2/osartifact_types.go +++ b/api/v1alpha2/osartifact_types.go @@ -30,20 +30,24 @@ const ( // OSArtifactSpec defines the desired state of OSArtifact type OSArtifactSpec struct { - // There are 3 ways to specify a Kairos image: - // Points to a prepared kairos image (e.g. a released one) + // +optional ImageName string `json:"imageName,omitempty"` + // +optional ISO bool `json:"iso,omitempty"` // +kubebuilder:validation:Type:=string // +kubebuilder:validation:Enum:=rpi3;rpi4 + // +optional Model *Model `json:"model,omitempty"` // +optional CloudConfigRef *corev1.SecretKeySelector `json:"cloudConfigRef,omitempty"` + // +optional + Project string `json:"project,omitempty"` + // +optional Bundles []string `json:"bundles,omitempty"` diff --git a/charts/osartifact/Chart.yaml b/charts/osartifact/Chart.yaml index 7d891c5..1a6d610 100644 --- a/charts/osartifact/Chart.yaml +++ b/charts/osartifact/Chart.yaml @@ -5,4 +5,4 @@ maintainers: - name: Plural email: support@plural.sh type: application -version: 0.8.2 \ No newline at end of file +version: 0.9.0 \ No newline at end of file diff --git a/charts/osartifact/templates/osartifact.yaml b/charts/osartifact/templates/osartifact.yaml index 662a420..be6b467 100644 --- a/charts/osartifact/templates/osartifact.yaml +++ b/charts/osartifact/templates/osartifact.yaml @@ -22,6 +22,9 @@ spec: cloudConfigRef: name: {{ include "osartifact.fullname" . }}-config key: cloud-config.yaml + {{- if .Values.project }} + project: {{ .Values.project }} + {{- end }} exporter: serviceAccount: {{ include "osartifact.fullname" . }} {{- with .Values.exporter.extraEnvVars }} diff --git a/charts/osartifact/values.yaml b/charts/osartifact/values.yaml index d34458e..93de827 100644 --- a/charts/osartifact/values.yaml +++ b/charts/osartifact/values.yaml @@ -7,6 +7,9 @@ image: quay.io/kairos/alpine:3.19-standard-arm64-rpi4-v3.2.4-k3sv1.31.3-k3s1 # Target device. Currently supported values: rpi4 device: rpi4 +# Plural project name this image should be tied to. +project: ~ + # WiFi configuration. It will attempt the connection on the first boot. wifi: enabled: false diff --git a/controllers/osartifact_controller.go b/controllers/osartifact_controller.go index 699bcb0..682c81f 100644 --- a/controllers/osartifact_controller.go +++ b/controllers/osartifact_controller.go @@ -18,11 +18,10 @@ package controllers import ( "context" + "encoding/json" "fmt" "time" - osbuilder "github.com/kairos-io/osbuilder/api/v1alpha2" - consoleclient "github.com/kairos-io/osbuilder/pkg/client" console "github.com/pluralsh/console/go/client" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" @@ -41,6 +40,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + osbuilder "github.com/kairos-io/osbuilder/api/v1alpha2" + consoleclient "github.com/kairos-io/osbuilder/pkg/client" ) const ( @@ -406,7 +408,7 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil } else if job.Spec.Completions != nil { if job.Status.Succeeded > 0 && artifact.Status.Phase == osbuilder.Exporting { artifact.Status.Phase = osbuilder.Ready - if err := r.upsertClusterIsoImage(artifact); err != nil { + if err := r.upsertClusterIsoImage(ctx, artifact); err != nil { artifact.Status.Phase = osbuilder.Error meta.SetStatusCondition(&artifact.Status.Conditions, metav1.Condition{ Type: "Ready", @@ -444,11 +446,25 @@ func (r *OSArtifactReconciler) checkExport(ctx context.Context, artifact *osbuil return requeue, nil } -func (r *OSArtifactReconciler) upsertClusterIsoImage(artifact *osbuilder.OSArtifact) error { +func (r *OSArtifactReconciler) upsertClusterIsoImage(ctx context.Context, artifact *osbuilder.OSArtifact) error { + cloudConfig, err := r.getCloudConfig(ctx, artifact) + if err != nil { + return err + } + + var projectID *string = nil + project, _ := r.ConsoleClient.GetProject(artifact.Spec.Project) + if project != nil { + projectID = &project.ID + } + image := fmt.Sprintf("%s:%s", artifact.Spec.Exporter.Registry.Image.Repository, artifact.Spec.Exporter.Registry.Image.Tag) attr := console.ClusterIsoImageAttributes{ - Image: image, - Registry: artifact.Spec.Exporter.Registry.Name, + Image: image, + Registry: artifact.Spec.Exporter.Registry.Name, + User: cloudConfig.Users.FirstUser().GetName(), + Password: cloudConfig.Users.FirstUser().GetPasswd(), + ProjectID: projectID, } getResponse, err := r.ConsoleClient.GetClusterIsoImage(&image) @@ -518,3 +534,27 @@ func (r *OSArtifactReconciler) CreateConfigMap(ctx context.Context, artifact *os return nil } + +func (r *OSArtifactReconciler) getCloudConfig(ctx context.Context, artifact *osbuilder.OSArtifact) (*osbuilder.CloudConfig, error) { + config := &osbuilder.CloudConfig{} + secret := &corev1.Secret{} + key := client.ObjectKey{ + Namespace: artifact.Namespace, + Name: artifact.Spec.CloudConfigRef.Name, + } + + if err := r.Get(ctx, key, secret); err != nil { + return config, err + } + + configBytes, exists := secret.Data[artifact.Spec.CloudConfigRef.Key] + if !exists { + return config, fmt.Errorf("could not find key %s in secret %s", artifact.Spec.CloudConfigRef.Key, artifact.Spec.CloudConfigRef.Name) + } + + if err := json.Unmarshal(configBytes, config); err != nil { + return config, err + } + + return config, nil +} diff --git a/pkg/client/console.go b/pkg/client/console.go index c4e1f00..52fc4d1 100644 --- a/pkg/client/console.go +++ b/pkg/client/console.go @@ -6,12 +6,13 @@ import ( "net/http" rawclient "github.com/Yamashou/gqlgenc/clientv2" - internalerror "github.com/kairos-io/osbuilder/pkg/errors" - "github.com/kairos-io/osbuilder/pkg/helpers" "github.com/pkg/errors" console "github.com/pluralsh/console/go/client" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" + + internalerror "github.com/kairos-io/osbuilder/pkg/errors" + "github.com/kairos-io/osbuilder/pkg/helpers" ) type client struct { @@ -37,6 +38,7 @@ type Client interface { UpdateClusterIsoImage(id string, attributes console.ClusterIsoImageAttributes) (*console.ClusterIsoImageFragment, error) GetClusterIsoImage(image *string) (*console.ClusterIsoImageFragment, error) DeleteClusterIsoImage(id string) (*console.ClusterIsoImageFragment, error) + GetProject(name string) (*console.ProjectFragment, error) } func (c *client) CreateClusterIsoImage(attributes console.ClusterIsoImageAttributes) (*console.ClusterIsoImageFragment, error) { @@ -82,6 +84,22 @@ func (c *client) GetClusterIsoImage(image *string) (*console.ClusterIsoImageFrag return response.ClusterIsoImage, nil } +func (c *client) GetProject(name string) (*console.ProjectFragment, error) { + response, err := c.consoleClient.GetProject(c.ctx, nil, &name) + if internalerror.IsNotFound(err) { + return nil, apierrors.NewNotFound(schema.GroupResource{}, name) + } + if err == nil && (response == nil || response.Project == nil) { + return nil, apierrors.NewNotFound(schema.GroupResource{}, name) + } + + if response == nil { + return nil, err + } + + return response.Project, nil +} + func GetErrorResponse(err error, methodName string) error { if err == nil { return nil