github.com/skyscape-cloud-services/terraform@v0.9.2-0.20170609144644-7ece028a1747/builtin/providers/kubernetes/schema_container.go (about)

     1  package kubernetes
     2  
     3  import "github.com/hashicorp/terraform/helper/schema"
     4  
     5  func handlerFields() map[string]*schema.Schema {
     6  	return map[string]*schema.Schema{
     7  		"exec": {
     8  			Type:        schema.TypeList,
     9  			Optional:    true,
    10  			MaxItems:    1,
    11  			Description: "exec specifies the action to take.",
    12  			Elem: &schema.Resource{
    13  				Schema: map[string]*schema.Schema{
    14  					"command": {
    15  						Type:        schema.TypeList,
    16  						Description: `Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.`,
    17  						Optional:    true,
    18  						Elem:        &schema.Schema{Type: schema.TypeString},
    19  					},
    20  				},
    21  			},
    22  		},
    23  		"http_get": {
    24  			Type:        schema.TypeList,
    25  			Optional:    true,
    26  			MaxItems:    1,
    27  			Description: "Specifies the http request to perform.",
    28  			Elem: &schema.Resource{
    29  				Schema: map[string]*schema.Schema{
    30  					"host": {
    31  						Type:        schema.TypeString,
    32  						Optional:    true,
    33  						Description: `Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.`,
    34  					},
    35  					"path": {
    36  						Type:        schema.TypeString,
    37  						Optional:    true,
    38  						Description: `Path to access on the HTTP server.`,
    39  					},
    40  					"scheme": {
    41  						Type:        schema.TypeString,
    42  						Optional:    true,
    43  						Default:     "HTTP",
    44  						Description: `Scheme to use for connecting to the host.`,
    45  					},
    46  					"port": {
    47  						Type:         schema.TypeString,
    48  						Optional:     true,
    49  						ValidateFunc: validatePortNumOrName,
    50  						Description:  `Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.`,
    51  					},
    52  					"http_header": {
    53  						Type:        schema.TypeList,
    54  						Optional:    true,
    55  						Description: `Scheme to use for connecting to the host.`,
    56  						Elem: &schema.Resource{
    57  							Schema: map[string]*schema.Schema{
    58  								"name": {
    59  									Type:        schema.TypeString,
    60  									Optional:    true,
    61  									Description: "The header field name",
    62  								},
    63  								"value": {
    64  									Type:        schema.TypeString,
    65  									Optional:    true,
    66  									Description: "The header field value",
    67  								},
    68  							},
    69  						},
    70  					},
    71  				},
    72  			},
    73  		},
    74  		"tcp_socket": {
    75  			Type:        schema.TypeList,
    76  			Optional:    true,
    77  			Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported",
    78  			Elem: &schema.Resource{
    79  				Schema: map[string]*schema.Schema{
    80  					"port": {
    81  						Type:         schema.TypeString,
    82  						Required:     true,
    83  						ValidateFunc: validatePortNumOrName,
    84  						Description:  "Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.",
    85  					},
    86  				},
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func resourcesField() map[string]*schema.Schema {
    93  	return map[string]*schema.Schema{
    94  		"limits": {
    95  			Type:        schema.TypeList,
    96  			Optional:    true,
    97  			Computed:    true,
    98  			MaxItems:    1,
    99  			Description: "Describes the maximum amount of compute resources allowed. More info: http://kubernetes.io/docs/user-guide/compute-resources/",
   100  			Elem: &schema.Resource{
   101  				Schema: map[string]*schema.Schema{
   102  					"cpu": {
   103  						Type:             schema.TypeString,
   104  						Optional:         true,
   105  						Computed:         true,
   106  						ValidateFunc:     validateResourceQuantity,
   107  						DiffSuppressFunc: suppressEquivalentResourceQuantity,
   108  					},
   109  					"memory": {
   110  						Type:             schema.TypeString,
   111  						Optional:         true,
   112  						Computed:         true,
   113  						ValidateFunc:     validateResourceQuantity,
   114  						DiffSuppressFunc: suppressEquivalentResourceQuantity,
   115  					},
   116  				},
   117  			},
   118  		},
   119  		"requests": {
   120  			Type:     schema.TypeList,
   121  			Optional: true,
   122  			Computed: true,
   123  			MaxItems: 1,
   124  			Elem: &schema.Resource{
   125  				Schema: map[string]*schema.Schema{
   126  					"cpu": {
   127  						Type:             schema.TypeString,
   128  						Optional:         true,
   129  						Computed:         true,
   130  						ValidateFunc:     validateResourceQuantity,
   131  						DiffSuppressFunc: suppressEquivalentResourceQuantity,
   132  					},
   133  					"memory": {
   134  						Type:             schema.TypeString,
   135  						Optional:         true,
   136  						Computed:         true,
   137  						ValidateFunc:     validateResourceQuantity,
   138  						DiffSuppressFunc: suppressEquivalentResourceQuantity,
   139  					},
   140  				},
   141  			},
   142  		},
   143  	}
   144  }
   145  
   146  func seLinuxOptionsField() map[string]*schema.Schema {
   147  	return map[string]*schema.Schema{
   148  		"level": {
   149  			Type:        schema.TypeString,
   150  			Optional:    true,
   151  			Description: "Level is SELinux level label that applies to the container.",
   152  		},
   153  		"role": {
   154  			Type:        schema.TypeString,
   155  			Optional:    true,
   156  			Description: "Role is a SELinux role label that applies to the container.",
   157  		},
   158  		"type": {
   159  			Type:        schema.TypeString,
   160  			Optional:    true,
   161  			Description: "Type is a SELinux type label that applies to the container.",
   162  		},
   163  		"user": {
   164  			Type:        schema.TypeString,
   165  			Optional:    true,
   166  			Description: "User is a SELinux user label that applies to the container.",
   167  		},
   168  	}
   169  }
   170  
   171  func volumeMountFields() map[string]*schema.Schema {
   172  	return map[string]*schema.Schema{
   173  		"mount_path": {
   174  			Type:        schema.TypeString,
   175  			Required:    true,
   176  			Description: "Path within the container at which the volume should be mounted. Must not contain ':'.",
   177  		},
   178  		"name": {
   179  			Type:        schema.TypeString,
   180  			Required:    true,
   181  			Description: "This must match the Name of a Volume.",
   182  		},
   183  		"read_only": {
   184  			Type:        schema.TypeBool,
   185  			Optional:    true,
   186  			Default:     false,
   187  			Description: "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.",
   188  		},
   189  		"sub_path": {
   190  			Type:        schema.TypeString,
   191  			Optional:    true,
   192  			Description: `Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root).`,
   193  		},
   194  	}
   195  }
   196  
   197  func containerFields() map[string]*schema.Schema {
   198  	return map[string]*schema.Schema{
   199  		"args": {
   200  			Type:        schema.TypeList,
   201  			Optional:    true,
   202  			Elem:        &schema.Schema{Type: schema.TypeString},
   203  			Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/containers#containers-and-commands",
   204  		},
   205  		"command": {
   206  			Type:        schema.TypeList,
   207  			Optional:    true,
   208  			Elem:        &schema.Schema{Type: schema.TypeString},
   209  			Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/containers#containers-and-commands",
   210  		},
   211  		"env": {
   212  			Type:        schema.TypeList,
   213  			Optional:    true,
   214  			ForceNew:    true,
   215  			Description: "List of environment variables to set in the container. Cannot be updated.",
   216  			Elem: &schema.Resource{
   217  				Schema: map[string]*schema.Schema{
   218  					"name": {
   219  						Type:        schema.TypeString,
   220  						Required:    true,
   221  						Description: "Name of the environment variable. Must be a C_IDENTIFIER",
   222  					},
   223  					"value": {
   224  						Type:        schema.TypeString,
   225  						Optional:    true,
   226  						Description: `Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".`,
   227  					},
   228  					"value_from": {
   229  						Type:        schema.TypeList,
   230  						Optional:    true,
   231  						MaxItems:    1,
   232  						Description: "Source for the environment variable's value",
   233  						Elem: &schema.Resource{
   234  							Schema: map[string]*schema.Schema{
   235  								"config_map_key_ref": {
   236  									Type:        schema.TypeList,
   237  									Optional:    true,
   238  									MaxItems:    1,
   239  									Description: "Selects a key of a ConfigMap.",
   240  									Elem: &schema.Resource{
   241  										Schema: map[string]*schema.Schema{
   242  											"key": {
   243  												Type:        schema.TypeString,
   244  												Optional:    true,
   245  												Description: "The key to select.",
   246  											},
   247  											"name": {
   248  												Type:        schema.TypeString,
   249  												Optional:    true,
   250  												Description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
   251  											},
   252  										},
   253  									},
   254  								},
   255  								"field_ref": {
   256  									Type:        schema.TypeList,
   257  									Optional:    true,
   258  									MaxItems:    1,
   259  									Description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.podIP..",
   260  									Elem: &schema.Resource{
   261  										Schema: map[string]*schema.Schema{
   262  											"api_version": {
   263  												Type:        schema.TypeString,
   264  												Optional:    true,
   265  												Default:     "v1",
   266  												Description: `Version of the schema the FieldPath is written in terms of, defaults to "v1".`,
   267  											},
   268  											"field_path": {
   269  												Type:        schema.TypeString,
   270  												Optional:    true,
   271  												Description: "Path of the field to select in the specified API version",
   272  											},
   273  										},
   274  									},
   275  								},
   276  								"resource_field_ref": {
   277  									Type:        schema.TypeList,
   278  									Optional:    true,
   279  									MaxItems:    1,
   280  									Description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.podIP..",
   281  									Elem: &schema.Resource{
   282  										Schema: map[string]*schema.Schema{
   283  											"container_name": {
   284  												Type:     schema.TypeString,
   285  												Optional: true,
   286  											},
   287  											"resource": {
   288  												Type:        schema.TypeString,
   289  												Required:    true,
   290  												Description: "Resource to select",
   291  											},
   292  										},
   293  									},
   294  								},
   295  								"secret_key_ref": {
   296  									Type:        schema.TypeList,
   297  									Optional:    true,
   298  									MaxItems:    1,
   299  									Description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.podIP..",
   300  									Elem: &schema.Resource{
   301  										Schema: map[string]*schema.Schema{
   302  											"key": {
   303  												Type:        schema.TypeString,
   304  												Optional:    true,
   305  												Description: "The key of the secret to select from. Must be a valid secret key.",
   306  											},
   307  											"name": {
   308  												Type:        schema.TypeString,
   309  												Optional:    true,
   310  												Description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
   311  											},
   312  										},
   313  									},
   314  								},
   315  							},
   316  						},
   317  					},
   318  				},
   319  			},
   320  		},
   321  		"image": {
   322  			Type:        schema.TypeString,
   323  			Optional:    true,
   324  			Description: "Docker image name. More info: http://kubernetes.io/docs/user-guide/images",
   325  		},
   326  		"image_pull_policy": {
   327  			Type:        schema.TypeString,
   328  			Optional:    true,
   329  			Computed:    true,
   330  			Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/images#updating-images",
   331  		},
   332  		"lifecycle": {
   333  			Type:        schema.TypeList,
   334  			Optional:    true,
   335  			MaxItems:    1,
   336  			ForceNew:    true,
   337  			Description: "Actions that the management system should take in response to container lifecycle events",
   338  			Elem: &schema.Resource{
   339  				Schema: map[string]*schema.Schema{
   340  					"post_start": {
   341  						Type:        schema.TypeList,
   342  						Description: `post_start is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: http://kubernetes.io/docs/user-guide/container-environment#hook-details`,
   343  						Optional:    true,
   344  						Elem: &schema.Resource{
   345  							Schema: handlerFields(),
   346  						},
   347  					},
   348  					"pre_stop": {
   349  						Type:        schema.TypeList,
   350  						Description: `pre_stop is called immediately before a container is terminated. The container is terminated after the handler completes. The reason for termination is passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated. Other management of the container blocks until the hook completes. More info: http://kubernetes.io/docs/user-guide/container-environment#hook-details`,
   351  						Optional:    true,
   352  						Elem: &schema.Resource{
   353  							Schema: handlerFields(),
   354  						},
   355  					},
   356  				},
   357  			},
   358  		},
   359  		"liveness_probe": {
   360  			Type:        schema.TypeList,
   361  			Optional:    true,
   362  			MaxItems:    1,
   363  			ForceNew:    true,
   364  			Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/pod-states#container-probes",
   365  			Elem:        probeSchema(),
   366  		},
   367  		"name": {
   368  			Type:        schema.TypeString,
   369  			Required:    true,
   370  			Description: "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.",
   371  		},
   372  		"port": {
   373  			Type:        schema.TypeList,
   374  			Optional:    true,
   375  			Description: `List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.0" address inside a container will be accessible from the network. Cannot be updated.`,
   376  			Elem: &schema.Resource{
   377  				Schema: map[string]*schema.Schema{
   378  					"container_port": {
   379  						Type:         schema.TypeInt,
   380  						Required:     true,
   381  						ValidateFunc: validatePortNumOrName,
   382  						Description:  "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
   383  					},
   384  					"host_ip": {
   385  						Type:        schema.TypeString,
   386  						Optional:    true,
   387  						Description: "What host IP to bind the external port to.",
   388  					},
   389  					"host_port": {
   390  						Type:        schema.TypeInt,
   391  						Optional:    true,
   392  						Description: "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
   393  					},
   394  					"name": {
   395  						Type:         schema.TypeString,
   396  						Optional:     true,
   397  						ValidateFunc: validatePortNumOrName,
   398  						Description:  "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services",
   399  					},
   400  					"protocol": {
   401  						Type:        schema.TypeString,
   402  						Optional:    true,
   403  						Description: `Protocol for port. Must be UDP or TCP. Defaults to "TCP".`,
   404  						Default:     "TCP",
   405  					},
   406  				},
   407  			},
   408  		},
   409  		"readiness_probe": {
   410  			Type:        schema.TypeList,
   411  			Optional:    true,
   412  			MaxItems:    1,
   413  			ForceNew:    true,
   414  			Description: "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/pod-states#container-probes",
   415  			Elem:        probeSchema(),
   416  		},
   417  		"resources": {
   418  			Type:        schema.TypeList,
   419  			Optional:    true,
   420  			MaxItems:    1,
   421  			Computed:    true,
   422  			Description: "Compute Resources required by this container. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#resources",
   423  			Elem: &schema.Resource{
   424  				Schema: resourcesField(),
   425  			},
   426  		},
   427  
   428  		"security_context": {
   429  			Type:        schema.TypeList,
   430  			Optional:    true,
   431  			MaxItems:    1,
   432  			ForceNew:    true,
   433  			Description: "Security options the pod should run with. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md",
   434  			Elem:        securityContextSchema(),
   435  		},
   436  		"stdin": {
   437  			Type:        schema.TypeBool,
   438  			Optional:    true,
   439  			Default:     false,
   440  			Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. ",
   441  		},
   442  		"stdin_once": {
   443  			Type:        schema.TypeBool,
   444  			Optional:    true,
   445  			Default:     false,
   446  			Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF.",
   447  		},
   448  		"termination_message_path": {
   449  			Type:        schema.TypeString,
   450  			Optional:    true,
   451  			ForceNew:    true,
   452  			Default:     "/dev/termination-log",
   453  			Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Defaults to /dev/termination-log. Cannot be updated.",
   454  		},
   455  		"tty": {
   456  			Type:        schema.TypeBool,
   457  			Optional:    true,
   458  			Default:     false,
   459  			Description: "Whether this container should allocate a TTY for itself",
   460  		},
   461  		"volume_mount": {
   462  			Type:        schema.TypeList,
   463  			Optional:    true,
   464  			Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.",
   465  			Elem: &schema.Resource{
   466  				Schema: volumeMountFields(),
   467  			},
   468  		},
   469  		"working_dir": {
   470  			Type:        schema.TypeString,
   471  			Optional:    true,
   472  			ForceNew:    true,
   473  			Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.",
   474  		},
   475  	}
   476  }
   477  
   478  func probeSchema() *schema.Resource {
   479  	h := handlerFields()
   480  	h["failure_threshold"] = &schema.Schema{
   481  		Type:         schema.TypeInt,
   482  		Optional:     true,
   483  		Description:  "Minimum consecutive failures for the probe to be considered failed after having succeeded.",
   484  		Default:      3,
   485  		ValidateFunc: validatePositiveInteger,
   486  	}
   487  	h["initial_delay_seconds"] = &schema.Schema{
   488  		Type:        schema.TypeInt,
   489  		Optional:    true,
   490  		Description: "Number of seconds after the container has started before liveness probes are initiated. More info: http://kubernetes.io/docs/user-guide/pod-states#container-probes",
   491  	}
   492  	h["period_seconds"] = &schema.Schema{
   493  		Type:         schema.TypeInt,
   494  		Optional:     true,
   495  		Default:      10,
   496  		ValidateFunc: validatePositiveInteger,
   497  		Description:  "How often (in seconds) to perform the probe",
   498  	}
   499  	h["success_threshold"] = &schema.Schema{
   500  		Type:         schema.TypeInt,
   501  		Optional:     true,
   502  		Default:      1,
   503  		ValidateFunc: validatePositiveInteger,
   504  		Description:  "Minimum consecutive successes for the probe to be considered successful after having failed.",
   505  	}
   506  
   507  	h["timeout_seconds"] = &schema.Schema{
   508  		Type:         schema.TypeInt,
   509  		Optional:     true,
   510  		Default:      1,
   511  		ValidateFunc: validatePositiveInteger,
   512  		Description:  "Number of seconds after which the probe times out. More info: http://kubernetes.io/docs/user-guide/pod-states#container-probes",
   513  	}
   514  	return &schema.Resource{
   515  		Schema: h,
   516  	}
   517  
   518  }
   519  
   520  func securityContextSchema() *schema.Resource {
   521  	m := map[string]*schema.Schema{
   522  		"privileged": {
   523  			Type:        schema.TypeBool,
   524  			Optional:    true,
   525  			Default:     false,
   526  			Description: `Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host.`,
   527  		},
   528  		"read_only_root_filesystem": {
   529  			Type:        schema.TypeBool,
   530  			Optional:    true,
   531  			Default:     false,
   532  			Description: "Whether this container has a read-only root filesystem.",
   533  		},
   534  		"run_as_non_root": {
   535  			Type:        schema.TypeBool,
   536  			Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does.",
   537  			Optional:    true,
   538  		},
   539  		"run_as_user": {
   540  			Type:        schema.TypeInt,
   541  			Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified",
   542  			Optional:    true,
   543  		},
   544  		"se_linux_options": {
   545  			Type:        schema.TypeList,
   546  			Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod",
   547  			Optional:    true,
   548  			MaxItems:    1,
   549  			Elem: &schema.Resource{
   550  				Schema: seLinuxOptionsField(),
   551  			},
   552  		},
   553  		"capabilities": {
   554  			Type:        schema.TypeList,
   555  			Optional:    true,
   556  			MaxItems:    1,
   557  			Description: "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.",
   558  			Elem: &schema.Resource{
   559  				Schema: map[string]*schema.Schema{
   560  					"add": {
   561  						Type:        schema.TypeList,
   562  						Optional:    true,
   563  						Elem:        &schema.Schema{Type: schema.TypeString},
   564  						Description: "Added capabilities",
   565  					},
   566  					"drop": {
   567  						Type:        schema.TypeList,
   568  						Optional:    true,
   569  						Elem:        &schema.Schema{Type: schema.TypeString},
   570  						Description: "Removed capabilities",
   571  					},
   572  				},
   573  			},
   574  		},
   575  	}
   576  
   577  	return &schema.Resource{
   578  		Schema: m,
   579  	}
   580  }