Skip to content

Commit

Permalink
Merge pull request #30 from pluralsh/marcin/eng-1976-allow-metadata-t…
Browse files Browse the repository at this point in the history
…o-be-set-in-cluster-resource-w-terraform

feat: Allow metadata and agent repo URL to be set in cluster resource
  • Loading branch information
maciaszczykm authored Apr 17, 2024
2 parents f838217 + 93f792c commit ffa21b4
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 35 deletions.
1 change: 1 addition & 0 deletions docs/data-sources/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ A representation of a cluster you can deploy to.
- `cloud` (String) The cloud provider used to create this cluster.
- `desired_version` (String) Desired Kubernetes version for this cluster.
- `inserted_at` (String) Creation date of this cluster.
- `metadata` (String) Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons).
- `name` (String) Human-readable name of this cluster, that also translates to cloud resource name.
- `node_pools` (Attributes Map) Map of node pool specs managed by this cluster, where the key is name of the node pool and value contains the spec. (see [below for nested schema](#nestedatt--node_pools))
- `protect` (Boolean) If set to `true` then this cluster cannot be deleted.
Expand Down
4 changes: 3 additions & 1 deletion docs/resources/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ A representation of a cluster you can deploy to.
- `cloud` (String) The cloud provider used to create this cluster.
- `cloud_settings` (Attributes) Cloud-specific settings for this cluster. (see [below for nested schema](#nestedatt--cloud_settings))
- `handle` (String) A short, unique human-readable name used to identify this cluster. Does not necessarily map to the cloud resource name.
- `helm_values` (String) Additional helm values you'd like to use in deployment agent helm installs. This is useful for BYOK clusters that need to use custom images or other constructs.
- `helm_repo_url` (String) Helm repository URL you'd like to use in deployment agent Helm install.
- `helm_values` (String) Additional Helm values you'd like to use in deployment agent Helm installs. This is useful for BYOK clusters that need to use custom images or other constructs.
- `kubeconfig` (Attributes) (see [below for nested schema](#nestedatt--kubeconfig))
- `metadata` (String) Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons).
- `node_pools` (Attributes Map) **Experimental, not ready for production use.** Map of node pool specs managed by this cluster, where the key is name of the node pool and value contains the spec. Leave empty for bring your own cluster. (see [below for nested schema](#nestedatt--node_pools))
- `protect` (Boolean) If set to `true` then this cluster cannot be deleted.
- `provider_id` (String) Provider used to create this cluster. Leave empty for bring your own cluster.
Expand Down
33 changes: 20 additions & 13 deletions example/azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ provider "plural" {
use_cli = true
}

resource "plural_provider" "azure_provider" {
name = "azure"
cloud = "azure"
cloud_settings = {
azure = {
# subscription_id = "" # Required, can be sourced from PLURAL_AZURE_SUBSCRIPTION_ID
# tenant_id = "" # Required, can be sourced from PLURAL_AZURE_TENANT_ID
# client_id = "" # Required, can be sourced from PLURAL_AZURE_CLIENT_ID
# client_secret = "" # Required, can be sourced from PLURAL_AZURE_CLIENT_SECRET
}
}
}
#resource "plural_provider" "azure_provider" {
# name = "azure"
# cloud = "azure"
# cloud_settings = {
# azure = {
# # subscription_id = "" # Required, can be sourced from PLURAL_AZURE_SUBSCRIPTION_ID
# # tenant_id = "" # Required, can be sourced from PLURAL_AZURE_TENANT_ID
# # client_id = "" # Required, can be sourced from PLURAL_AZURE_CLIENT_ID
# # client_secret = "" # Required, can be sourced from PLURAL_AZURE_CLIENT_SECRET
# }
# }
#}

data "plural_provider" "azure_provider" {
cloud = "aws"
cloud = "azure"
}

resource "plural_cluster" "azure_cluster" {
Expand All @@ -43,6 +43,13 @@ resource "plural_cluster" "azure_cluster" {
location = "eastus"
}
}
metadata = jsonencode({
test1 = "test"
test2 = false
test3 = jsonencode({
abc = false
})
})
tags = {
"managed-by" = "terraform-provider-plural"
}
Expand Down
17 changes: 10 additions & 7 deletions example/byok/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ provider "plural" {
use_cli = true
}

resource "plural_cluster" "byok_workload_cluster" {
name = "workload-cluster-tf"
handle = "wctf"
resource "plural_cluster" "byok" {
name = "byok"
protect = "false"
kubeconfig = {
# Required, can be sourced from environment variables
# export PLURAL_KUBE_CONFIG_PATH to read from local file
}
metadata = jsonencode({
test1 = "test"
test2 = false
test3 = jsonencode({
abc = false
})
})
helm_repo_url = "https://pluralsh.github.io/deployment-operator"
tags = {
"managed-by" = "terraform-provider-plural"
}
}

#data "plural_cluster" "byok_workload_cluster" {
# handle = "wctf"
#}
16 changes: 16 additions & 0 deletions example/cluster_data/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
terraform {
required_providers {
plural = {
source = "pluralsh/plural"
version = "0.0.1"
}
}
}

provider "plural" {
use_cli = true
}

data "plural_cluster" "cluster" {
handle = "mgmt"
}
1 change: 0 additions & 1 deletion example/gcp/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,5 @@ resource "plural_cluster" "gcp_workload_cluster" {
tags = {
"managed-by" = "terraform-provider-plural"
}

depends_on = [plural_provider.gcp_provider]
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/mitchellh/go-homedir v1.1.0
github.com/pluralsh/console-client-go v0.0.96
github.com/pluralsh/console-client-go v0.1.16
github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37
github.com/pluralsh/polly v0.1.7
github.com/samber/lo v1.38.1
Expand All @@ -21,6 +21,7 @@ require (
helm.sh/helm/v3 v3.11.2
k8s.io/apimachinery v0.26.4
k8s.io/client-go v0.26.4
sigs.k8s.io/yaml v1.3.0
)

require (
Expand Down Expand Up @@ -353,7 +354,6 @@ require (
sigs.k8s.io/kustomize/api v0.13.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace (
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -856,14 +856,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pluralsh/console-client-go v0.0.96 h1:ZllMRmbmCiO3mvPNaLf5MeaniI5MHbuYnDUgnjkLiCM=
github.com/pluralsh/console-client-go v0.0.96/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo=
github.com/pluralsh/console-client-go v0.1.16 h1:f+d4ah3r+dAJ6hSMFsAmTlps4IsmExCzkCOwUpSYkbs=
github.com/pluralsh/console-client-go v0.1.16/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo=
github.com/pluralsh/gqlclient v1.11.0 h1:FfXW7FiEJLHOfTAa7NxDb8jb3aMZNIpCAcG+bg8uHYA=
github.com/pluralsh/gqlclient v1.11.0/go.mod h1:qSXKUlio1F2DRPy8el4oFYsmpKbkUYspgPB87T4it5I=
github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37 h1:DBnaKvKmbTbKwbkrh/2gJBwyHYfaXdxeT3UGh+94K4g=
github.com/pluralsh/plural-cli v0.8.5-0.20240216094552-efc34ee6de37/go.mod h1:ZjvgOn9wE5vQXkImmoQknKKP0/68Ph6Hj19fUE1WdDU=
github.com/pluralsh/polly v0.1.4 h1:Kz90peCgvsfF3ERt8cujr5TR9z4wUlqQE60Eg09ZItY=
github.com/pluralsh/polly v0.1.4/go.mod h1:Yo1/jcW+4xwhWG+ZJikZy4J4HJkMNPZ7sq5auL2c/tY=
github.com/pluralsh/polly v0.1.7 h1:MUuTb6rCUV1doisaFGC+iz+33ZPU4FZHOb/kFwSDkjw=
github.com/pluralsh/polly v0.1.7/go.mod h1:Yo1/jcW+4xwhWG+ZJikZy4J4HJkMNPZ7sq5auL2c/tY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
4 changes: 4 additions & 0 deletions internal/datasource/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func (d *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
Computed: true,
ElementType: types.StringType,
},
"metadata": schema.StringAttribute{
MarkdownDescription: "Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons).",
Computed: true,
},
"node_pools": schema.MapNestedAttribute{
Description: "Map of node pool specs managed by this cluster, where the key is name of the node pool and value contains the spec.",
MarkdownDescription: "Map of node pool specs managed by this cluster, where the key is name of the node pool and value contains the spec.",
Expand Down
10 changes: 10 additions & 0 deletions internal/datasource/cluster_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package datasource

import (
"context"
"encoding/json"
"fmt"

"terraform-provider-plural/internal/common"

Expand All @@ -20,17 +22,25 @@ type cluster struct {
Cloud types.String `tfsdk:"cloud"`
Protect types.Bool `tfsdk:"protect"`
Tags types.Map `tfsdk:"tags"`
Metadata types.String `tfsdk:"metadata"`
NodePools types.Map `tfsdk:"node_pools"`
}

func (c *cluster) From(cl *console.ClusterFragment, ctx context.Context, d diag.Diagnostics) {
metadata, err := json.Marshal(cl.Metadata)
if err != nil {
d.AddError("Provider Error", fmt.Sprintf("Cannot marshall metadata, got error: %s", err))
return
}

c.Id = types.StringValue(cl.ID)
c.InsertedAt = types.StringPointerValue(cl.InsertedAt)
c.Name = types.StringValue(cl.Name)
c.Handle = types.StringPointerValue(cl.Handle)
c.DesiredVersion = types.StringPointerValue(cl.Version)
c.Protect = types.BoolPointerValue(cl.Protect)
c.Tags = common.ClusterTagsFrom(cl.Tags, d)
c.Metadata = types.StringValue(string(metadata))
c.ProviderId = common.ClusterProviderIdFrom(cl.Provider)
c.NodePools = common.ClusterNodePoolsFrom(cl.NodePools, c.NodePools, ctx, d)
}
2 changes: 1 addition & 1 deletion internal/resource/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (r *clusterResource) Create(ctx context.Context, req resource.CreateRequest
return
}

handler, err := NewOperatorHandler(ctx, data.GetKubeconfig(), data.HelmValues.ValueStringPointer(), r.consoleUrl)
handler, err := NewOperatorHandler(ctx, data.GetKubeconfig(), data.HelmRepoUrl.ValueString(), data.HelmValues.ValueStringPointer(), r.consoleUrl)
if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to init operator handler, got error: %s", err))
return
Expand Down
28 changes: 26 additions & 2 deletions internal/resource/cluster_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package resource

import (
"context"
"encoding/json"
"fmt"

"terraform-provider-plural/internal/common"

Expand All @@ -22,9 +24,11 @@ type cluster struct {
Cloud types.String `tfsdk:"cloud"`
Protect types.Bool `tfsdk:"protect"`
Tags types.Map `tfsdk:"tags"`
Metadata types.String `tfsdk:"metadata"`
Bindings *common.ClusterBindings `tfsdk:"bindings"`
NodePools types.Map `tfsdk:"node_pools"`
CloudSettings *ClusterCloudSettings `tfsdk:"cloud_settings"`
HelmRepoUrl types.String `tfsdk:"helm_repo_url"`
HelmValues types.String `tfsdk:"helm_values"`
Kubeconfig *Kubeconfig `tfsdk:"kubeconfig"`
}
Expand Down Expand Up @@ -76,6 +80,7 @@ func (c *cluster) Attributes(ctx context.Context, d diag.Diagnostics) console.Cl
WriteBindings: c.Bindings.WriteAttributes(),
Tags: c.TagsAttribute(ctx, d),
NodePools: c.NodePoolsAttribute(ctx, d),
Metadata: c.Metadata.ValueStringPointer(),
}
}

Expand All @@ -89,29 +94,48 @@ func (c *cluster) UpdateAttributes(ctx context.Context, d diag.Diagnostics) cons
}

func (c *cluster) From(cl *console.ClusterFragment, ctx context.Context, d diag.Diagnostics) {
metadata, err := json.Marshal(cl.Metadata)
if err != nil {
d.AddError("Provider Error", fmt.Sprintf("Cannot marshall metadata, got error: %s", err))
return
}

c.Id = types.StringValue(cl.ID)
c.InsertedAt = types.StringPointerValue(cl.InsertedAt)
c.Name = types.StringValue(cl.Name)
c.Handle = types.StringPointerValue(cl.Handle)
c.DesiredVersion = types.StringPointerValue(cl.Version)
c.DesiredVersion = c.ClusterVersionFrom(cl.Version, cl.CurrentVersion)
c.Protect = types.BoolPointerValue(cl.Protect)
c.Tags = common.ClusterTagsFrom(cl.Tags, d)
c.ProviderId = common.ClusterProviderIdFrom(cl.Provider)
c.NodePools = common.ClusterNodePoolsFrom(cl.NodePools, c.NodePools, ctx, d)
c.Metadata = types.StringValue(string(metadata))
}

func (c *cluster) FromCreate(cc *console.CreateCluster, ctx context.Context, d diag.Diagnostics) {
c.Id = types.StringValue(cc.CreateCluster.ID)
c.InsertedAt = types.StringPointerValue(cc.CreateCluster.InsertedAt)
c.Name = types.StringValue(cc.CreateCluster.Name)
c.Handle = types.StringPointerValue(cc.CreateCluster.Handle)
c.DesiredVersion = types.StringPointerValue(cc.CreateCluster.Version)
c.DesiredVersion = c.ClusterVersionFrom(cc.CreateCluster.Version, cc.CreateCluster.CurrentVersion)
c.Protect = types.BoolPointerValue(cc.CreateCluster.Protect)
c.Tags = common.ClusterTagsFrom(cc.CreateCluster.Tags, d)
c.ProviderId = common.ClusterProviderIdFrom(cc.CreateCluster.Provider)
c.NodePools = common.ClusterNodePoolsFrom(cc.CreateCluster.NodePools, c.NodePools, ctx, d)
}

func (c *cluster) ClusterVersionFrom(version, currentVersion *string) types.String {
if version != nil && len(*version) > 0 {
return types.StringPointerValue(version)
}

if currentVersion != nil && len(*currentVersion) > 0 {
return types.StringPointerValue(currentVersion)
}

return types.StringValue("unknown")
}

func (c *cluster) HasKubeconfig() bool {
return c.Kubeconfig != nil || (c.CloudSettings != nil && c.CloudSettings.BYOK != nil && c.CloudSettings.BYOK.Kubeconfig != nil)
}
Expand Down
10 changes: 8 additions & 2 deletions internal/resource/cluster_operator_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ import (

type OperatorHandler struct {
ctx context.Context

// kubeconfig is a model.Kubeconfig data model read from terraform
kubeconfig *Kubeconfig

// url is an url to the Console API, i.e. https://console.mycluster.onplural.sh
url string

// repoUrl is an URL of the deployment agent chart.
repoUrl string

// additional values used on install
vals map[string]interface{}

Expand Down Expand Up @@ -67,7 +72,7 @@ func (oh *OperatorHandler) init() error {
}

func (oh *OperatorHandler) initRepo() error {
return helm.AddRepo(console.ReleaseName, console.RepoUrl)
return helm.AddRepo(console.ReleaseName, oh.repoUrl)
}

func (oh *OperatorHandler) initChart() error {
Expand Down Expand Up @@ -171,7 +176,7 @@ func (oh *OperatorHandler) Uninstall() error {
return err
}

func NewOperatorHandler(ctx context.Context, kubeconfig *Kubeconfig, values *string, consoleUrl string) (*OperatorHandler, error) {
func NewOperatorHandler(ctx context.Context, kubeconfig *Kubeconfig, repoUrl string, values *string, consoleUrl string) (*OperatorHandler, error) {
vals := map[string]interface{}{}
if values != nil {
if err := yaml.Unmarshal([]byte(*values), &vals); err != nil {
Expand All @@ -182,6 +187,7 @@ func NewOperatorHandler(ctx context.Context, kubeconfig *Kubeconfig, values *str
handler := &OperatorHandler{
ctx: ctx,
kubeconfig: kubeconfig,
repoUrl: repoUrl,
url: consoleUrl,
vals: vals,
}
Expand Down
20 changes: 18 additions & 2 deletions internal/resource/cluster_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package resource

import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/pluralsh/plural-cli/pkg/console"

"terraform-provider-plural/internal/common"
"terraform-provider-plural/internal/defaults"
Expand Down Expand Up @@ -75,6 +76,12 @@ func (r *clusterResource) schema() schema.Schema {
path.MatchRoot("cloud")),
},
},
"metadata": schema.StringAttribute{
Description: "Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons).",
MarkdownDescription: "Arbitrary JSON metadata to store user-specific state of this cluster (e.g. IAM roles for add-ons).",
Optional: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
"cloud": schema.StringAttribute{
Description: "The cloud provider used to create this cluster.",
MarkdownDescription: "The cloud provider used to create this cluster.",
Expand Down Expand Up @@ -108,9 +115,17 @@ func (r *clusterResource) schema() schema.Schema {
},
PlanModifiers: []planmodifier.Object{objectplanmodifier.RequiresReplace()},
},
"helm_repo_url": schema.StringAttribute{
Description: "Helm repository URL you'd like to use in deployment agent Helm install.",
MarkdownDescription: "Helm repository URL you'd like to use in deployment agent Helm install.",
Optional: true,
Computed: true,
Default: stringdefault.StaticString(console.RepoUrl),
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
},
"helm_values": schema.StringAttribute{
Description: "Additional helm values you'd like to use in deployment agent helm installs. This is useful for BYOK clusters that need to use custom images or other constructs.",
MarkdownDescription: "Additional helm values you'd like to use in deployment agent helm installs. This is useful for BYOK clusters that need to use custom images or other constructs.",
Description: "Additional Helm values you'd like to use in deployment agent Helm installs. This is useful for BYOK clusters that need to use custom images or other constructs.",
MarkdownDescription: "Additional Helm values you'd like to use in deployment agent Helm installs. This is useful for BYOK clusters that need to use custom images or other constructs.",
Optional: true,
},
"kubeconfig": r.kubeconfigSchema(false),
Expand All @@ -119,6 +134,7 @@ func (r *clusterResource) schema() schema.Schema {
MarkdownDescription: "**Experimental, not ready for production use.** Map of node pool specs managed by this cluster, where the key is name of the node pool and value contains the spec. Leave empty for bring your own cluster.",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.Map{mapplanmodifier.UseStateForUnknown()},
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"name": schema.StringAttribute{
Expand Down

0 comments on commit ffa21b4

Please sign in to comment.