diff --git a/docs/resources/service.md b/docs/resources/service.md index 036e291c5..b681047c6 100644 --- a/docs/resources/service.md +++ b/docs/resources/service.md @@ -370,7 +370,7 @@ Optional: - **config_name** (String) Name of the config that this references, but this is just provided for lookup/display purposes. The config in the reference will be identified by its ID - **file_gid** (String) Represents the file GID. Defaults to `0`. -- **file_mode** (Number) Represents represents the FileMode of the file. Defaults to `0o444`. +- **file_mode** (String) Represents represents the FileMode of the file. Defaults to `0o444`. - **file_uid** (String) Represents the file UID. Defaults to `0`. @@ -515,7 +515,7 @@ Required: Optional: - **file_gid** (String) Represents the file GID. Defaults to `0` -- **file_mode** (Number) Represents represents the FileMode of the file. Defaults to `0o444` +- **file_mode** (String) Represents represents the FileMode of the file. Defaults to `0o444` - **file_uid** (String) Represents the file UID. Defaults to `0` - **secret_name** (String) Name of the secret that this references, but this is just provided for lookup/display purposes. The config in the reference will be identified by its ID diff --git a/internal/provider/file_mode.go b/internal/provider/file_mode.go new file mode 100644 index 000000000..93e60a8a4 --- /dev/null +++ b/internal/provider/file_mode.go @@ -0,0 +1,56 @@ +package provider + +import ( + "fmt" + "log" + "os" + "strconv" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const defaultFileMode = "0444" + +func convertStrToFileMode(s string) (os.FileMode, error) { + if s == "" { + s = defaultFileMode + } + a, err := strconv.ParseInt(s, 8, 32) + if err != nil { + return 0, err + } + if a < 0 { + return 0, fmt.Errorf("file_mode must be greater equal than 0: %d", a) + } + if a > 0o4777 { + return 0, fmt.Errorf("file_mode must be less equal than 0o4777: %d", a) + } + return os.FileMode(a), nil +} + +func fileModeDiffSuppressFunc(k, oldV, newV string, d *schema.ResourceData) bool { + log.Printf("[DEBUG] DiffSuppressFunc(key: %s, old value: %s, new value: %s)", k, oldV, newV) + if oldV == newV { + return true + } + a, err := convertStrToFileMode(oldV) + if err != nil { + log.Printf("[DEBUG] DiffSuppressFunc(key: %s, old value: %s, new value: %s): old value is invalid: %s", k, oldV, newV, err.Error()) + return false + } + b, err := convertStrToFileMode(newV) + if err != nil { + log.Printf("[DEBUG] DiffSuppressFunc(key: %s, old value: %s, new value: %s): new value is invalid: %s", k, oldV, newV, err.Error()) + return false + } + return a == b +} + +func fileModeSchemaValidateDiagFunc(value interface{}, ctyPath cty.Path) diag.Diagnostics { + if _, err := convertStrToFileMode(value.(string)); err != nil { + return diag.FromErr(err) + } + return nil +} diff --git a/internal/provider/helper.go b/internal/provider/helper.go new file mode 100644 index 000000000..2b6f3cb94 --- /dev/null +++ b/internal/provider/helper.go @@ -0,0 +1,21 @@ +package provider + +import "hash/crc32" + +// hashString hashes a string to a unique hashcode. +// +// crc32 returns a uint32, but for our use we need +// and non negative integer. Here we cast to an integer +// and invert it if the result is negative. +// https://www.terraform.io/docs/extend/guides/v2-upgrade-guide.html#removal-of-helper-hashcode-package +func hashString(s string) int { + v := int(crc32.ChecksumIEEE([]byte(s))) + if v >= 0 { + return v + } + if -v >= 0 { + return -v + } + // v == MinInt + return 0 +} diff --git a/internal/provider/resource_docker_service.go b/internal/provider/resource_docker_service.go index d96c158c6..dab33a8ff 100644 --- a/internal/provider/resource_docker_service.go +++ b/internal/provider/resource_docker_service.go @@ -439,11 +439,12 @@ func resourceDockerService() *schema.Resource { Optional: true, }, "file_mode": { - Type: schema.TypeInt, + Type: schema.TypeString, Description: "Represents represents the FileMode of the file. Defaults to `0o444`", - Default: 0o444, + Default: defaultFileMode, Optional: true, - ValidateDiagFunc: validateIntegerGeqThan(0), + ValidateDiagFunc: fileModeSchemaValidateDiagFunc, + DiffSuppressFunc: fileModeDiffSuppressFunc, }, }, }, @@ -452,6 +453,9 @@ func resourceDockerService() *schema.Resource { Type: schema.TypeSet, Description: "References to zero or more configs that will be exposed to the service", Optional: true, + Set: func(v interface{}) int { + return hashString(v.(map[string]interface{})["config_id"].(string)) + }, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "config_id": { @@ -482,11 +486,12 @@ func resourceDockerService() *schema.Resource { Optional: true, }, "file_mode": { - Type: schema.TypeInt, + Type: schema.TypeString, Description: "Represents represents the FileMode of the file. Defaults to `0o444`.", - Default: 0o444, + Default: defaultFileMode, Optional: true, - ValidateDiagFunc: validateIntegerGeqThan(0), + ValidateDiagFunc: fileModeSchemaValidateDiagFunc, + DiffSuppressFunc: fileModeDiffSuppressFunc, }, }, },