diff --git a/akp/apis/v1alpha1/cluster.go b/akp/apis/v1alpha1/cluster.go index b399f4c..d82ef82 100644 --- a/akp/apis/v1alpha1/cluster.go +++ b/akp/apis/v1alpha1/cluster.go @@ -25,11 +25,33 @@ type ClusterSpec struct { Data ClusterData `json:"data,omitempty"` } +type Resources struct { + Mem string `json:"mem,omitempty"` + Cpu string `json:"cpu,omitempty"` +} + type ManagedClusterConfig struct { SecretName string `json:"secretName,omitempty"` SecretKey string `json:"secretKey,omitempty"` } +type AutoScalerConfig struct { + ApplicationController *AppControllerAutoScalingConfig `json:"applicationController,omitempty"` + RepoServer *RepoServerAutoScalingConfig `json:"repoServer,omitempty"` +} + +type AppControllerAutoScalingConfig struct { + ResourceMinimum *Resources `json:"resourceMinimum,omitempty"` + ResourceMaximum *Resources `json:"resourceMaximum,omitempty"` +} + +type RepoServerAutoScalingConfig struct { + ResourceMinimum *Resources `json:"resourceMinimum,omitempty"` + ResourceMaximum *Resources `json:"resourceMaximum,omitempty"` + ReplicaMaximum int32 `json:"replicaMaximum,omitempty"` + ReplicaMinimum int32 `json:"replicaMinimum,omitempty"` +} + type ClusterData struct { Size ClusterSize `json:"size,omitempty"` AutoUpgradeDisabled *bool `json:"autoUpgradeDisabled,omitempty"` @@ -42,5 +64,6 @@ type ClusterData struct { EksAddonEnabled *bool `json:"eksAddonEnabled,omitempty"` ManagedClusterConfig *ManagedClusterConfig `json:"managedClusterConfig,omitempty"` - MultiClusterK8SDashboardEnabled *bool `json:"multiClusterK8sDashboardEnabled,omitempty"` + MultiClusterK8SDashboardEnabled *bool `json:"multiClusterK8sDashboardEnabled,omitempty"` + AutoscalerConfig *AutoScalerConfig `json:"autoscalerConfig,omitempty"` } diff --git a/akp/data_source_akp_cluster.go b/akp/data_source_akp_cluster.go index 4d06c00..7f4a2f7 100644 --- a/akp/data_source_akp_cluster.go +++ b/akp/data_source_akp_cluster.go @@ -54,7 +54,7 @@ func (d *AkpClusterDataSource) Read(ctx context.Context, req datasource.ReadRequ return } ctx = httpctx.SetAuthorizationHeader(ctx, d.akpCli.Cred.Scheme(), d.akpCli.Cred.Credential()) - refreshClusterState(ctx, &resp.Diagnostics, d.akpCli.Cli, &data, d.akpCli.OrgId, &resp.State) + refreshClusterState(ctx, &resp.Diagnostics, d.akpCli.Cli, &data, d.akpCli.OrgId, &resp.State, &data) // Save data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } diff --git a/akp/data_source_akp_cluster_schema.go b/akp/data_source_akp_cluster_schema.go index f1db7bb..fa88752 100644 --- a/akp/data_source_akp_cluster_schema.go +++ b/akp/data_source_akp_cluster_schema.go @@ -81,7 +81,7 @@ func getClusterSpecDataSourceAttributes() map[string]schema.Attribute { func getClusterDataDataSourceAttributes() map[string]schema.Attribute { return map[string]schema.Attribute{ "size": schema.StringAttribute{ - MarkdownDescription: "Cluster Size. One of `small`, `medium` or `large`", + MarkdownDescription: "Cluster Size. One of `small`, `medium`, `large`, `custom` or `auto`", Computed: true, }, "auto_upgrade_disabled": schema.BoolAttribute{ @@ -121,6 +121,16 @@ func getClusterDataDataSourceAttributes() map[string]schema.Attribute { MarkdownDescription: "Enable the KubeVision feature on the managed cluster", Computed: true, }, + "auto_agent_size_config": schema.SingleNestedAttribute{ + MarkdownDescription: "Autoscaler config for auto agent size", + Computed: true, + Attributes: getAutoScalerConfigDataSourceAttributes(), + }, + "custom_agent_size_config": schema.SingleNestedAttribute{ + MarkdownDescription: "Custom agent size config", + Computed: true, + Attributes: getCustomAgentSizeConfigDataSourceAttributes(), + }, } } @@ -201,3 +211,114 @@ func getManagedClusterConfigDataSourceAttributes() map[string]schema.Attribute { }, } } + +func getAutoScalerConfigDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "application_controller": schema.SingleNestedAttribute{ + Description: "Application Controller auto scaling config", + Computed: true, + Attributes: getAppControllerAutoScalingConfigDataSourceAttributes(), + }, + "repo_server": schema.SingleNestedAttribute{ + Description: "Repo Server auto scaling config", + Computed: true, + Attributes: getRepoServerAutoScalingConfigDataSourceAttributes(), + }, + } +} + +func getCustomAgentSizeConfigDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "application_controller": schema.SingleNestedAttribute{ + Description: "Application Controller custom agent size config", + Computed: true, + Attributes: getAppControllerCustomAgentSizeConfigDataSourceAttributes(), + }, + "repo_server": schema.SingleNestedAttribute{ + Description: "Repo Server custom agent size config", + Computed: true, + Attributes: getRepoServerCustomAgentSizeConfigDataSourceAttributes(), + }, + } +} + +func getAppControllerCustomAgentSizeConfigDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "memory": schema.StringAttribute{ + Description: "Memory", + Computed: true, + }, + "cpu": schema.StringAttribute{ + Description: "CPU", + Computed: true, + }, + } +} + +func getRepoServerCustomAgentSizeConfigDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "memory": schema.StringAttribute{ + Description: "Memory", + Computed: true, + }, + "cpu": schema.StringAttribute{ + Description: "CPU", + Computed: true, + }, + "replicas": schema.Int64Attribute{ + Description: "Replica", + Computed: true, + }, + } +} + +func getAppControllerAutoScalingConfigDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "resource_minimum": schema.SingleNestedAttribute{ + Description: "Resource minimum", + Computed: true, + Attributes: getResourcesDataSourceAttributes(), + }, + "resource_maximum": schema.SingleNestedAttribute{ + Description: "Resource maximum", + Computed: true, + Attributes: getResourcesDataSourceAttributes(), + }, + } +} + +func getRepoServerAutoScalingConfigDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "resource_minimum": schema.SingleNestedAttribute{ + Description: "Resource minimum", + Computed: true, + Attributes: getResourcesDataSourceAttributes(), + }, + "resource_maximum": schema.SingleNestedAttribute{ + Description: "Resource maximum", + Computed: true, + Attributes: getResourcesDataSourceAttributes(), + }, + "replicas_maximum": schema.Int64Attribute{ + Description: "Replica maximum", + Computed: true, + }, + "replicas_minimum": schema.Int64Attribute{ + Description: "Replica minimum", + Computed: true, + }, + } +} + +func getResourcesDataSourceAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "memory": schema.StringAttribute{ + Description: "Memory", + Computed: true, + }, + "cpu": schema.StringAttribute{ + Description: "CPU", + Computed: true, + }, + } +} diff --git a/akp/data_source_akp_clusters.go b/akp/data_source_akp_clusters.go index a5bdab5..dabbb59 100644 --- a/akp/data_source_akp_clusters.go +++ b/akp/data_source_akp_clusters.go @@ -75,7 +75,7 @@ func (d *AkpClustersDataSource) Read(ctx context.Context, req datasource.ReadReq stateCluster := types.Cluster{ InstanceID: data.InstanceID, } - stateCluster.Update(ctx, &resp.Diagnostics, cluster) + stateCluster.Update(ctx, &resp.Diagnostics, cluster, nil) data.Clusters = append(data.Clusters, stateCluster) } // Save data into Terraform state diff --git a/akp/resource_akp_cluster.go b/akp/resource_akp_cluster.go index 4d5e971..2c5bde5 100644 --- a/akp/resource_akp_cluster.go +++ b/akp/resource_akp_cluster.go @@ -3,10 +3,11 @@ package akp import ( "context" "fmt" - "github.com/pkg/errors" "strings" "time" + "github.com/pkg/errors" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -95,7 +96,7 @@ func (r *AkpClusterResource) Read(ctx context.Context, req resource.ReadRequest, } ctx = httpctx.SetAuthorizationHeader(ctx, r.akpCli.Cred.Scheme(), r.akpCli.Cred.Credential()) - err := refreshClusterState(ctx, &resp.Diagnostics, r.akpCli.Cli, &data, r.akpCli.OrgId, &resp.State) + err := refreshClusterState(ctx, &resp.Diagnostics, r.akpCli.Cli, &data, r.akpCli.OrgId, &resp.State, &data) if err != nil { resp.Diagnostics.AddError("Client Error", err.Error()) } else { @@ -186,12 +187,14 @@ func (r *AkpClusterResource) ImportState(ctx context.Context, req resource.Impor func (r *AkpClusterResource) upsert(ctx context.Context, diagnostics *diag.Diagnostics, plan *types.Cluster, isCreate bool) (*types.Cluster, error) { ctx = httpctx.SetAuthorizationHeader(ctx, r.akpCli.Cred.Scheme(), r.akpCli.Cred.Credential()) apiReq := buildClusterApplyRequest(ctx, diagnostics, plan, r.akpCli.OrgId) + if diagnostics.HasError() { + return nil, nil + } result, err := r.applyInstance(ctx, plan, apiReq, isCreate, r.akpCli.Cli.ApplyInstance, r.upsertKubeConfig) if err != nil { return result, err } - - return result, refreshClusterState(ctx, diagnostics, r.akpCli.Cli, result, r.akpCli.OrgId, nil) + return result, refreshClusterState(ctx, diagnostics, r.akpCli.Cli, result, r.akpCli.OrgId, nil, plan) } func (r *AkpClusterResource) applyInstance(ctx context.Context, plan *types.Cluster, apiReq *argocdv1.ApplyInstanceRequest, isCreate bool, applyInstance func(context.Context, *argocdv1.ApplyInstanceRequest) (*argocdv1.ApplyInstanceResponse, error), upsertKubeConfig func(ctx context.Context, plan *types.Cluster, isCreate bool) error) (*types.Cluster, error) { @@ -240,7 +243,7 @@ func (r *AkpClusterResource) upsertKubeConfig(ctx context.Context, plan *types.C } func refreshClusterState(ctx context.Context, diagnostics *diag.Diagnostics, client argocdv1.ArgoCDServiceGatewayClient, cluster *types.Cluster, - orgID string, state *tfsdk.State) error { + orgID string, state *tfsdk.State, plan *types.Cluster) error { clusterReq := &argocdv1.GetInstanceClusterRequest{ OrganizationId: orgID, InstanceId: cluster.InstanceID.ValueString(), @@ -258,7 +261,7 @@ func refreshClusterState(ctx context.Context, diagnostics *diag.Diagnostics, cli return errors.Wrap(err, "Unable to read Argo CD cluster") } tflog.Debug(ctx, fmt.Sprintf("Get cluster response: %s", clusterResp)) - cluster.Update(ctx, diagnostics, clusterResp.GetCluster()) + cluster.Update(ctx, diagnostics, clusterResp.GetCluster(), plan) return nil } diff --git a/akp/resource_akp_cluster_schema.go b/akp/resource_akp_cluster_schema.go index d64d63b..2836ab8 100644 --- a/akp/resource_akp_cluster_schema.go +++ b/akp/resource_akp_cluster_schema.go @@ -9,8 +9,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -142,7 +144,7 @@ func getClusterSpecAttributes() map[string]schema.Attribute { func getClusterDataAttributes() map[string]schema.Attribute { return map[string]schema.Attribute{ "size": schema.StringAttribute{ - MarkdownDescription: "Cluster Size. One of `small`, `medium` or `large`", + MarkdownDescription: "Cluster Size. One of `small`, `medium`, `large`, `custom` or `auto`", Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -218,6 +220,17 @@ func getClusterDataAttributes() map[string]schema.Attribute { boolplanmodifier.UseStateForUnknown(), }, }, + "auto_agent_size_config": schema.SingleNestedAttribute{ + MarkdownDescription: "Autoscaler config for auto agent size", + Optional: true, + Computed: true, + Attributes: getAutoScalerConfigAttributes(), + }, + "custom_agent_size_config": schema.SingleNestedAttribute{ + MarkdownDescription: "Custom agent size config", + Optional: true, + Attributes: getCustomAgentSizeConfigAttributes(), + }, } } @@ -346,3 +359,174 @@ func getManagedClusterConfigAttributes() map[string]schema.Attribute { }, } } + +func getCustomAgentSizeConfigAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "application_controller": schema.SingleNestedAttribute{ + Description: "Application Controller custom agent size config", + Optional: true, + Attributes: getAppControllerCustomAgentSizeConfigAttributes(), + }, + "repo_server": schema.SingleNestedAttribute{ + Description: "Repo Server custom agent size config", + Optional: true, + Attributes: getRepoServerCustomAgentSizeConfigAttributes(), + }, + } +} + +func getAppControllerCustomAgentSizeConfigAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "cpu": schema.StringAttribute{ + Description: "CPU", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "memory": schema.StringAttribute{ + Description: "Memory", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + } +} + +func getRepoServerCustomAgentSizeConfigAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "cpu": schema.StringAttribute{ + Description: "CPU", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "memory": schema.StringAttribute{ + Description: "Memory", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "replicas": schema.Int64Attribute{ + Description: "Replica", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + } +} + +func getAutoScalerConfigAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "application_controller": schema.SingleNestedAttribute{ + Description: "Application Controller auto scaling config", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: getAppControllerAutoScalingConfigAttributes(), + }, + "repo_server": schema.SingleNestedAttribute{ + Description: "Repo Server auto scaling config", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: getRepoServerAutoScalingConfigAttributes(), + }, + } +} + +func getAppControllerAutoScalingConfigAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "resource_minimum": schema.SingleNestedAttribute{ + Description: "Resource minimum", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: getResourcesAttributes(), + }, + "resource_maximum": schema.SingleNestedAttribute{ + Description: "Resource maximum", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: getResourcesAttributes(), + }, + } +} + +func getRepoServerAutoScalingConfigAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "resource_minimum": schema.SingleNestedAttribute{ + Description: "Resource minimum", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: getResourcesAttributes(), + }, + "resource_maximum": schema.SingleNestedAttribute{ + Description: "Resource maximum", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, + Attributes: getResourcesAttributes(), + }, + "replicas_maximum": schema.Int64Attribute{ + Description: "Replica maximum", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + "replicas_minimum": schema.Int64Attribute{ + Description: "Replica minimum, this should be set to 1 as a minimum", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + } +} + +func getResourcesAttributes() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "cpu": schema.StringAttribute{ + Description: "CPU", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "memory": schema.StringAttribute{ + Description: "Memory", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + } +} diff --git a/akp/resource_akp_cluster_test.go b/akp/resource_akp_cluster_test.go index 05a3c67..75b273e 100644 --- a/akp/resource_akp_cluster_test.go +++ b/akp/resource_akp_cluster_test.go @@ -3,15 +3,16 @@ package akp import ( "context" "fmt" - argocdv1 "github.com/akuity/api-client-go/pkg/api/gen/argocd/v1" - "github.com/akuity/terraform-provider-akp/akp/types" - hashitype "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "testing" + hashitype "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + + argocdv1 "github.com/akuity/api-client-go/pkg/api/gen/argocd/v1" + "github.com/akuity/terraform-provider-akp/akp/types" ) func TestAccClusterResource(t *testing.T) { diff --git a/akp/types/cluster.go b/akp/types/cluster.go index bb1f3ad..e97a826 100644 --- a/akp/types/cluster.go +++ b/akp/types/cluster.go @@ -7,6 +7,7 @@ package types import ( "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) type Cluster struct { @@ -33,20 +34,60 @@ type ClusterSpec struct { Data ClusterData `tfsdk:"data"` } +type Resources struct { + Memory types.String `tfsdk:"memory"` + Cpu types.String `tfsdk:"cpu"` +} + type ManagedClusterConfig struct { SecretName types.String `tfsdk:"secret_name"` SecretKey types.String `tfsdk:"secret_key"` } +type AutoScalerConfig struct { + ApplicationController *AppControllerAutoScalingConfig `tfsdk:"application_controller"` + RepoServer *RepoServerAutoScalingConfig `tfsdk:"repo_server"` +} + +type AppControllerAutoScalingConfig struct { + ResourceMinimum *Resources `tfsdk:"resource_minimum"` + ResourceMaximum *Resources `tfsdk:"resource_maximum"` +} + +type RepoServerAutoScalingConfig struct { + ResourceMinimum *Resources `tfsdk:"resource_minimum"` + ResourceMaximum *Resources `tfsdk:"resource_maximum"` + ReplicasMaximum types.Int64 `tfsdk:"replicas_maximum"` + ReplicasMinimum types.Int64 `tfsdk:"replicas_minimum"` +} + +type CustomAgentSizeConfig struct { + ApplicationController *AppControllerCustomAgentSizeConfig `tfsdk:"application_controller"` + RepoServer *RepoServerCustomAgentSizeConfig `tfsdk:"repo_server"` +} + +type AppControllerCustomAgentSizeConfig struct { + Memory types.String `tfsdk:"memory"` + Cpu types.String `tfsdk:"cpu"` +} + +type RepoServerCustomAgentSizeConfig struct { + Memory types.String `tfsdk:"memory"` + Cpu types.String `tfsdk:"cpu"` + Replicas types.Int64 `tfsdk:"replicas"` +} + type ClusterData struct { - Size types.String `tfsdk:"size"` - AutoUpgradeDisabled types.Bool `tfsdk:"auto_upgrade_disabled"` - Kustomization types.String `tfsdk:"kustomization"` - AppReplication types.Bool `tfsdk:"app_replication"` - TargetVersion types.String `tfsdk:"target_version"` - RedisTunneling types.Bool `tfsdk:"redis_tunneling"` - DatadogAnnotationsEnabled types.Bool `tfsdk:"datadog_annotations_enabled"` - EksAddonEnabled types.Bool `tfsdk:"eks_addon_enabled"` - ManagedClusterConfig *ManagedClusterConfig `tfsdk:"managed_cluster_config"` - MultiClusterK8SDashboardEnabled types.Bool `tfsdk:"multi_cluster_k8s_dashboard_enabled"` + Size types.String `tfsdk:"size"` + AutoUpgradeDisabled types.Bool `tfsdk:"auto_upgrade_disabled"` + Kustomization types.String `tfsdk:"kustomization"` + AppReplication types.Bool `tfsdk:"app_replication"` + TargetVersion types.String `tfsdk:"target_version"` + RedisTunneling types.Bool `tfsdk:"redis_tunneling"` + DatadogAnnotationsEnabled types.Bool `tfsdk:"datadog_annotations_enabled"` + EksAddonEnabled types.Bool `tfsdk:"eks_addon_enabled"` + ManagedClusterConfig *ManagedClusterConfig `tfsdk:"managed_cluster_config"` + MultiClusterK8SDashboardEnabled types.Bool `tfsdk:"multi_cluster_k8s_dashboard_enabled"` + AutoscalerConfig basetypes.ObjectValue `tfsdk:"auto_agent_size_config"` + CustomAgentSizeConfig *CustomAgentSizeConfig `tfsdk:"custom_agent_size_config"` } diff --git a/akp/types/types.go b/akp/types/types.go index 10bb643..79b2ff3 100644 --- a/akp/types/types.go +++ b/akp/types/types.go @@ -12,8 +12,11 @@ import ( tftypes "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "google.golang.org/protobuf/types/known/structpb" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + kustomizetypes "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/yaml" argocdv1 "github.com/akuity/api-client-go/pkg/api/gen/argocd/v1" @@ -38,6 +41,7 @@ var ( argocdv1.ClusterSize_CLUSTER_SIZE_SMALL: "small", argocdv1.ClusterSize_CLUSTER_SIZE_MEDIUM: "medium", argocdv1.ClusterSize_CLUSTER_SIZE_LARGE: "large", + argocdv1.ClusterSize_CLUSTER_SIZE_AUTO: "auto", argocdv1.ClusterSize_CLUSTER_SIZE_UNSPECIFIED: "unspecified", } ) @@ -139,7 +143,7 @@ func (a *ArgoCD) ToArgoCDAPIModel(ctx context.Context, diag *diag.Diagnostics, n } } -func (c *Cluster) Update(ctx context.Context, diagnostics *diag.Diagnostics, apiCluster *argocdv1.Cluster) { +func (c *Cluster) Update(ctx context.Context, diagnostics *diag.Diagnostics, apiCluster *argocdv1.Cluster, plan *Cluster) { c.ID = tftypes.StringValue(apiCluster.GetId()) c.Name = tftypes.StringValue(apiCluster.GetName()) c.Namespace = tftypes.StringValue(apiCluster.GetNamespace()) @@ -182,13 +186,91 @@ func (c *Cluster) Update(ctx context.Context, diagnostics *diag.Diagnostics, api } } + var existingConfig kustomizetypes.Kustomization + size := tftypes.StringValue(ClusterSizeString[apiCluster.GetData().GetSize()]) + var customConfig *CustomAgentSizeConfig + if err := yaml.Unmarshal(yamlData, &existingConfig); err == nil && plan != nil && plan.Spec != nil && plan.Spec.Data.CustomAgentSizeConfig != nil { + extractedCustomConfig := extractCustomSizeConfig(existingConfig) + if extractedCustomConfig != nil { + if plan != nil && plan.Spec != nil && plan.Spec.Data.CustomAgentSizeConfig != nil { + if areCustomAgentConfigsEquivalent(plan.Spec.Data.CustomAgentSizeConfig, extractedCustomConfig) { + customConfig = plan.Spec.Data.CustomAgentSizeConfig + } else { + customConfig = plan.Spec.Data.CustomAgentSizeConfig + } + existingConfig.Patches = filterNonSizePatchesKustomize(existingConfig.Patches) + existingConfig.Replicas = filterNonRepoServerReplicasKustomize(existingConfig.Replicas) + } else { + customConfig = extractedCustomConfig + } + + if existingConfig.CheckEmpty() != nil { + kustomization = tftypes.StringValue("{}\n") + } + + cleanYamlData, err := yaml.Marshal(existingConfig) + if err != nil { + diagnostics.AddError("failed to marshal cleaned config to yaml", err.Error()) + } else { + kustomization = tftypes.StringValue(string(cleanYamlData)) + } + size = tftypes.StringValue("custom") + } + } + c.Labels = labels c.Annotations = annotations + + var autoscalerConfig basetypes.ObjectValue + if c.Spec != nil && plan != nil { + newAPIConfig := apiCluster.GetData().GetAutoscalerConfig() + if !plan.Spec.Data.AutoscalerConfig.IsNull() && !plan.Spec.Data.AutoscalerConfig.IsUnknown() && newAPIConfig != nil && + newAPIConfig.RepoServer != nil && newAPIConfig.ApplicationController != nil { + autoscalerConfig = plan.Spec.Data.AutoscalerConfig + newConfig := &AutoScalerConfig{ + ApplicationController: &AppControllerAutoScalingConfig{ + ResourceMinimum: &Resources{ + Memory: tftypes.StringValue(newAPIConfig.ApplicationController.ResourceMinimum.Mem), + Cpu: tftypes.StringValue(newAPIConfig.ApplicationController.ResourceMinimum.Cpu), + }, + ResourceMaximum: &Resources{ + Memory: tftypes.StringValue(newAPIConfig.ApplicationController.ResourceMaximum.Mem), + Cpu: tftypes.StringValue(newAPIConfig.ApplicationController.ResourceMaximum.Cpu), + }, + }, + RepoServer: &RepoServerAutoScalingConfig{ + ResourceMinimum: &Resources{ + Memory: tftypes.StringValue(newAPIConfig.RepoServer.ResourceMinimum.Mem), + Cpu: tftypes.StringValue(newAPIConfig.RepoServer.ResourceMinimum.Cpu), + }, + ResourceMaximum: &Resources{ + Memory: tftypes.StringValue(newAPIConfig.RepoServer.ResourceMaximum.Mem), + Cpu: tftypes.StringValue(newAPIConfig.RepoServer.ResourceMaximum.Cpu), + }, + ReplicasMaximum: tftypes.Int64Value(int64(newAPIConfig.RepoServer.ReplicaMaximum)), + ReplicasMinimum: tftypes.Int64Value(int64(newAPIConfig.RepoServer.ReplicaMinimum)), + }, + } + if areAutoScalerConfigsEquivalent(extractConfigFromObjectValue(plan.Spec.Data.AutoscalerConfig), newConfig) { + autoscalerConfig = plan.Spec.Data.AutoscalerConfig + } else { + autoscalerConfig = toAutoScalerConfigTFModel(newAPIConfig) + } + } else { + autoscalerConfig = toAutoScalerConfigTFModel(newAPIConfig) + } + } else { + if plan == nil || plan.Spec == nil || plan.Spec.Data.AutoscalerConfig.IsNull() { + autoscalerConfig = basetypes.ObjectValue{} + } + autoscalerConfig = toAutoScalerConfigTFModel(apiCluster.GetData().GetAutoscalerConfig()) + } + c.Spec = &ClusterSpec{ Description: tftypes.StringValue(apiCluster.GetDescription()), NamespaceScoped: tftypes.BoolValue(apiCluster.GetNamespaceScoped()), Data: ClusterData{ - Size: tftypes.StringValue(ClusterSizeString[apiCluster.GetData().GetSize()]), + Size: size, AutoUpgradeDisabled: tftypes.BoolValue(apiCluster.GetData().GetAutoUpgradeDisabled()), Kustomization: kustomization, AppReplication: tftypes.BoolValue(apiCluster.GetData().GetAppReplication()), @@ -198,6 +280,8 @@ func (c *Cluster) Update(ctx context.Context, diagnostics *diag.Diagnostics, api EksAddonEnabled: tftypes.BoolValue(apiCluster.GetData().GetEksAddonEnabled()), ManagedClusterConfig: toManagedClusterConfigTFModel(apiCluster.GetData().GetManagedClusterConfig()), MultiClusterK8SDashboardEnabled: tftypes.BoolValue(apiCluster.GetData().GetMultiClusterK8SDashboardEnabled()), + AutoscalerConfig: autoscalerConfig, + CustomAgentSizeConfig: customConfig, }, } } @@ -221,7 +305,7 @@ func (c *Cluster) ToClusterAPIModel(ctx context.Context, diagnostics *diag.Diagn Spec: v1alpha1.ClusterSpec{ Description: c.Spec.Description.ValueString(), NamespaceScoped: c.Spec.NamespaceScoped.ValueBool(), - Data: toClusterDataAPIModel(diagnostics, c.Spec.Data), + Data: toClusterDataAPIModel(ctx, diagnostics, c.Spec.Data), }, } } @@ -285,10 +369,19 @@ func ToConfigManagementPluginsTFModel(ctx context.Context, diagnostics *diag.Dia return newCMPs } -func toClusterDataAPIModel(diagnostics *diag.Diagnostics, clusterData ClusterData) v1alpha1.ClusterData { - raw := runtime.RawExtension{} - if err := yaml.Unmarshal([]byte(clusterData.Kustomization.ValueString()), &raw); err != nil { - diagnostics.AddError("failed unmarshal kustomization string to yaml", err.Error()) +func toClusterDataAPIModel(ctx context.Context, diagnostics *diag.Diagnostics, clusterData ClusterData) v1alpha1.ClusterData { + var autoscalerConfig *AutoScalerConfig + if d := clusterData.AutoscalerConfig.As(ctx, &autoscalerConfig, basetypes.ObjectAsOptions{ + UnhandledNullAsEmpty: true, + UnhandledUnknownAsEmpty: true, + }); d.HasError() { + diagnostics.AddError("failed to convert autoscaler config", "") + return v1alpha1.ClusterData{} + } + + size, raw := handleAgentSizeAndKustomization(diagnostics, &clusterData, autoscalerConfig) + if diagnostics.HasError() { + return v1alpha1.ClusterData{} } var managedConfig *v1alpha1.ManagedClusterConfig @@ -299,8 +392,76 @@ func toClusterDataAPIModel(diagnostics *diag.Diagnostics, clusterData ClusterDat } } + autoscalerConfigAPI := &v1alpha1.AutoScalerConfig{} + if autoscalerConfig != nil { + if autoscalerConfig.RepoServer != nil { + if autoscalerConfig.RepoServer.ResourceMaximum == nil || autoscalerConfig.RepoServer.ResourceMinimum == nil { + diagnostics.AddError("repo server autoscaler config requires minimum and maximum resources", "") + return v1alpha1.ClusterData{} + } + if autoscalerConfig.RepoServer.ResourceMinimum.Memory.ValueString() == "" || autoscalerConfig.RepoServer.ResourceMinimum.Cpu.ValueString() == "" || + autoscalerConfig.RepoServer.ResourceMaximum.Memory.ValueString() == "" || autoscalerConfig.RepoServer.ResourceMaximum.Cpu.ValueString() == "" || + autoscalerConfig.RepoServer.ReplicasMaximum.ValueInt64() == 0 || autoscalerConfig.RepoServer.ReplicasMinimum.ValueInt64() == 0 { + diagnostics.AddError("repo server autoscaler config requires memory, cpu, and replicas values", "") + return v1alpha1.ClusterData{} + } + if !areResourcesValid( + autoscalerConfig.RepoServer.ResourceMinimum.Memory.ValueString(), + autoscalerConfig.RepoServer.ResourceMaximum.Memory.ValueString(), + ) { + diagnostics.AddError("repo server minimum memory must be less than or equal to maximum memory", "") + return v1alpha1.ClusterData{} + } + if !areResourcesValid( + autoscalerConfig.RepoServer.ResourceMinimum.Cpu.ValueString(), + autoscalerConfig.RepoServer.ResourceMaximum.Cpu.ValueString(), + ) { + diagnostics.AddError("repo server minimum CPU must be less than or equal to maximum CPU", "") + return v1alpha1.ClusterData{} + } + if autoscalerConfig.RepoServer.ReplicasMinimum.ValueInt64() > autoscalerConfig.RepoServer.ReplicasMaximum.ValueInt64() { + diagnostics.AddError("repo server minimum replicas must be less than or equal to maximum replicas", "") + return v1alpha1.ClusterData{} + } + autoscalerConfigAPI.RepoServer = &v1alpha1.RepoServerAutoScalingConfig{ + ResourceMinimum: toResourcesAPIModel(autoscalerConfig.RepoServer.ResourceMinimum), + ResourceMaximum: toResourcesAPIModel(autoscalerConfig.RepoServer.ResourceMaximum), + ReplicaMaximum: int32(autoscalerConfig.RepoServer.ReplicasMaximum.ValueInt64()), + ReplicaMinimum: int32(autoscalerConfig.RepoServer.ReplicasMinimum.ValueInt64()), + } + } + if autoscalerConfig.ApplicationController != nil { + if autoscalerConfig.ApplicationController.ResourceMaximum == nil || autoscalerConfig.ApplicationController.ResourceMinimum == nil { + diagnostics.AddError("app controller autoscaler config requires minimum and maximum resources", "") + return v1alpha1.ClusterData{} + } + if autoscalerConfig.ApplicationController.ResourceMinimum.Memory.ValueString() == "" || autoscalerConfig.ApplicationController.ResourceMinimum.Cpu.ValueString() == "" || + autoscalerConfig.ApplicationController.ResourceMaximum.Memory.ValueString() == "" || autoscalerConfig.ApplicationController.ResourceMaximum.Cpu.ValueString() == "" { + diagnostics.AddError("app controller autoscaler config requires memory, cpu values", "") + return v1alpha1.ClusterData{} + } + if !areResourcesValid( + autoscalerConfig.ApplicationController.ResourceMinimum.Memory.ValueString(), + autoscalerConfig.ApplicationController.ResourceMaximum.Memory.ValueString(), + ) { + diagnostics.AddError("application controller minimum memory must be less than or equal to maximum memory", "") + return v1alpha1.ClusterData{} + } + if !areResourcesValid( + autoscalerConfig.ApplicationController.ResourceMinimum.Cpu.ValueString(), + autoscalerConfig.ApplicationController.ResourceMaximum.Cpu.ValueString(), + ) { + diagnostics.AddError("application controller minimum CPU must be less than or equal to maximum CPU", "") + return v1alpha1.ClusterData{} + } + autoscalerConfigAPI.ApplicationController = &v1alpha1.AppControllerAutoScalingConfig{ + ResourceMinimum: toResourcesAPIModel(autoscalerConfig.ApplicationController.ResourceMinimum), + ResourceMaximum: toResourcesAPIModel(autoscalerConfig.ApplicationController.ResourceMaximum), + } + } + } return v1alpha1.ClusterData{ - Size: v1alpha1.ClusterSize(clusterData.Size.ValueString()), + Size: v1alpha1.ClusterSize(size), AutoUpgradeDisabled: clusterData.AutoUpgradeDisabled.ValueBoolPointer(), Kustomization: raw, AppReplication: clusterData.AppReplication.ValueBoolPointer(), @@ -310,6 +471,7 @@ func toClusterDataAPIModel(diagnostics *diag.Diagnostics, clusterData ClusterDat EksAddonEnabled: clusterData.EksAddonEnabled.ValueBoolPointer(), ManagedClusterConfig: managedConfig, MultiClusterK8SDashboardEnabled: clusterData.MultiClusterK8SDashboardEnabled.ValueBoolPointer(), + AutoscalerConfig: autoscalerConfigAPI, } } @@ -874,3 +1036,540 @@ func toManagedClusterConfigTFModel(cfg *argocdv1.ManagedClusterConfig) *ManagedC SecretKey: types.StringValue(cfg.SecretKey), } } + +func toAutoScalerConfigTFModel(cfg *argocdv1.AutoScalerConfig) basetypes.ObjectValue { + attributeTypes := map[string]attr.Type{ + "application_controller": basetypes.ObjectType{ + AttrTypes: map[string]attr.Type{ + "resource_minimum": basetypes.ObjectType{ + AttrTypes: map[string]attr.Type{ + "memory": types.StringType, + "cpu": types.StringType, + }, + }, + "resource_maximum": basetypes.ObjectType{ + AttrTypes: map[string]attr.Type{ + "memory": types.StringType, + "cpu": types.StringType, + }, + }, + }, + }, + "repo_server": basetypes.ObjectType{ + AttrTypes: map[string]attr.Type{ + "resource_minimum": basetypes.ObjectType{ + AttrTypes: map[string]attr.Type{ + "memory": types.StringType, + "cpu": types.StringType, + }, + }, + "resource_maximum": basetypes.ObjectType{ + AttrTypes: map[string]attr.Type{ + "memory": types.StringType, + "cpu": types.StringType, + }, + }, + "replicas_maximum": types.Int64Type, + "replicas_minimum": types.Int64Type, + }, + }, + } + + attributes := map[string]attr.Value{} + if cfg.ApplicationController != nil { + attributes["application_controller"] = basetypes.NewObjectValueMust( + attributeTypes["application_controller"].(basetypes.ObjectType).AttrTypes, + map[string]attr.Value{ + "resource_minimum": basetypes.NewObjectValueMust( + attributeTypes["application_controller"].(basetypes.ObjectType).AttrTypes["resource_minimum"].(basetypes.ObjectType).AttrTypes, + map[string]attr.Value{ + "memory": basetypes.NewStringValue(cfg.ApplicationController.ResourceMinimum.Mem), + "cpu": basetypes.NewStringValue(cfg.ApplicationController.ResourceMinimum.Cpu), + }, + ), + "resource_maximum": basetypes.NewObjectValueMust( + attributeTypes["application_controller"].(basetypes.ObjectType).AttrTypes["resource_maximum"].(basetypes.ObjectType).AttrTypes, + map[string]attr.Value{ + "memory": basetypes.NewStringValue(cfg.ApplicationController.ResourceMaximum.Mem), + "cpu": basetypes.NewStringValue(cfg.ApplicationController.ResourceMaximum.Cpu), + }, + ), + }) + } + if cfg.RepoServer != nil { + attributes["repo_server"] = basetypes.NewObjectValueMust( + attributeTypes["repo_server"].(basetypes.ObjectType).AttrTypes, + map[string]attr.Value{ + "resource_minimum": basetypes.NewObjectValueMust( + attributeTypes["repo_server"].(basetypes.ObjectType).AttrTypes["resource_minimum"].(basetypes.ObjectType).AttrTypes, + map[string]attr.Value{ + "memory": basetypes.NewStringValue(cfg.RepoServer.ResourceMinimum.Mem), + "cpu": basetypes.NewStringValue(cfg.RepoServer.ResourceMinimum.Cpu), + }, + ), + "resource_maximum": basetypes.NewObjectValueMust( + attributeTypes["repo_server"].(basetypes.ObjectType).AttrTypes["resource_maximum"].(basetypes.ObjectType).AttrTypes, + map[string]attr.Value{ + "memory": basetypes.NewStringValue(cfg.RepoServer.ResourceMaximum.Mem), + "cpu": basetypes.NewStringValue(cfg.RepoServer.ResourceMaximum.Cpu), + }, + ), + "replicas_maximum": basetypes.NewInt64Value(int64(cfg.RepoServer.ReplicaMaximum)), + "replicas_minimum": basetypes.NewInt64Value(int64(cfg.RepoServer.ReplicaMinimum)), + }, + ) + } + + objectValue, diags := basetypes.NewObjectValue(attributeTypes, attributes) + if diags.HasError() { + return basetypes.NewObjectUnknown(attributeTypes) + } + return objectValue +} + +func handleAgentSizeAndKustomization(diagnostics *diag.Diagnostics, clusterData *ClusterData, autoscalerConfig *AutoScalerConfig) (size string, kustomization runtime.RawExtension) { + // Validate configs + customSizeConfig := clusterData.CustomAgentSizeConfig + if autoscalerConfig != nil && clusterData.Size.ValueString() != "auto" { + diagnostics.AddError("autoscaler config should not be set when size is not auto", "") + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + if customSizeConfig == nil && clusterData.Size.ValueString() == "custom" { + diagnostics.AddError("custom agent size config is required when size is custom", "") + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + if customSizeConfig != nil && clusterData.Size.ValueString() != "custom" { + diagnostics.AddError("custom agent size config should not be set when size is not custom", "") + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + + // Parse existing kustomization if it exists + var existingConfig map[string]any + raw := runtime.RawExtension{} + if clusterData.Kustomization.ValueString() != "" { + if err := yaml.Unmarshal([]byte(clusterData.Kustomization.ValueString()), &raw); err != nil { + diagnostics.AddError("failed unmarshal kustomization string to yaml", err.Error()) + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + if err := yaml.Unmarshal(raw.Raw, &existingConfig); err != nil { + diagnostics.AddError("failed to parse existing kustomization", err.Error()) + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + } + if clusterData.Size.ValueString() != "custom" { + return clusterData.Size.ValueString(), raw + } + + if existingConfig == nil { + existingConfig = map[string]any{ + "apiVersion": "kustomize.config.k8s.io/v1beta1", + "kind": "Kustomization", + } + } + patches := make([]map[string]any, 0) + replicas := make([]map[string]any, 0) + if customSizeConfig.ApplicationController != nil { + if customSizeConfig.ApplicationController.Memory.ValueString() == "" || customSizeConfig.ApplicationController.Cpu.ValueString() == "" { + diagnostics.AddError("memory and cpu are required for app controller custom size", "") + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + patches = append(patches, map[string]any{ + "patch": generateAppControllerPatch(customSizeConfig.ApplicationController), + "target": map[string]string{ + "kind": "Deployment", + "name": "argocd-application-controller", + }, + }) + } + + if customSizeConfig.RepoServer != nil { + if customSizeConfig.RepoServer.Memory.ValueString() == "" || customSizeConfig.RepoServer.Cpu.ValueString() == "" || customSizeConfig.RepoServer.Replicas.ValueInt64() == 0 { + diagnostics.AddError("memory, cpu and replicas are required for repo server custom size", "") + return clusterData.Size.ValueString(), runtime.RawExtension{} + } else if customSizeConfig.RepoServer.Replicas.ValueInt64() < 0 { + diagnostics.AddError("replicas must be greater than or equal to 0", "") + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + patches = append(patches, map[string]any{ + "patch": generateRepoServerPatch(customSizeConfig.RepoServer), + "target": map[string]string{ + "kind": "Deployment", + "name": "argocd-repo-server", + }, + }) + + replicas = append(replicas, map[string]any{ + "count": customSizeConfig.RepoServer.Replicas.ValueInt64(), + "name": "argocd-repo-server", + }) + } + + if existingPatches, ok := existingConfig["patches"].([]any); ok { + patches = append(filterNonSizePatches(existingPatches), patches...) + } + if existingReplicas, ok := existingConfig["replicas"].([]any); ok { + replicas = append(filterNonRepoServerReplicas(existingReplicas), replicas...) + } + + existingConfig["patches"] = patches + if len(replicas) > 0 { + existingConfig["replicas"] = replicas + } + + yamlData, err := yaml.Marshal(existingConfig) + if err != nil { + diagnostics.AddError("failed to marshal config to yaml", err.Error()) + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + + if err = yaml.Unmarshal(yamlData, &raw); err != nil { + diagnostics.AddError("failed unmarshal kustomization string to yaml", err.Error()) + return clusterData.Size.ValueString(), runtime.RawExtension{} + } + + // Custom size will be represented as large with kustomization + return "large", raw +} + +func filterNonSizePatches(patches []any) []map[string]any { + var filtered []map[string]any + for _, p := range patches { + patch, ok := p.(map[string]any) + if !ok { + continue + } + target, ok := patch["target"].(map[string]any) + if !ok { + filtered = append(filtered, patch) + continue + } + name, ok := target["name"].(string) + if !ok || (name != "argocd-application-controller" && name != "argocd-repo-server") { + filtered = append(filtered, patch) + } + } + return filtered +} + +func filterNonRepoServerReplicas(replicas []any) []map[string]any { + var filtered []map[string]any + for _, r := range replicas { + replica, ok := r.(map[string]any) + if !ok { + continue + } + name, ok := replica["name"].(string) + if !ok || name != "argocd-repo-server" { + filtered = append(filtered, replica) + } + } + return filtered +} + +func filterNonSizePatchesKustomize(patches []kustomizetypes.Patch) []kustomizetypes.Patch { + var filtered []kustomizetypes.Patch + for _, p := range patches { + if p.Target == nil || (p.Target.Name != "argocd-application-controller" && p.Target.Name != "argocd-repo-server") { + filtered = append(filtered, p) + } + } + return filtered +} + +func filterNonRepoServerReplicasKustomize(replicas []kustomizetypes.Replica) []kustomizetypes.Replica { + var filtered []kustomizetypes.Replica + for _, r := range replicas { + if r.Name != "argocd-repo-server" { + filtered = append(filtered, r) + } + } + return filtered +} + +func generateAppControllerPatch(config *AppControllerCustomAgentSizeConfig) string { + if config == nil { + return "" + } + return fmt.Sprintf(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-application-controller +spec: + template: + spec: + containers: + - name: argocd-application-controller + resources: + limits: + memory: %s + requests: + cpu: %s + memory: %s`, + config.Memory.ValueString(), + config.Cpu.ValueString(), + config.Memory.ValueString()) +} + +func generateRepoServerPatch(config *RepoServerCustomAgentSizeConfig) string { + if config == nil { + return "" + } + return fmt.Sprintf(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-repo-server +spec: + template: + spec: + containers: + - name: argocd-repo-server + resources: + limits: + memory: %s + requests: + cpu: %s + memory: %s`, + config.Memory.ValueString(), + config.Cpu.ValueString(), + config.Memory.ValueString()) +} + +func toResourcesAPIModel(resources *Resources) *v1alpha1.Resources { + if resources == nil { + return nil + } + return &v1alpha1.Resources{ + Mem: resources.Memory.ValueString(), + Cpu: resources.Cpu.ValueString(), + } +} + +func extractCustomSizeConfig(existingConfig kustomizetypes.Kustomization) *CustomAgentSizeConfig { + var appController *AppControllerCustomAgentSizeConfig + var repoServer *RepoServerCustomAgentSizeConfig + + for _, p := range existingConfig.Patches { + var patch appsv1.Deployment + if err := yaml.Unmarshal([]byte(p.Patch), &patch); err != nil { + continue + } + + switch p.Target.Name { + case "argocd-application-controller": + for _, container := range patch.Spec.Template.Spec.Containers { + if container.Name == "argocd-application-controller" { + appController = &AppControllerCustomAgentSizeConfig{ + Memory: tftypes.StringValue(container.Resources.Requests.Memory().String()), + Cpu: tftypes.StringValue(container.Resources.Requests.Cpu().String()), + } + break + } + } + case "argocd-repo-server": + for _, container := range patch.Spec.Template.Spec.Containers { + if container.Name == "argocd-repo-server" { + repoServer = &RepoServerCustomAgentSizeConfig{ + Memory: tftypes.StringValue(container.Resources.Requests.Memory().String()), + Cpu: tftypes.StringValue(container.Resources.Requests.Cpu().String()), + } + break + } + } + } + } + if repoServer != nil { + for _, r := range existingConfig.Replicas { + if r.Name == "argocd-repo-server" { + repoServer.Replicas = tftypes.Int64Value(r.Count) + break + } + } + } + + if appController == nil && repoServer == nil { + return nil + } + + return &CustomAgentSizeConfig{ + ApplicationController: appController, + RepoServer: repoServer, + } +} + +func areCustomAgentConfigsEquivalent(config1, config2 *CustomAgentSizeConfig) bool { + if config1 == nil || config2 == nil { + return config1 == config2 + } + if config1.ApplicationController != nil && config2.ApplicationController != nil { + if !areResourcesEquivalent( + config1.ApplicationController.Cpu.ValueString(), + config2.ApplicationController.Cpu.ValueString(), + ) || !areResourcesEquivalent( + config1.ApplicationController.Memory.ValueString(), + config2.ApplicationController.Memory.ValueString(), + ) { + return false + } + } else if config1.ApplicationController != nil || config2.ApplicationController != nil { + return false + } + if config1.RepoServer != nil && config2.RepoServer != nil { + if !areResourcesEquivalent( + config1.RepoServer.Cpu.ValueString(), + config2.RepoServer.Cpu.ValueString(), + ) || !areResourcesEquivalent( + config1.RepoServer.Memory.ValueString(), + config2.RepoServer.Memory.ValueString(), + ) || config1.RepoServer.Replicas != config2.RepoServer.Replicas { + return false + } + } else if config1.RepoServer != nil || config2.RepoServer != nil { + return false + } + return true +} + +func areAutoScalerConfigsEquivalent(plan, now *AutoScalerConfig) bool { + if plan == nil { + return true + } + if plan.ApplicationController != nil && now.ApplicationController != nil { + if !areResourcesEquivalent( + plan.ApplicationController.ResourceMinimum.Cpu.ValueString(), + now.ApplicationController.ResourceMinimum.Cpu.ValueString(), + ) || !areResourcesEquivalent( + plan.ApplicationController.ResourceMinimum.Memory.ValueString(), + now.ApplicationController.ResourceMinimum.Memory.ValueString(), + ) || !areResourcesEquivalent( + plan.ApplicationController.ResourceMaximum.Cpu.ValueString(), + now.ApplicationController.ResourceMaximum.Cpu.ValueString(), + ) || !areResourcesEquivalent( + plan.ApplicationController.ResourceMaximum.Memory.ValueString(), + now.ApplicationController.ResourceMaximum.Memory.ValueString(), + ) { + return false + } + } + if plan.RepoServer != nil && now.RepoServer != nil { + if !areResourcesEquivalent( + plan.RepoServer.ResourceMinimum.Cpu.ValueString(), + now.RepoServer.ResourceMinimum.Cpu.ValueString(), + ) || !areResourcesEquivalent( + plan.RepoServer.ResourceMinimum.Memory.ValueString(), + now.RepoServer.ResourceMinimum.Memory.ValueString(), + ) || !areResourcesEquivalent( + plan.RepoServer.ResourceMaximum.Cpu.ValueString(), + now.RepoServer.ResourceMaximum.Cpu.ValueString(), + ) || !areResourcesEquivalent( + plan.RepoServer.ResourceMaximum.Memory.ValueString(), + now.RepoServer.ResourceMaximum.Memory.ValueString(), + ) || plan.RepoServer.ReplicasMaximum != now.RepoServer.ReplicasMaximum || + plan.RepoServer.ReplicasMinimum != now.RepoServer.ReplicasMinimum { + return false + } + } + return true +} + +func areResourcesEquivalent(plan, new string) bool { + if plan == "" { + return true + } + planQ, err1 := resource.ParseQuantity(plan) + newQ, err2 := resource.ParseQuantity(new) + if err1 != nil || err2 != nil { + return plan == new + } + + planVal := planQ.Value() + newVal := newQ.Value() + + var diff float64 + if planVal > newVal { + diff = float64(planVal-newVal) / float64(planVal) + } else { + diff = float64(newVal-planVal) / float64(newVal) + } + + // there maybe Mi to Gi conversion with some rounding difference + return diff <= 0.05 +} + +func extractConfigFromObjectValue(obj basetypes.ObjectValue) *AutoScalerConfig { + if obj.IsNull() || obj.IsUnknown() { + return nil + } + + attrs := obj.Attributes() + config := &AutoScalerConfig{} + if appCtrl, ok := attrs["application_controller"].(basetypes.ObjectValue); ok { + appCtrlAttrs := appCtrl.Attributes() + if resMin, ok := appCtrlAttrs["resource_minimum"].(basetypes.ObjectValue); ok { + resMinAttrs := resMin.Attributes() + if cpu, ok := resMinAttrs["cpu"].(basetypes.StringValue); ok { + if mem, ok := resMinAttrs["memory"].(basetypes.StringValue); ok { + if resMax, ok := appCtrlAttrs["resource_maximum"].(basetypes.ObjectValue); ok { + resMaxAttrs := resMax.Attributes() + if maxCpu, ok := resMaxAttrs["cpu"].(basetypes.StringValue); ok { + if maxMem, ok := resMaxAttrs["memory"].(basetypes.StringValue); ok { + config.ApplicationController = &AppControllerAutoScalingConfig{ + ResourceMinimum: &Resources{ + Cpu: cpu, + Memory: mem, + }, + ResourceMaximum: &Resources{ + Cpu: maxCpu, + Memory: maxMem, + }, + } + } + } + } + } + } + } + } + if repoServer, ok := attrs["repo_server"].(basetypes.ObjectValue); ok { + repoServerAttrs := repoServer.Attributes() + if resMin, ok := repoServerAttrs["resource_minimum"].(basetypes.ObjectValue); ok { + resMinAttrs := resMin.Attributes() + if cpu, ok := resMinAttrs["cpu"].(basetypes.StringValue); ok { + if mem, ok := resMinAttrs["memory"].(basetypes.StringValue); ok { + if resMax, ok := repoServerAttrs["resource_maximum"].(basetypes.ObjectValue); ok { + resMaxAttrs := resMax.Attributes() + if maxCpu, ok := resMaxAttrs["cpu"].(basetypes.StringValue); ok { + if maxMem, ok := resMaxAttrs["memory"].(basetypes.StringValue); ok { + if replMax, ok := repoServerAttrs["replicas_maximum"].(basetypes.Int64Value); ok { + if replMin, ok := repoServerAttrs["replicas_minimum"].(basetypes.Int64Value); ok { + config.RepoServer = &RepoServerAutoScalingConfig{ + ResourceMinimum: &Resources{ + Cpu: cpu, + Memory: mem, + }, + ResourceMaximum: &Resources{ + Cpu: maxCpu, + Memory: maxMem, + }, + ReplicasMaximum: replMax, + ReplicasMinimum: replMin, + } + } + } + } + } + } + } + } + } + } + return config +} + +func areResourcesValid(min, max string) bool { + minQ, err1 := resource.ParseQuantity(min) + maxQ, err2 := resource.ParseQuantity(max) + if err1 != nil || err2 != nil { + return true + } + return minQ.Cmp(maxQ) <= 0 +} diff --git a/docs/data-sources/cluster.md b/docs/data-sources/cluster.md index c59fa2d..c6fb930 100644 --- a/docs/data-sources/cluster.md +++ b/docs/data-sources/cluster.md @@ -77,16 +77,111 @@ Read-Only: Read-Only: - `app_replication` (Boolean) Enables Argo CD state replication to the managed cluster that allows disconnecting the cluster from Akuity Platform without losing core Argocd features +- `auto_agent_size_config` (Attributes) Autoscaler config for auto agent size (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config)) - `auto_upgrade_disabled` (Boolean) Disables agents auto upgrade. On resource update terraform will try to update the agent if this is set to `true`. Otherwise agent will update itself automatically +- `custom_agent_size_config` (Attributes) Custom agent size config (see [below for nested schema](#nestedatt--spec--data--custom_agent_size_config)) - `datadog_annotations_enabled` (Boolean) Enable Datadog metrics collection of Application Controller and Repo Server. Make sure that you install Datadog agent in cluster. - `eks_addon_enabled` (Boolean) Enable this if you are installing this cluster on EKS. - `kustomization` (String) Kustomize configuration that will be applied to generated agent installation manifests - `managed_cluster_config` (Attributes) The config to access managed Kubernetes cluster. By default agent is using "in-cluster" config. (see [below for nested schema](#nestedatt--spec--data--managed_cluster_config)) - `multi_cluster_k8s_dashboard_enabled` (Boolean) Enable the KubeVision feature on the managed cluster - `redis_tunneling` (Boolean) Enables the ability to connect to Redis over a web-socket tunnel that allows using Akuity agent behind HTTPS proxy -- `size` (String) Cluster Size. One of `small`, `medium` or `large` +- `size` (String) Cluster Size. One of `small`, `medium`, `large`, `custom` or `auto` - `target_version` (String) The version of the agent to install on your cluster + +### Nested Schema for `spec.data.auto_agent_size_config` + +Read-Only: + +- `application_controller` (Attributes) Application Controller auto scaling config (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--application_controller)) +- `repo_server` (Attributes) Repo Server auto scaling config (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--repo_server)) + + +### Nested Schema for `spec.data.auto_agent_size_config.application_controller` + +Read-Only: + +- `resource_maximum` (Attributes) Resource maximum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--application_controller--resource_maximum)) +- `resource_minimum` (Attributes) Resource minimum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--application_controller--resource_minimum)) + + +### Nested Schema for `spec.data.auto_agent_size_config.application_controller.resource_maximum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `spec.data.auto_agent_size_config.application_controller.resource_minimum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + + +### Nested Schema for `spec.data.auto_agent_size_config.repo_server` + +Read-Only: + +- `replicas_maximum` (Number) Replica maximum +- `replicas_minimum` (Number) Replica minimum +- `resource_maximum` (Attributes) Resource maximum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--repo_server--resource_maximum)) +- `resource_minimum` (Attributes) Resource minimum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--repo_server--resource_minimum)) + + +### Nested Schema for `spec.data.auto_agent_size_config.repo_server.resource_maximum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `spec.data.auto_agent_size_config.repo_server.resource_minimum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + + + +### Nested Schema for `spec.data.custom_agent_size_config` + +Read-Only: + +- `application_controller` (Attributes) Application Controller custom agent size config (see [below for nested schema](#nestedatt--spec--data--custom_agent_size_config--application_controller)) +- `repo_server` (Attributes) Repo Server custom agent size config (see [below for nested schema](#nestedatt--spec--data--custom_agent_size_config--repo_server)) + + +### Nested Schema for `spec.data.custom_agent_size_config.application_controller` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `spec.data.custom_agent_size_config.repo_server` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory +- `replicas` (Number) Replica + + + ### Nested Schema for `spec.data.managed_cluster_config` diff --git a/docs/data-sources/clusters.md b/docs/data-sources/clusters.md index aa010a1..b3f7236 100644 --- a/docs/data-sources/clusters.md +++ b/docs/data-sources/clusters.md @@ -88,16 +88,111 @@ Read-Only: Read-Only: - `app_replication` (Boolean) Enables Argo CD state replication to the managed cluster that allows disconnecting the cluster from Akuity Platform without losing core Argocd features +- `auto_agent_size_config` (Attributes) Autoscaler config for auto agent size (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config)) - `auto_upgrade_disabled` (Boolean) Disables agents auto upgrade. On resource update terraform will try to update the agent if this is set to `true`. Otherwise agent will update itself automatically +- `custom_agent_size_config` (Attributes) Custom agent size config (see [below for nested schema](#nestedatt--clusters--spec--data--custom_agent_size_config)) - `datadog_annotations_enabled` (Boolean) Enable Datadog metrics collection of Application Controller and Repo Server. Make sure that you install Datadog agent in cluster. - `eks_addon_enabled` (Boolean) Enable this if you are installing this cluster on EKS. - `kustomization` (String) Kustomize configuration that will be applied to generated agent installation manifests - `managed_cluster_config` (Attributes) The config to access managed Kubernetes cluster. By default agent is using "in-cluster" config. (see [below for nested schema](#nestedatt--clusters--spec--data--managed_cluster_config)) - `multi_cluster_k8s_dashboard_enabled` (Boolean) Enable the KubeVision feature on the managed cluster - `redis_tunneling` (Boolean) Enables the ability to connect to Redis over a web-socket tunnel that allows using Akuity agent behind HTTPS proxy -- `size` (String) Cluster Size. One of `small`, `medium` or `large` +- `size` (String) Cluster Size. One of `small`, `medium`, `large`, `custom` or `auto` - `target_version` (String) The version of the agent to install on your cluster + +### Nested Schema for `clusters.spec.data.auto_agent_size_config` + +Read-Only: + +- `application_controller` (Attributes) Application Controller auto scaling config (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config--application_controller)) +- `repo_server` (Attributes) Repo Server auto scaling config (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config--repo_server)) + + +### Nested Schema for `clusters.spec.data.auto_agent_size_config.application_controller` + +Read-Only: + +- `resource_maximum` (Attributes) Resource maximum (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config--application_controller--resource_maximum)) +- `resource_minimum` (Attributes) Resource minimum (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config--application_controller--resource_minimum)) + + +### Nested Schema for `clusters.spec.data.auto_agent_size_config.application_controller.resource_maximum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `clusters.spec.data.auto_agent_size_config.application_controller.resource_minimum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + + +### Nested Schema for `clusters.spec.data.auto_agent_size_config.repo_server` + +Read-Only: + +- `replicas_maximum` (Number) Replica maximum +- `replicas_minimum` (Number) Replica minimum +- `resource_maximum` (Attributes) Resource maximum (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config--repo_server--resource_maximum)) +- `resource_minimum` (Attributes) Resource minimum (see [below for nested schema](#nestedatt--clusters--spec--data--auto_agent_size_config--repo_server--resource_minimum)) + + +### Nested Schema for `clusters.spec.data.auto_agent_size_config.repo_server.resource_maximum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `clusters.spec.data.auto_agent_size_config.repo_server.resource_minimum` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + + + +### Nested Schema for `clusters.spec.data.custom_agent_size_config` + +Read-Only: + +- `application_controller` (Attributes) Application Controller custom agent size config (see [below for nested schema](#nestedatt--clusters--spec--data--custom_agent_size_config--application_controller)) +- `repo_server` (Attributes) Repo Server custom agent size config (see [below for nested schema](#nestedatt--clusters--spec--data--custom_agent_size_config--repo_server)) + + +### Nested Schema for `clusters.spec.data.custom_agent_size_config.application_controller` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `clusters.spec.data.custom_agent_size_config.repo_server` + +Read-Only: + +- `cpu` (String) CPU +- `memory` (String) Memory +- `replicas` (Number) Replica + + + ### Nested Schema for `clusters.spec.data.managed_cluster_config` diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md index 04ee793..6208de8 100644 --- a/docs/resources/cluster.md +++ b/docs/resources/cluster.md @@ -36,6 +36,99 @@ resource "akp_cluster" "my-cluster" { For a complete working example using a GKE cluster, see [akuity/examples](https://github.com/akuity/examples/tree/main/terraform/akuity). +## Example Usage (Custom agent size) +```terraform +data "akp_instance" "example" { + name = "test" +} + +resource "akp_cluster" "example" { + instance_id = data.akp_instance.example.id + name = "test-cluster" + namespace = "test" + labels = { + test-label = true + } + annotations = { + test-annotation = false + } + spec = { + namespace_scoped = true + description = "test-description" + data = { + size = "custom" + auto_upgrade_disabled = false + custom_agent_size_config = { + application_controller = { + cpu = "1000m" + memory = "2Gi" + } + repo_server = { + replicas = 3, + cpu = "1000m" + memory = "2Gi" + } + } + } + } +} +``` + +## Example Usage (Auto agent size) +```terraform +data "akp_instance" "example" { + name = "test" +} + +resource "akp_cluster" "example" { + instance_id = data.akp_instance.example.id + name = "test-cluster" + namespace = "test" + labels = { + test-label = true + } + annotations = { + test-annotation = false + } + spec = { + namespace_scoped = true + description = "test-description" + data = { + size = "auto" + # auto_upgrade_disabled can be set to true if you want to enable auto scaling of the agent size for the cluster + auto_upgrade_disabled = false + auto_agent_size_config = { + application_controller = { + resource_maximum = { + cpu = "3" + memory = "2Gi" + }, + resource_minimum = { + cpu = "250m", + memory = "1Gi" + } + }, + repo_server = { + replicas_maximum = 3, + # minimum number of replicas should be set to 1 + replicas_minimum = 1, + resource_maximum = { + cpu = "3" + memory = "2.00Gi" + }, + resource_minimum = { + cpu = "250m", + memory = "256Mi" + } + } + } + } + } +} +``` + +- This example uses the `auto` agent size, which will automatically scale the agent based on the number of applications in the cluster. `auto_upgrade_disabled` cannot be set to `true` when using `auto` agent size. + ## Example Usage (Exhaustive) ```terraform data "akp_instance" "example" { @@ -147,12 +240,14 @@ Optional: Required: -- `size` (String) Cluster Size. One of `small`, `medium` or `large` +- `size` (String) Cluster Size. One of `small`, `medium`, `large`, `custom` or `auto` Optional: - `app_replication` (Boolean) Enables Argo CD state replication to the managed cluster that allows disconnecting the cluster from Akuity Platform without losing core Argocd features +- `auto_agent_size_config` (Attributes) Autoscaler config for auto agent size (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config)) - `auto_upgrade_disabled` (Boolean) Disable Agents Auto Upgrade. On resource update terraform will try to update the agent if this is set to `true`. Otherwise agent will update itself automatically +- `custom_agent_size_config` (Attributes) Custom agent size config (see [below for nested schema](#nestedatt--spec--data--custom_agent_size_config)) - `datadog_annotations_enabled` (Boolean) Enable Datadog metrics collection of Application Controller and Repo Server. Make sure that you install Datadog agent in cluster. - `eks_addon_enabled` (Boolean) Enable this if you are installing this cluster on EKS. - `kustomization` (String) Kustomize configuration that will be applied to generated agent installation manifests @@ -161,6 +256,99 @@ Optional: - `redis_tunneling` (Boolean) Enables the ability to connect to Redis over a web-socket tunnel that allows using Akuity agent behind HTTPS proxy - `target_version` (String) The version of the agent to install on your cluster + +### Nested Schema for `spec.data.auto_agent_size_config` + +Optional: + +- `application_controller` (Attributes) Application Controller auto scaling config (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--application_controller)) +- `repo_server` (Attributes) Repo Server auto scaling config (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--repo_server)) + + +### Nested Schema for `spec.data.auto_agent_size_config.application_controller` + +Optional: + +- `resource_maximum` (Attributes) Resource maximum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--application_controller--resource_maximum)) +- `resource_minimum` (Attributes) Resource minimum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--application_controller--resource_minimum)) + + +### Nested Schema for `spec.data.auto_agent_size_config.application_controller.resource_maximum` + +Optional: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `spec.data.auto_agent_size_config.application_controller.resource_minimum` + +Optional: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + + +### Nested Schema for `spec.data.auto_agent_size_config.repo_server` + +Optional: + +- `replicas_maximum` (Number) Replica maximum +- `replicas_minimum` (Number) Replica minimum, this should be set to 1 as a minimum +- `resource_maximum` (Attributes) Resource maximum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--repo_server--resource_maximum)) +- `resource_minimum` (Attributes) Resource minimum (see [below for nested schema](#nestedatt--spec--data--auto_agent_size_config--repo_server--resource_minimum)) + + +### Nested Schema for `spec.data.auto_agent_size_config.repo_server.resource_maximum` + +Optional: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `spec.data.auto_agent_size_config.repo_server.resource_minimum` + +Optional: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + + + +### Nested Schema for `spec.data.custom_agent_size_config` + +Optional: + +- `application_controller` (Attributes) Application Controller custom agent size config (see [below for nested schema](#nestedatt--spec--data--custom_agent_size_config--application_controller)) +- `repo_server` (Attributes) Repo Server custom agent size config (see [below for nested schema](#nestedatt--spec--data--custom_agent_size_config--repo_server)) + + +### Nested Schema for `spec.data.custom_agent_size_config.application_controller` + +Optional: + +- `cpu` (String) CPU +- `memory` (String) Memory + + + +### Nested Schema for `spec.data.custom_agent_size_config.repo_server` + +Optional: + +- `cpu` (String) CPU +- `memory` (String) Memory +- `replicas` (Number) Replica + + + ### Nested Schema for `spec.data.managed_cluster_config` diff --git a/examples/resources/akp_cluster/auto_agent_size.tf b/examples/resources/akp_cluster/auto_agent_size.tf new file mode 100644 index 0000000..f2ebd84 --- /dev/null +++ b/examples/resources/akp_cluster/auto_agent_size.tf @@ -0,0 +1,49 @@ +data "akp_instance" "example" { + name = "test" +} + +resource "akp_cluster" "example" { + instance_id = data.akp_instance.example.id + name = "test-cluster" + namespace = "test" + labels = { + test-label = true + } + annotations = { + test-annotation = false + } + spec = { + namespace_scoped = true + description = "test-description" + data = { + size = "auto" + # auto_upgrade_disabled can be set to true if you want to enable auto scaling of the agent size for the cluster + auto_upgrade_disabled = false + auto_agent_size_config = { + application_controller = { + resource_maximum = { + cpu = "3" + memory = "2Gi" + }, + resource_minimum = { + cpu = "250m", + memory = "1Gi" + } + }, + repo_server = { + replicas_maximum = 3, + # minimum number of replicas should be set to 1 + replicas_minimum = 1, + resource_maximum = { + cpu = "3" + memory = "2.00Gi" + }, + resource_minimum = { + cpu = "250m", + memory = "256Mi" + } + } + } + } + } +} diff --git a/examples/resources/akp_cluster/custom_agent_size.tf b/examples/resources/akp_cluster/custom_agent_size.tf new file mode 100644 index 0000000..ba2bd8f --- /dev/null +++ b/examples/resources/akp_cluster/custom_agent_size.tf @@ -0,0 +1,34 @@ +data "akp_instance" "example" { + name = "test" +} + +resource "akp_cluster" "example" { + instance_id = data.akp_instance.example.id + name = "test-cluster" + namespace = "test" + labels = { + test-label = true + } + annotations = { + test-annotation = false + } + spec = { + namespace_scoped = true + description = "test-description" + data = { + size = "custom" + auto_upgrade_disabled = false + custom_agent_size_config = { + application_controller = { + cpu = "1000m" + memory = "2Gi" + } + repo_server = { + replicas = 3, + cpu = "1000m" + memory = "2Gi" + } + } + } + } +} diff --git a/templates/resources/cluster.md.tmpl b/templates/resources/cluster.md.tmpl index ea48fa4..55a6eb6 100644 --- a/templates/resources/cluster.md.tmpl +++ b/templates/resources/cluster.md.tmpl @@ -19,6 +19,14 @@ description: |- For a complete working example using a GKE cluster, see [akuity/examples](https://github.com/akuity/examples/tree/main/terraform/akuity). +## Example Usage (Custom agent size) +{{ tffile "./examples/resources/akp_cluster/custom_agent_size.tf" }} + +## Example Usage (Auto agent size) +{{ tffile "./examples/resources/akp_cluster/auto_agent_size.tf" }} + +- This example uses the `auto` agent size, which will automatically scale the agent based on the number of applications in the cluster. `auto_upgrade_disabled` cannot be set to `true` when using `auto` agent size. + ## Example Usage (Exhaustive) {{ tffile "./examples/resources/akp_cluster/resource.tf" }} {{- end }}