diff --git a/docker/resource_docker_plugin.go b/docker/resource_docker_plugin.go index cfb3712cb..e4207374c 100644 --- a/docker/resource_docker_plugin.go +++ b/docker/resource_docker_plugin.go @@ -14,11 +14,13 @@ func resourceDockerPlugin() *schema.Resource { State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ - "plugin_reference": { - Type: schema.TypeString, - Description: "Docker Plugin Reference", - Required: true, - ForceNew: true, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Docker Plugin name", + DiffSuppressFunc: diffSuppressFuncPluginName, + ValidateFunc: validateFuncPluginName, }, "alias": { Type: schema.TypeString, @@ -68,6 +70,11 @@ func resourceDockerPlugin() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "plugin_reference": { + Type: schema.TypeString, + Description: "Docker Plugin Reference", + Computed: true, + }, "force_destroy": { Type: schema.TypeBool, diff --git a/docker/resource_docker_plugin_funcs.go b/docker/resource_docker_plugin_funcs.go index 171c7a5f0..6b15efe9f 100644 --- a/docker/resource_docker_plugin_funcs.go +++ b/docker/resource_docker_plugin_funcs.go @@ -7,6 +7,7 @@ import ( "log" "strings" + "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -35,6 +36,33 @@ func complementTag(image string) string { return image + ":latest" } +func normalizePluginName(name string) (string, error) { + ref, err := reference.ParseAnyReference(name) + if err != nil { + return "", fmt.Errorf("parse the plugin name: %w", err) + } + return complementTag(ref.String()), nil +} + +func diffSuppressFuncPluginName(k, oldV, newV string, d *schema.ResourceData) bool { + o, err := normalizePluginName(oldV) + if err != nil { + return false + } + n, err := normalizePluginName(newV) + if err != nil { + return false + } + return o == n +} + +func validateFuncPluginName(val interface{}, key string) (warns []string, errs []error) { + if _, err := normalizePluginName(val.(string)); err != nil { + return warns, append(errs, fmt.Errorf("%s is invalid: %w", key, err)) + } + return +} + func getDockerPluginGrantPermissions(src interface{}) func(types.PluginPrivileges) (bool, error) { grantPermissionsSet := src.(*schema.Set) grantPermissions := make(map[string]map[string]struct{}, grantPermissionsSet.Len()) @@ -69,11 +97,11 @@ func getDockerPluginGrantPermissions(src interface{}) func(types.PluginPrivilege func resourceDockerPluginCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ProviderConfig).DockerClient ctx := context.Background() - pluginRef := d.Get("plugin_reference").(string) + pluginName := d.Get("name").(string) alias := d.Get("alias").(string) - log.Printf("[DEBUG] Install a Docker plugin " + pluginRef) + log.Printf("[DEBUG] Install a Docker plugin " + pluginName) opts := types.PluginInstallOptions{ - RemoteRef: pluginRef, + RemoteRef: pluginName, AcceptAllPermissions: d.Get("grant_all_permissions").(bool), Disabled: !d.Get("enabled").(bool), // TODO support other settings @@ -84,10 +112,10 @@ func resourceDockerPluginCreate(d *schema.ResourceData, meta interface{}) error } body, err := client.PluginInstall(ctx, alias, opts) if err != nil { - return fmt.Errorf("install a Docker plugin "+pluginRef+": %w", err) + return fmt.Errorf("install a Docker plugin "+pluginName+": %w", err) } _, _ = ioutil.ReadAll(body) - key := pluginRef + key := pluginName if alias != "" { key = alias } @@ -103,6 +131,7 @@ func setDockerPlugin(d *schema.ResourceData, plugin *types.Plugin) { d.SetId(plugin.ID) d.Set("plugin_reference", plugin.PluginReference) d.Set("alias", plugin.Name) + d.Set("name", plugin.PluginReference) d.Set("enabled", plugin.Enabled) // TODO support other settings // https://docs.docker.com/engine/reference/commandline/plugin_set/#extended-description diff --git a/docker/resource_docker_plugin_test.go b/docker/resource_docker_plugin_test.go index 6afb5c616..e57b96223 100644 --- a/docker/resource_docker_plugin_test.go +++ b/docker/resource_docker_plugin_test.go @@ -151,6 +151,7 @@ func TestAccDockerPlugin_basic(t *testing.T) { ResourceName: resourceName, Config: testAccDockerPluginMinimum, Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "plugin_reference", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "alias", "tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "enabled", "true"), @@ -160,6 +161,7 @@ func TestAccDockerPlugin_basic(t *testing.T) { ResourceName: resourceName, Config: testAccDockerPluginAlias, Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "plugin_reference", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "alias", "sample:latest"), resource.TestCheckResourceAttr(resourceName, "enabled", "true"), @@ -169,6 +171,7 @@ func TestAccDockerPlugin_basic(t *testing.T) { ResourceName: resourceName, Config: testAccDockerPluginDisableWhenSet, Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "plugin_reference", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "alias", "sample:latest"), resource.TestCheckResourceAttr(resourceName, "enabled", "true"), @@ -181,6 +184,7 @@ func TestAccDockerPlugin_basic(t *testing.T) { ResourceName: resourceName, Config: testAccDockerPluginDisabled, Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "plugin_reference", "docker.io/tiborvass/sample-volume-plugin:latest"), resource.TestCheckResourceAttr(resourceName, "alias", "sample:latest"), resource.TestCheckResourceAttr(resourceName, "enabled", "false"), @@ -208,6 +212,7 @@ func TestAccDockerPlugin_grantAllPermissions(t *testing.T) { ResourceName: resourceName, Config: testAccDockerPluginGrantAllPermissions, Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "docker.io/vieux/sshfs:latest"), resource.TestCheckResourceAttr(resourceName, "plugin_reference", "docker.io/vieux/sshfs:latest"), resource.TestCheckResourceAttr(resourceName, "alias", "vieux/sshfs:latest"), resource.TestCheckResourceAttr(resourceName, "grant_all_permissions", "true"), @@ -231,6 +236,7 @@ func TestAccDockerPlugin_grantPermissions(t *testing.T) { ResourceName: resourceName, Config: testAccDockerPluginGrantPermissions, Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", "docker.io/vieux/sshfs:latest"), resource.TestCheckResourceAttr(resourceName, "plugin_reference", "docker.io/vieux/sshfs:latest"), resource.TestCheckResourceAttr(resourceName, "alias", "vieux/sshfs:latest"), ), @@ -245,24 +251,24 @@ func TestAccDockerPlugin_grantPermissions(t *testing.T) { const testAccDockerPluginMinimum = ` resource "docker_plugin" "test" { - plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" - force_destroy = true + name = "docker.io/tiborvass/sample-volume-plugin:latest" + force_destroy = true }` const testAccDockerPluginAlias = ` resource "docker_plugin" "test" { - plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" + name = "docker.io/tiborvass/sample-volume-plugin:latest" alias = "sample:latest" force_destroy = true }` const testAccDockerPluginDisableWhenSet = ` resource "docker_plugin" "test" { - plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" - alias = "sample:latest" - grant_all_permissions = true - force_destroy = true - enable_timeout = 60 + name = "docker.io/tiborvass/sample-volume-plugin:latest" + alias = "sample:latest" + grant_all_permissions = true + force_destroy = true + enable_timeout = 60 env = [ "DEBUG=1" ] @@ -270,13 +276,13 @@ resource "docker_plugin" "test" { const testAccDockerPluginDisabled = ` resource "docker_plugin" "test" { - plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" - alias = "sample:latest" - enabled = false - grant_all_permissions = true - force_destroy = true - force_disable = true - enable_timeout = 60 + name = "docker.io/tiborvass/sample-volume-plugin:latest" + alias = "sample:latest" + enabled = false + grant_all_permissions = true + force_destroy = true + force_disable = true + enable_timeout = 60 env = [ "DEBUG=1" ] @@ -285,7 +291,7 @@ resource "docker_plugin" "test" { // To install this plugin, it is required to grant required permissions. const testAccDockerPluginGrantAllPermissions = ` resource "docker_plugin" "test" { - plugin_reference = "docker.io/vieux/sshfs:latest" + name = "docker.io/vieux/sshfs:latest" grant_all_permissions = true force_destroy = true }` @@ -293,8 +299,8 @@ resource "docker_plugin" "test" { // To install this plugin, it is required to grant required permissions. const testAccDockerPluginGrantPermissions = ` resource "docker_plugin" "test" { - plugin_reference = "docker.io/vieux/sshfs:latest" - force_destroy = true + name = "vieux/sshfs" + force_destroy = true grant_permissions { name = "network" value = [ diff --git a/go.mod b/go.mod index ade4195fe..07b48e294 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/Microsoft/hcsshim v0.8.9 // indirect github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect github.com/docker/cli v0.0.0-20200303215952-eb310fca4956 // v19.03.8 - github.com/docker/distribution v0.0.0-20180522175653-f0cc92778478 // indirect + github.com/docker/distribution v0.0.0-20180522175653-f0cc92778478 github.com/docker/docker v0.7.3-0.20190525203055-f25e0c6f3093 github.com/docker/docker-credential-helpers v0.6.3 github.com/docker/go-connections v0.4.0 diff --git a/website/docs/r/plugin.html.markdown b/website/docs/r/plugin.html.markdown index 27cd250d6..8cd4489b0 100644 --- a/website/docs/r/plugin.html.markdown +++ b/website/docs/r/plugin.html.markdown @@ -14,14 +14,14 @@ Manages the lifecycle of a Docker plugin. ```hcl resource "docker_plugin" "sample-volume-plugin" { - plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" + name = "docker.io/tiborvass/sample-volume-plugin:latest" } ``` ```hcl resource "docker_plugin" "sample-volume-plugin" { - plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" - alias = "sample-volume-plugin:latest" + name = "tiborvass/sample-volume-plugin" + alias = "sample-volume-plugin" enabled = false grant_all_permissions = true force_destroy = true @@ -37,7 +37,7 @@ resource "docker_plugin" "sample-volume-plugin" { The following arguments are supported: -* `plugin_reference` - (Required, string, Forces new resource) The plugin reference. The registry path and image tag should not be omitted. See [plugin_references, alias](#plugin-references-alias-1) below for details. +* `name` - (Required, string, Forces new resource) The plugin name. If the tag is omitted, `:latest` is complemented to the attribute value. * `alias` - (Optional, string, Forces new resource) The alias of the Docker plugin. If the tag is omitted, `:latest` is complemented to the attribute value. * `enabled` - (Optional, boolean) If true, the plugin is enabled. The default value is `true`. * `grant_all_permissions` - (Optional, boolean) If true, grant all permissions necessary to run the plugin. This attribute conflicts with `grant_permissions`. @@ -47,47 +47,6 @@ The following arguments are supported: * `enable_timeout` - (Optional, int) HTTP client timeout to enable the plugin. * `force_disable` - (Optional, boolean) If true, then the plugin is disabled forcibly when the plugin is disabled. - -## plugin_reference - -`plugin_reference` must be full path. Otherwise, after `terraform apply` is run, there would be diffs of them. - -For example, - -```hcl -resource "docker_plugin" "sample-volume-plugin" { - plugin_reference = "tiborvass/sample-volume-plugin" # must be "docker.io/tiborvass/sample-volume-plugin:latest" - alias = "sample" -} -``` - -```sh -$ terraform apply # a plugin is installed - -Apply complete! Resources: 1 added, 0 changed, 0 destroyed. - -$ terraform plan - -An execution plan has been generated and is shown below. -Resource actions are indicated with the following symbols: --/+ destroy and then create replacement - -Terraform will perform the following actions: - - # docker_plugin.sample-volume-plugin must be replaced --/+ resource "docker_plugin" "sample-volume-plugin" { - ~ alias = "sample:latest" -> "sample:latest" - - enabled = false -> null - ~ env = [ - - "DEBUG=0", - ] -> (known after apply) - ~ id = "27784976e1471c1e473a901a0a02055ddc8bc1c9dec9c44d81a49d516c0c28f9" -> (known after apply) - ~ plugin_reference = "docker.io/tiborvass/sample-volume-plugin:latest" -> "tiborvass/sample-volume-plugin" # forces replacement - } - -Plan: 1 to add, 0 to change, 1 to destroy. -``` - ## grant_permissions @@ -101,7 +60,7 @@ Example: ```hcl resource "docker_plugin" "sshfs" { - plugin_reference = "docker.io/vieux/sshfs:latest" + name = "docker.io/vieux/sshfs:latest" grant_permissions { name = "network" value = [ @@ -132,6 +91,8 @@ resource "docker_plugin" "sshfs" { ## Attributes Reference +* `plugin_reference` - (string) The plugin reference. + ## Import Docker plugins can be imported using the long id, e.g. for a plugin `tiborvass/sample-volume-plugin:latest`: