From 08eafb41096352d09b252736b28ca17014523793 Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Sat, 23 Dec 2023 15:51:06 +1300 Subject: [PATCH] parse memory from a potentially humanized string to mb --- docs/resources/cluster.md | 2 +- go.mod | 2 +- minikube/generator/schema_builder.go | 95 +++++++++++++++-------- minikube/generator/schema_builder_test.go | 9 ++- minikube/lib/mock_minikube_client.go | 2 +- minikube/lib/mock_minikube_cluster.go | 2 +- minikube/lib/mock_minikube_downloader.go | 2 +- minikube/resource_cluster_test.go | 10 +-- minikube/schema_cluster.go | 9 ++- minikube/state_utils/memory.go | 45 +++++++++++ minikube/state_utils/memory_test.go | 62 +++++++++++++++ 11 files changed, 193 insertions(+), 47 deletions(-) create mode 100644 minikube/state_utils/memory.go create mode 100644 minikube/state_utils/memory_test.go diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md index 19c0f60..f9cac0b 100644 --- a/docs/resources/cluster.md +++ b/docs/resources/cluster.md @@ -158,7 +158,7 @@ resource "kubernetes_deployment" "deployment" { - `kvm_numa_count` (Number) Simulate numa node count in minikube, supported numa node count range is 1-8 (kvm2 driver only) - `kvm_qemu_uri` (String) The KVM QEMU connection URI. (kvm2 driver only) - `listen_address` (String) IP Address to use to expose ports (docker and podman driver only) -- `memory` (String) Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g) +- `memory` (String) Amount of RAM to allocate to Kubernetes (format: [(case-insensitive)], where unit = b, k, kb, m, mb, g or gb) - `mount` (Boolean) This will start the mount daemon and automatically mount files into minikube. - `mount_9p_version` (String) Specify the 9p version that the mount should use - `mount_gid` (String) Default group id used for the mount diff --git a/go.mod b/go.mod index b07b2a9..430091f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/docker/machine v0.16.2 github.com/golang/mock v1.6.0 + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-docs v0.16.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 @@ -99,7 +100,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-getter v1.7.3 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect diff --git a/minikube/generator/schema_builder.go b/minikube/generator/schema_builder.go index cf73e52..09dc56f 100644 --- a/minikube/generator/schema_builder.go +++ b/minikube/generator/schema_builder.go @@ -42,10 +42,12 @@ var computedFields []string = []string{ } type SchemaOverride struct { - Description string - Default string - Type SchemaType - DefaultFunc string + Description string + Default string + Type SchemaType + DefaultFunc string + StateFunc string + ValidateDiagFunc string } var updateFields = []string{ @@ -54,9 +56,11 @@ var updateFields = []string{ var schemaOverrides map[string]SchemaOverride = map[string]SchemaOverride{ "memory": { - Default: "4000mb", - Description: "Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g)", - Type: String, + Default: "4g", + Description: "Amount of RAM to allocate to Kubernetes (format: [(case-insensitive)], where unit = b, k, kb, m, mb, g or gb)", + Type: String, + StateFunc: "state_utils.MemoryConverter()", + ValidateDiagFunc: "state_utils.MemoryValidator()", }, "cpus": { Default: "2", @@ -99,12 +103,14 @@ func run(ctx context.Context, args ...string) (string, error) { } type SchemaEntry struct { - Parameter string - Default string - DefaultFunc string - Description string - Type SchemaType - ArrayType SchemaType + Parameter string + Default string + DefaultFunc string + StateFunc string + ValidateDiagFunc string + Description string + Type SchemaType + ArrayType SchemaType } type SchemaBuilder struct { @@ -194,6 +200,8 @@ func loadParameter(line string) SchemaEntry { schemaEntry.Default = val.Default schemaEntry.DefaultFunc = val.DefaultFunc schemaEntry.Type = val.Type + schemaEntry.StateFunc = val.StateFunc + schemaEntry.ValidateDiagFunc = val.ValidateDiagFunc } if schemaEntry.Type == String { @@ -207,19 +215,23 @@ func addEntry(entries []SchemaEntry, currentEntry SchemaEntry) ([]SchemaEntry, e switch currentEntry.Type { case String: entries = append(entries, SchemaEntry{ - Parameter: currentEntry.Parameter, - Default: fmt.Sprintf("\"%s\"", currentEntry.Default), - Type: currentEntry.Type, - Description: currentEntry.Description, - DefaultFunc: currentEntry.DefaultFunc, + Parameter: currentEntry.Parameter, + Default: fmt.Sprintf("\"%s\"", currentEntry.Default), + Type: currentEntry.Type, + Description: currentEntry.Description, + DefaultFunc: currentEntry.DefaultFunc, + StateFunc: currentEntry.StateFunc, + ValidateDiagFunc: currentEntry.ValidateDiagFunc, }) case Bool: entries = append(entries, SchemaEntry{ - Parameter: currentEntry.Parameter, - Default: currentEntry.Default, - Type: currentEntry.Type, - Description: currentEntry.Description, - DefaultFunc: currentEntry.DefaultFunc, + Parameter: currentEntry.Parameter, + Default: currentEntry.Default, + Type: currentEntry.Type, + Description: currentEntry.Description, + DefaultFunc: currentEntry.DefaultFunc, + StateFunc: currentEntry.StateFunc, + ValidateDiagFunc: currentEntry.ValidateDiagFunc, }) case Int: val, err := strconv.Atoi(currentEntry.Default) @@ -233,19 +245,23 @@ func addEntry(entries []SchemaEntry, currentEntry SchemaEntry) ([]SchemaEntry, e currentEntry.Description = fmt.Sprintf("%s (Configured in minutes)", currentEntry.Description) } entries = append(entries, SchemaEntry{ - Parameter: currentEntry.Parameter, - Default: strconv.Itoa(val), - Type: currentEntry.Type, - Description: currentEntry.Description, - DefaultFunc: currentEntry.DefaultFunc, + Parameter: currentEntry.Parameter, + Default: strconv.Itoa(val), + Type: currentEntry.Type, + Description: currentEntry.Description, + DefaultFunc: currentEntry.DefaultFunc, + StateFunc: currentEntry.StateFunc, + ValidateDiagFunc: currentEntry.ValidateDiagFunc, }) case Array: entries = append(entries, SchemaEntry{ - Parameter: currentEntry.Parameter, - Type: Array, - ArrayType: String, - Description: currentEntry.Description, - DefaultFunc: currentEntry.DefaultFunc, + Parameter: currentEntry.Parameter, + Type: Array, + ArrayType: String, + Description: currentEntry.Description, + DefaultFunc: currentEntry.DefaultFunc, + StateFunc: currentEntry.StateFunc, + ValidateDiagFunc: currentEntry.ValidateDiagFunc, }) } @@ -265,6 +281,9 @@ package minikube import ( "runtime" "os" + + "github.com/scott-the-programmer/terraform-provider-minikube/minikube/state_utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -348,6 +367,16 @@ var ( Default: %s,`, entry.Default) } + if entry.StateFunc != "" { + extraParams += fmt.Sprintf(` + StateFunc: %s,`, entry.StateFunc) + } + + if entry.ValidateDiagFunc != "" { + extraParams += fmt.Sprintf(` + ValidateDiagFunc: %s,`, entry.ValidateDiagFunc) + } + body = body + fmt.Sprintf(` "%s": { Type: %s, diff --git a/minikube/generator/schema_builder_test.go b/minikube/generator/schema_builder_test.go index 045a876..8b35cc1 100644 --- a/minikube/generator/schema_builder_test.go +++ b/minikube/generator/schema_builder_test.go @@ -15,6 +15,9 @@ package minikube import ( "runtime" "os" + + "github.com/scott-the-programmer/terraform-provider-minikube/minikube/state_utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -333,12 +336,14 @@ func TestOverride(t *testing.T) { assert.Equal(t, header+` "memory": { Type: schema.TypeString, - Description: "Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g)", + Description: "Amount of RAM to allocate to Kubernetes (format: [(case-insensitive)], where unit = b, k, kb, m, mb, g or gb)", Optional: true, ForceNew: true, - Default: "4000mb", + Default: "4g", + StateFunc: state_utils.MemoryConverter(), + ValidateDiagFunc: state_utils.MemoryValidator(), }, } diff --git a/minikube/lib/mock_minikube_client.go b/minikube/lib/mock_minikube_client.go index b04ed1b..853c888 100644 --- a/minikube/lib/mock_minikube_client.go +++ b/minikube/lib/mock_minikube_client.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: minikube_client.go -// package lib is a generated GoMock package. +// Package lib is a generated GoMock package. package lib import ( diff --git a/minikube/lib/mock_minikube_cluster.go b/minikube/lib/mock_minikube_cluster.go index 18b6c0c..fa8a74d 100644 --- a/minikube/lib/mock_minikube_cluster.go +++ b/minikube/lib/mock_minikube_cluster.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: minikube_cluster.go -// package lib is a generated GoMock package. +// Package lib is a generated GoMock package. package lib import ( diff --git a/minikube/lib/mock_minikube_downloader.go b/minikube/lib/mock_minikube_downloader.go index 2df3315..00c6d54 100644 --- a/minikube/lib/mock_minikube_downloader.go +++ b/minikube/lib/mock_minikube_downloader.go @@ -1,7 +1,7 @@ // Code generated by MockGen. DO NOT EDIT. // Source: minikube_downloader.go -// package lib is a generated GoMock package. +// Package lib is a generated GoMock package. package lib import ( diff --git a/minikube/resource_cluster_test.go b/minikube/resource_cluster_test.go index 529eebe..3ccead4 100644 --- a/minikube/resource_cluster_test.go +++ b/minikube/resource_cluster_test.go @@ -293,7 +293,7 @@ func getBaseMockClient(ctrl *gomock.Controller, clusterName string) *lib.MockClu MinikubeISO: defaultIso, KicBaseImage: clusterSchema["base_image"].Default.(string), Network: clusterSchema["network"].Default.(string), - Memory: 4000, + Memory: 4096, CPUs: 2, DiskSize: 20000, Driver: "some_driver", @@ -429,7 +429,7 @@ func testAcceptanceClusterConfig(driver string, clusterName string) string { driver = "%s" cluster_name = "%s" cpus = 2 - memory = "6000mb" + memory = "6GiB" addons = [ "dashboard", @@ -446,7 +446,7 @@ func testAcceptanceClusterConfig_Update(driver string, clusterName string) strin driver = "%s" cluster_name = "%s" cpus = 2 - memory = "6000mb" + memory = "6GiB" addons = [ "dashboard", @@ -464,7 +464,7 @@ func testAcceptanceClusterConfig_StorageProvisioner(driver string, clusterName s driver = "%s" cluster_name = "%s" cpus = 2 - memory = "6000mb" + memory = "6000GiB" addons = [ "dashboard", @@ -482,7 +482,7 @@ func testAcceptanceClusterConfig_OutOfOrderAddons(driver string, clusterName str driver = "%s" cluster_name = "%s" cpus = 2 - memory = "6000mb" + memory = "6000GiB" addons = [ "storage-provisioner", diff --git a/minikube/schema_cluster.go b/minikube/schema_cluster.go index eeb8158..44f8d62 100644 --- a/minikube/schema_cluster.go +++ b/minikube/schema_cluster.go @@ -5,6 +5,9 @@ package minikube import ( "runtime" "os" + + "github.com/scott-the-programmer/terraform-provider-minikube/minikube/state_utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -648,12 +651,14 @@ var ( "memory": { Type: schema.TypeString, - Description: "Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g)", + Description: "Amount of RAM to allocate to Kubernetes (format: [(case-insensitive)], where unit = b, k, kb, m, mb, g or gb)", Optional: true, ForceNew: true, - Default: "4000mb", + Default: "4g", + StateFunc: state_utils.MemoryConverter(), + ValidateDiagFunc: state_utils.MemoryValidator(), }, "mount": { diff --git a/minikube/state_utils/memory.go b/minikube/state_utils/memory.go new file mode 100644 index 0000000..80495d7 --- /dev/null +++ b/minikube/state_utils/memory.go @@ -0,0 +1,45 @@ +package state_utils + +import ( + "errors" + "strconv" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + pkgutil "k8s.io/minikube/pkg/util" +) + +func MemoryConverter() schema.SchemaStateFunc { + return func(val interface{}) string { + memory, ok := val.(string) + if !ok { + panic(errors.New("memory flag is not a string")) + } + memoryMb, err := pkgutil.CalculateSizeInMB(memory) + if err != nil { + panic(errors.New("invalid memory value")) + } + + return strconv.Itoa(memoryMb) + "mb" + + } + +} + +func MemoryValidator() schema.SchemaValidateDiagFunc { + return schema.SchemaValidateDiagFunc(func(val interface{}, path cty.Path) diag.Diagnostics { + memory, ok := val.(string) + if !ok { + diag := diag.FromErr(errors.New("memory flag is not a string")) + return diag + } + _, err := pkgutil.CalculateSizeInMB(memory) + if err != nil { + diag := diag.FromErr(errors.New("invalid memory value")) + return diag + } + return nil + }) +} diff --git a/minikube/state_utils/memory_test.go b/minikube/state_utils/memory_test.go new file mode 100644 index 0000000..482fda1 --- /dev/null +++ b/minikube/state_utils/memory_test.go @@ -0,0 +1,62 @@ +package state_utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMemoryConverter(t *testing.T) { + converter := MemoryConverter() + + t.Run("ValidMemoryFlagG", func(t *testing.T) { + result := converter("2G") + assert.Equal(t, "2048mb", result) + }) + + t.Run("ValidMemoryFlagGb", func(t *testing.T) { + result := converter("2Gb") + assert.Equal(t, "2048mb", result) + }) + + t.Run("Equivalence", func(t *testing.T) { + resultg := converter("2Gb") + resultm := converter("2048mb") + assert.Equal(t, resultm, resultg) + }) + + t.Run("InvalidMemoryFlag", func(t *testing.T) { + assert.PanicsWithError(t, "memory flag is not a string", func() { + converter(123) + }) + }) + + t.Run("InvalidMemoryValue", func(t *testing.T) { + assert.PanicsWithError(t, "invalid memory value", func() { + converter("invalid") + }) + }) +} + +func TestMemoryValidator(t *testing.T) { + validator := MemoryValidator() + + t.Run("ValidMemoryFlag", func(t *testing.T) { + diags := validator("2G", nil) + assert.Empty(t, diags) + }) + + t.Run("InvalidMemoryFlag", func(t *testing.T) { + diags := validator(123, nil) + require.Len(t, diags, 1) + assert.Equal(t, diags[0].Summary, "memory flag is not a string") + }) + + t.Run("InvalidMemoryValue", func(t *testing.T) { + diags := validator("invalid", nil) + require.Len(t, diags, 1) + assert.True(t, diags.HasError()) + }) + +}