Skip to content

Commit

Permalink
Support for annotations (#6809)
Browse files Browse the repository at this point in the history
* Add annotations to clusters

Signed-off-by: Waleed Malik <[email protected]>

* Annotation handling for nodes and clusters

Signed-off-by: Waleed Malik <[email protected]>

* Don't show hidden annotations

Signed-off-by: Waleed Malik <[email protected]>

* Annotations for cluster templates

Signed-off-by: Waleed Malik <[email protected]>

* Force close the SSH keys dropdown in e2e tests

* Fix unit tests

Signed-off-by: Waleed Malik <[email protected]>

* Handle empty annotation values for admin setting defaults

---------

Signed-off-by: Waleed Malik <[email protected]>
Co-authored-by: Waseem Abbas <[email protected]>
  • Loading branch information
ahmedwaleedmalik and Waseem826 authored Sep 11, 2024
1 parent 8a66036 commit c0bf02c
Show file tree
Hide file tree
Showing 50 changed files with 941 additions and 83 deletions.
64 changes: 52 additions & 12 deletions modules/api/cmd/kubermatic-api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -11787,13 +11787,6 @@
],
"operationId": "listAWSSizesNoCredentialsV2",
"parameters": [
{
"type": "string",
"x-go-name": "Architecture",
"description": "architecture query parameter. Supports: arm64 and x64 types.",
"name": "architecture",
"in": "query"
},
{
"type": "string",
"x-go-name": "ProjectID",
Expand All @@ -11807,6 +11800,13 @@
"name": "cluster_id",
"in": "path",
"required": true
},
{
"type": "string",
"x-go-name": "Architecture",
"description": "architecture query parameter. Supports: arm64 and x64 types.",
"name": "architecture",
"in": "query"
}
],
"responses": {
Expand Down Expand Up @@ -12321,11 +12321,6 @@
],
"operationId": "listKubeVirtInstancetypesNoCredentials",
"parameters": [
{
"type": "string",
"name": "DatacenterName",
"in": "header"
},
{
"type": "string",
"x-go-name": "ProjectID",
Expand All @@ -12339,6 +12334,11 @@
"name": "cluster_id",
"in": "path",
"required": true
},
{
"type": "string",
"name": "DatacenterName",
"in": "header"
}
],
"responses": {
Expand Down Expand Up @@ -27274,6 +27274,29 @@
},
"x-go-package": "k8c.io/dashboard/v2/pkg/api/v1"
},
"AnnotationSettings": {
"type": "object",
"title": "AnnotationSettings is the settings for the annotations.",
"properties": {
"hiddenAnnotations": {
"description": "HiddenAnnotations are the annotations that are hidden from the user in the UI.\n+optional",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "HiddenAnnotations"
},
"protectedAnnotations": {
"description": "ProtectedAnnotations are the annotations that are visible in the UI but cannot be added or modified by the user.\n+optional",
"type": "array",
"items": {
"type": "string"
},
"x-go-name": "ProtectedAnnotations"
}
},
"x-go-package": "k8c.io/kubermatic/v2/pkg/apis/kubermatic/v1"
},
"Anything": {
"description": "Anything is a struct wrapper around a field of type `interface{}`\nthat plays nicely with controller-gen\n+kubebuilder:object:generate=false\n+kubebuilder:validation:Type=\"\"",
"type": "object",
Expand Down Expand Up @@ -29415,6 +29438,13 @@
"type": "object",
"title": "ClusterTemplateInfo represents a ClusterTemplateInfo object.",
"properties": {
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"x-go-name": "Annotations"
},
"credential": {
"description": "indicates the preset name",
"type": "string",
Expand Down Expand Up @@ -32894,6 +32924,9 @@
},
"x-go-name": "AllowedOperatingSystems"
},
"annotations": {
"$ref": "#/definitions/AnnotationSettings"
},
"cleanupOptions": {
"$ref": "#/definitions/CleanupOptions"
},
Expand Down Expand Up @@ -35538,6 +35571,13 @@
"versions"
],
"properties": {
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"x-go-name": "Annotations"
},
"cloud": {
"$ref": "#/definitions/NodeCloudSpec"
},
Expand Down
2 changes: 2 additions & 0 deletions modules/api/pkg/api/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1601,6 +1601,8 @@ type NodeSpec struct {
// It will be applied to Nodes allowing users run their apps on specific Node using labelSelector.
// required: false
Labels map[string]string `json:"labels,omitempty"`
// required: false
Annotations map[string]string `json:"annotations,omitempty"`
// List of taints to set on new nodes
Taints []TaintSpec `json:"taints,omitempty"`
}
Expand Down
4 changes: 4 additions & 0 deletions modules/api/pkg/api/v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ type ClusterTemplate struct {
// ClusterTemplateInfo represents a ClusterTemplateInfo object.
type ClusterTemplateInfo struct {
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
InheritedLabels map[string]string `json:"inheritedLabels,omitempty"`
// indicates the preset name
Credential string `json:"credential,omitempty"`
Expand Down Expand Up @@ -2193,6 +2194,9 @@ type GlobalSettings struct {

// StaticLabels are a list of labels that can be used for the clusters.
StaticLabels []kubermaticv1.StaticLabel `json:"staticLabels,omitempty"`

// Annotations are the settings for the annotations in KKP UI.
Annotations kubermaticv1.AnnotationSettings `json:"annotations,omitempty"`
}

// VSphereTagCategory is the object representing a vsphere tag category.
Expand Down
3 changes: 3 additions & 0 deletions modules/api/pkg/handler/common/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ func GenerateCluster(
partialCluster.Labels = make(map[string]string)
}
partialCluster.Annotations = make(map[string]string)
if body.Cluster.Annotations != nil {
partialCluster.Annotations = body.Cluster.Annotations
}

credentialName := body.Cluster.Credential
if len(credentialName) > 0 {
Expand Down
6 changes: 4 additions & 2 deletions modules/api/pkg/handler/common/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ func OutputMachineDeployment(md *clusterv1alpha1.MachineDeployment) (*apiv1.Node
Spec: apiv1.NodeDeploymentSpec{
Replicas: *md.Spec.Replicas,
Template: apiv1.NodeSpec{
Labels: label.FilterLabels(label.NodeDeploymentResourceType, md.Spec.Template.Spec.Labels),
Taints: taints,
Labels: label.FilterLabels(label.NodeDeploymentResourceType, md.Spec.Template.Spec.Labels),
Annotations: md.Spec.Template.Spec.Annotations,
Taints: taints,
Versions: apiv1.NodeVersionInfo{
Kubelet: md.Spec.Template.Spec.Versions.Kubelet,
},
Expand Down Expand Up @@ -762,6 +763,7 @@ func outputMachine(machine *clusterv1alpha1.Machine, node *corev1.Node, hideInit
Name: displayName,
DeletionTimestamp: deletionTimestamp,
CreationTimestamp: apiv1.NewTime(machine.CreationTimestamp.Time),
Annotations: machine.Annotations,
},
Spec: apiv1.NodeSpec{
Versions: apiv1.NodeVersionInfo{
Expand Down
23 changes: 23 additions & 0 deletions modules/api/pkg/handler/v1/admin/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"io"
"net/http"
"slices"

jsonpatch "github.com/evanphx/json-patch"
"github.com/go-kit/kit/endpoint"
Expand All @@ -32,6 +33,7 @@ import (
kubermaticv1 "k8c.io/kubermatic/v2/pkg/apis/kubermatic/v1"
utilerrors "k8c.io/kubermatic/v2/pkg/util/errors"

corev1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
)

Expand Down Expand Up @@ -151,8 +153,11 @@ func convertAPISettingsToSettingsSpec(settings *apiv2.GlobalSettings) (kubermati
DisableChangelogPopup: settings.DisableChangelogPopup,
WebTerminalOptions: settings.WebTerminalOptions,
StaticLabels: settings.StaticLabels,
Annotations: settings.Annotations,
}

addDefaultAnnotations(&s.Annotations)

if settings.DefaultProjectResourceQuota != nil {
crdQuota, err := apiv2.ConvertToCRDQuota(settings.DefaultProjectResourceQuota.Quota)
if err != nil {
Expand Down Expand Up @@ -199,8 +204,11 @@ func ConvertCRDSettingsToAPISettingsSpec(settings *kubermaticv1.SettingSpec) api
DisableChangelogPopup: settings.DisableChangelogPopup,
WebTerminalOptions: settings.WebTerminalOptions,
StaticLabels: settings.StaticLabels,
Annotations: settings.Annotations,
}

addDefaultAnnotations(&s.Annotations)

if settings.DefaultProjectResourceQuota != nil {
apiQuota := apiv2.ConvertToAPIQuota(settings.DefaultProjectResourceQuota.Quota)
s.DefaultProjectResourceQuota = &apiv2.ProjectResourceQuota{
Expand All @@ -210,3 +218,18 @@ func ConvertCRDSettingsToAPISettingsSpec(settings *kubermaticv1.SettingSpec) api

return s
}

// These annotations are forced by KKP and cannot be changed by the user. Since KKP API is responsible for managing KubermaticSettings at the moment,
// we need to make sure that these annotations are always present.
// This might change with https://github.com/kubermatic/kubermatic/issues/13671 in the future.
func addDefaultAnnotations(annotations *kubermaticv1.AnnotationSettings) {
if !slices.Contains(annotations.ProtectedAnnotations, kubermaticv1.PresetNameAnnotation) {
annotations.ProtectedAnnotations = append(annotations.ProtectedAnnotations, kubermaticv1.PresetNameAnnotation)
}

if len(annotations.HiddenAnnotations) == 0 {
annotations.HiddenAnnotations = append(annotations.HiddenAnnotations, corev1.LastAppliedConfigAnnotation)
annotations.HiddenAnnotations = append(annotations.HiddenAnnotations, kubermaticv1.InitialApplicationInstallationsRequestAnnotation)
annotations.HiddenAnnotations = append(annotations.HiddenAnnotations, kubermaticv1.InitialMachineDeploymentRequestAnnotation)
}
}
8 changes: 4 additions & 4 deletions modules/api/pkg/handler/v1/admin/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ func TestGetGlobalSettings(t *testing.T) {
// scenario 1
{
name: "scenario 1: user gets settings first time",
expectedResponse: `{"customLinks":[],"defaultNodeCount":2,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":false,"enableDashboard":true,"enableOIDCKubeconfig":false,"userProjectsLimit":0,"restrictProjectCreation":false,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{},"opaOptions":{},"mlaOptions":{},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"webTerminalOptions":{"enabled":false},"machineDeploymentVMResourceQuota":{"minCPU":2,"maxCPU":32,"minRAM":2,"maxRAM":128,"enableGPU":false},"machineDeploymentOptions":{}}`,
expectedResponse: `{"customLinks":[],"defaultNodeCount":2,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":false,"enableDashboard":true,"enableOIDCKubeconfig":false,"userProjectsLimit":0,"restrictProjectCreation":false,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{},"opaOptions":{},"mlaOptions":{},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"webTerminalOptions":{"enabled":false},"machineDeploymentVMResourceQuota":{"minCPU":2,"maxCPU":32,"minRAM":2,"maxRAM":128,"enableGPU":false},"machineDeploymentOptions":{},"annotations":{"hiddenAnnotations":["kubectl.kubernetes.io/last-applied-configuration","kubermatic.io/initial-application-installations-request","kubermatic.io/initial-machinedeployment-request"],"protectedAnnotations":["presetName"]}}`,
httpStatus: http.StatusOK,
existingKubermaticObjs: []ctrlruntimeclient.Object{genUser("Bob", "[email protected]", true)},
existingAPIUser: test.GenDefaultAPIUser(),
},
// scenario 2
{
name: "scenario 2: user gets existing global settings",
expectedResponse: `{"customLinks":[{"label":"label","url":"url:label","icon":"icon","location":"EU"}],"defaultNodeCount":5,"displayDemoInfo":true,"displayAPIDocs":true,"displayTermsOfService":true,"enableDashboard":false,"enableShareCluster":true,"enableOIDCKubeconfig":false,"enableEtcdBackup":true,"userProjectsLimit":0,"restrictProjectCreation":false,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{"enabled":true,"enforced":true},"opaOptions":{"enabled":true,"enforced":true},"mlaOptions":{"loggingEnabled":true,"loggingEnforced":true,"monitoringEnabled":true,"monitoringEnforced":true},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"defaultQuota":{"quota":{"cpu":2,"memory":5,"storage":10}},"machineDeploymentOptions":{}}`,
expectedResponse: `{"customLinks":[{"label":"label","url":"url:label","icon":"icon","location":"EU"}],"defaultNodeCount":5,"displayDemoInfo":true,"displayAPIDocs":true,"displayTermsOfService":true,"enableDashboard":false,"enableShareCluster":true,"enableOIDCKubeconfig":false,"enableEtcdBackup":true,"userProjectsLimit":0,"restrictProjectCreation":false,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{"enabled":true,"enforced":true},"opaOptions":{"enabled":true,"enforced":true},"mlaOptions":{"loggingEnabled":true,"loggingEnforced":true,"monitoringEnabled":true,"monitoringEnforced":true},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"defaultQuota":{"quota":{"cpu":2,"memory":5,"storage":10}},"machineDeploymentOptions":{},"annotations":{"hiddenAnnotations":["kubectl.kubernetes.io/last-applied-configuration","kubermatic.io/initial-application-installations-request","kubermatic.io/initial-machinedeployment-request"],"protectedAnnotations":["presetName"]}}`,
httpStatus: http.StatusOK,
existingKubermaticObjs: []ctrlruntimeclient.Object{genUser("Bob", "[email protected]", true),
test.GenDefaultGlobalSettings()},
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestUpdateGlobalSettings(t *testing.T) {
{
name: "scenario 2: authorized user updates default settings",
body: `{"customLinks":[{"label":"label","url":"url:label","icon":"icon","location":"EU"}],"cleanupOptions":{"enabled":true,"enforced":true},"defaultNodeCount":100,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":true,"machineDeploymentOptions":{}}`,
expectedResponse: `{"customLinks":[{"label":"label","url":"url:label","icon":"icon","location":"EU"}],"defaultNodeCount":100,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":true,"enableDashboard":true,"enableOIDCKubeconfig":false,"userProjectsLimit":0,"restrictProjectCreation":false,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{"enabled":true,"enforced":true},"opaOptions":{},"mlaOptions":{},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"webTerminalOptions":{"enabled":false},"machineDeploymentVMResourceQuota":{"minCPU":2,"maxCPU":32,"minRAM":2,"maxRAM":128,"enableGPU":false},"machineDeploymentOptions":{}}`,
expectedResponse: `{"customLinks":[{"label":"label","url":"url:label","icon":"icon","location":"EU"}],"defaultNodeCount":100,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":true,"enableDashboard":true,"enableOIDCKubeconfig":false,"userProjectsLimit":0,"restrictProjectCreation":false,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{"enabled":true,"enforced":true},"opaOptions":{},"mlaOptions":{},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"webTerminalOptions":{"enabled":false},"machineDeploymentVMResourceQuota":{"minCPU":2,"maxCPU":32,"minRAM":2,"maxRAM":128,"enableGPU":false},"machineDeploymentOptions":{},"annotations":{"hiddenAnnotations":["kubectl.kubernetes.io/last-applied-configuration","kubermatic.io/initial-application-installations-request","kubermatic.io/initial-machinedeployment-request"],"protectedAnnotations":["presetName"]}}`,
httpStatus: http.StatusOK,
existingKubermaticObjs: []ctrlruntimeclient.Object{genUser("Bob", "[email protected]", true)},
existingAPIUser: test.GenDefaultAPIUser(),
Expand All @@ -116,7 +116,7 @@ func TestUpdateGlobalSettings(t *testing.T) {
{
name: "scenario 3: authorized user updates existing global settings",
body: `{"customLinks":[],"cleanupOptions":{"enabled":true,"enforced":true},"defaultNodeCount":100,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":true,"userProjectsLimit":10,"restrictProjectCreation":true,"restrictProjectDeletion":false,"defaultQuota":{"cpu":4,"storage":12},"machineDeploymentOptions":{}}`,
expectedResponse: `{"customLinks":[],"defaultNodeCount":100,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":true,"enableDashboard":false,"enableShareCluster":true,"enableOIDCKubeconfig":false,"enableEtcdBackup":true,"userProjectsLimit":10,"restrictProjectCreation":true,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{"enabled":true,"enforced":true},"opaOptions":{"enabled":true,"enforced":true},"mlaOptions":{"loggingEnabled":true,"loggingEnforced":true,"monitoringEnabled":true,"monitoringEnforced":true},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"defaultQuota":{"quota":{"cpu":2,"memory":5,"storage":10}},"machineDeploymentOptions":{}}`,
expectedResponse: `{"customLinks":[],"defaultNodeCount":100,"displayDemoInfo":false,"displayAPIDocs":false,"displayTermsOfService":true,"enableDashboard":false,"enableShareCluster":true,"enableOIDCKubeconfig":false,"enableEtcdBackup":true,"userProjectsLimit":10,"restrictProjectCreation":true,"restrictProjectDeletion":false,"enableExternalClusterImport":true,"cleanupOptions":{"enabled":true,"enforced":true},"opaOptions":{"enabled":true,"enforced":true},"mlaOptions":{"loggingEnabled":true,"loggingEnforced":true,"monitoringEnabled":true,"monitoringEnforced":true},"mlaAlertmanagerPrefix":"","mlaGrafanaPrefix":"","notifications":{},"providerConfiguration":{"openStack":{},"vmwareCloudDirector":{}},"defaultQuota":{"quota":{"cpu":2,"memory":5,"storage":10}},"machineDeploymentOptions":{},"annotations":{"hiddenAnnotations":["kubectl.kubernetes.io/last-applied-configuration","kubermatic.io/initial-application-installations-request","kubermatic.io/initial-machinedeployment-request"],"protectedAnnotations":["presetName"]}}`,
httpStatus: http.StatusOK,
existingKubermaticObjs: []ctrlruntimeclient.Object{genUser("Bob", "[email protected]", true),
test.GenDefaultGlobalSettings()},
Expand Down
17 changes: 2 additions & 15 deletions modules/api/pkg/handler/v2/cluster_template/cluster_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ func convertInternalClusterTemplatetoExternal(template *kubermaticv1.ClusterTemp
ID: template.Name,
Name: template.Spec.HumanReadableName,
CreationTimestamp: apiv1.NewTime(template.CreationTimestamp.Time),
Annotations: template.Annotations,
DeletionTimestamp: func() *apiv1.Time {
if template.DeletionTimestamp != nil {
deletionTimestamp := apiv1.NewTime(template.DeletionTimestamp.Time)
Expand All @@ -778,6 +779,7 @@ func convertInternalClusterTemplatetoExternal(template *kubermaticv1.ClusterTemp
Scope: template.Labels[kubermaticv1.ClusterTemplateScopeLabelKey],
Cluster: &apiv2.ClusterTemplateInfo{
Labels: label.FilterLabels(label.ClusterResourceType, template.ClusterLabels),
Annotations: template.Annotations,
InheritedLabels: template.InheritedClusterLabels,
Credential: template.Credential,
Spec: apiv1.ClusterSpec{
Expand Down Expand Up @@ -815,21 +817,6 @@ func convertInternalClusterTemplatetoExternal(template *kubermaticv1.ClusterTemp
Applications: apps,
}

ct.Annotations = make(map[string]string)
if template.Annotations != nil {
// Add preset annotations
if value, ok := template.Annotations[kubermaticv1.PresetNameAnnotation]; ok {
ct.Annotations[kubermaticv1.PresetNameAnnotation] = value
}
if value, ok := template.Annotations[kubermaticv1.PresetInvalidatedAnnotation]; ok {
ct.Annotations[kubermaticv1.PresetInvalidatedAnnotation] = value
}
// Add initial CNI values request annotation
if value, ok := template.Annotations[kubermaticv1.InitialCNIValuesRequestAnnotation]; ok {
ct.Annotations[kubermaticv1.InitialCNIValuesRequestAnnotation] = value
}
}

if len(template.UserSSHKeys) > 0 {
for _, sshKey := range template.UserSSHKeys {
ct.UserSSHKeys = append(ct.UserSSHKeys, apiv2.ClusterTemplateSSHKey{
Expand Down
Loading

0 comments on commit c0bf02c

Please sign in to comment.