github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/docker/resource_docker_container.go (about)

     1  package docker
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"regexp"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  )
    12  
    13  func resourceDockerContainer() *schema.Resource {
    14  	return &schema.Resource{
    15  		Create: resourceDockerContainerCreate,
    16  		Read:   resourceDockerContainerRead,
    17  		Update: resourceDockerContainerUpdate,
    18  		Delete: resourceDockerContainerDelete,
    19  
    20  		Schema: map[string]*schema.Schema{
    21  			"name": &schema.Schema{
    22  				Type:     schema.TypeString,
    23  				Required: true,
    24  				ForceNew: true,
    25  			},
    26  
    27  			// Indicates whether the container must be running.
    28  			//
    29  			// An assumption is made that configured containers
    30  			// should be running; if not, they should not be in
    31  			// the configuration. Therefore a stopped container
    32  			// should be started. Set to false to have the
    33  			// provider leave the container alone.
    34  			//
    35  			// Actively-debugged containers are likely to be
    36  			// stopped and started manually, and Docker has
    37  			// some provisions for restarting containers that
    38  			// stop. The utility here comes from the fact that
    39  			// this will delete and re-create the container
    40  			// following the principle that the containers
    41  			// should be pristine when started.
    42  			"must_run": &schema.Schema{
    43  				Type:     schema.TypeBool,
    44  				Default:  true,
    45  				Optional: true,
    46  			},
    47  
    48  			// ForceNew is not true for image because we need to
    49  			// sane this against Docker image IDs, as each image
    50  			// can have multiple names/tags attached do it.
    51  			"image": &schema.Schema{
    52  				Type:     schema.TypeString,
    53  				Required: true,
    54  				ForceNew: true,
    55  			},
    56  
    57  			"hostname": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				Optional: true,
    60  				ForceNew: true,
    61  			},
    62  
    63  			"domainname": &schema.Schema{
    64  				Type:     schema.TypeString,
    65  				Optional: true,
    66  				ForceNew: true,
    67  			},
    68  
    69  			"command": &schema.Schema{
    70  				Type:     schema.TypeList,
    71  				Optional: true,
    72  				ForceNew: true,
    73  				Elem:     &schema.Schema{Type: schema.TypeString},
    74  			},
    75  
    76  			"entrypoint": &schema.Schema{
    77  				Type:     schema.TypeList,
    78  				Optional: true,
    79  				ForceNew: true,
    80  				Elem:     &schema.Schema{Type: schema.TypeString},
    81  			},
    82  
    83  			"user": &schema.Schema{
    84  				Type:     schema.TypeString,
    85  				Optional: true,
    86  				ForceNew: true,
    87  				Elem:     &schema.Schema{Type: schema.TypeString},
    88  			},
    89  
    90  			"dns": &schema.Schema{
    91  				Type:     schema.TypeSet,
    92  				Optional: true,
    93  				ForceNew: true,
    94  				Elem:     &schema.Schema{Type: schema.TypeString},
    95  				Set:      schema.HashString,
    96  			},
    97  
    98  			"dns_opts": &schema.Schema{
    99  				Type:     schema.TypeSet,
   100  				Optional: true,
   101  				ForceNew: true,
   102  				Elem:     &schema.Schema{Type: schema.TypeString},
   103  				Set:      schema.HashString,
   104  			},
   105  
   106  			"dns_search": &schema.Schema{
   107  				Type:     schema.TypeSet,
   108  				Optional: true,
   109  				ForceNew: true,
   110  				Elem:     &schema.Schema{Type: schema.TypeString},
   111  				Set:      schema.HashString,
   112  			},
   113  
   114  			"publish_all_ports": &schema.Schema{
   115  				Type:     schema.TypeBool,
   116  				Optional: true,
   117  				ForceNew: true,
   118  			},
   119  
   120  			"restart": &schema.Schema{
   121  				Type:     schema.TypeString,
   122  				Optional: true,
   123  				ForceNew: true,
   124  				Default:  "no",
   125  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   126  					value := v.(string)
   127  					if !regexp.MustCompile(`^(no|on-failure|always|unless-stopped)$`).MatchString(value) {
   128  						es = append(es, fmt.Errorf(
   129  							"%q must be one of \"no\", \"on-failure\", \"always\" or \"unless-stopped\"", k))
   130  					}
   131  					return
   132  				},
   133  			},
   134  
   135  			"max_retry_count": &schema.Schema{
   136  				Type:     schema.TypeInt,
   137  				Optional: true,
   138  				ForceNew: true,
   139  			},
   140  
   141  			"capabilities": &schema.Schema{
   142  				Type:     schema.TypeSet,
   143  				Optional: true,
   144  				ForceNew: true,
   145  				MaxItems: 1,
   146  				Elem: &schema.Resource{
   147  					Schema: map[string]*schema.Schema{
   148  						"add": &schema.Schema{
   149  							Type:     schema.TypeSet,
   150  							Optional: true,
   151  							ForceNew: true,
   152  							Elem:     &schema.Schema{Type: schema.TypeString},
   153  							Set:      schema.HashString,
   154  						},
   155  
   156  						"drop": &schema.Schema{
   157  							Type:     schema.TypeSet,
   158  							Optional: true,
   159  							ForceNew: true,
   160  							Elem:     &schema.Schema{Type: schema.TypeString},
   161  							Set:      schema.HashString,
   162  						},
   163  					},
   164  				},
   165  				Set: resourceDockerCapabilitiesHash,
   166  			},
   167  
   168  			"volumes": &schema.Schema{
   169  				Type:     schema.TypeSet,
   170  				Optional: true,
   171  				ForceNew: true,
   172  				Elem: &schema.Resource{
   173  					Schema: map[string]*schema.Schema{
   174  						"from_container": &schema.Schema{
   175  							Type:     schema.TypeString,
   176  							Optional: true,
   177  							ForceNew: true,
   178  						},
   179  
   180  						"container_path": &schema.Schema{
   181  							Type:     schema.TypeString,
   182  							Optional: true,
   183  							ForceNew: true,
   184  						},
   185  
   186  						"host_path": &schema.Schema{
   187  							Type:     schema.TypeString,
   188  							Optional: true,
   189  							ForceNew: true,
   190  							ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   191  								value := v.(string)
   192  								if !regexp.MustCompile(`^/`).MatchString(value) {
   193  									es = append(es, fmt.Errorf(
   194  										"%q must be an absolute path", k))
   195  								}
   196  								return
   197  							},
   198  						},
   199  
   200  						"volume_name": &schema.Schema{
   201  							Type:     schema.TypeString,
   202  							Optional: true,
   203  							ForceNew: true,
   204  						},
   205  
   206  						"read_only": &schema.Schema{
   207  							Type:     schema.TypeBool,
   208  							Optional: true,
   209  							ForceNew: true,
   210  						},
   211  					},
   212  				},
   213  				Set: resourceDockerVolumesHash,
   214  			},
   215  
   216  			"ports": &schema.Schema{
   217  				Type:     schema.TypeSet,
   218  				Optional: true,
   219  				ForceNew: true,
   220  				Elem: &schema.Resource{
   221  					Schema: map[string]*schema.Schema{
   222  						"internal": &schema.Schema{
   223  							Type:     schema.TypeInt,
   224  							Required: true,
   225  							ForceNew: true,
   226  						},
   227  
   228  						"external": &schema.Schema{
   229  							Type:     schema.TypeInt,
   230  							Optional: true,
   231  							ForceNew: true,
   232  						},
   233  
   234  						"ip": &schema.Schema{
   235  							Type:     schema.TypeString,
   236  							Optional: true,
   237  							ForceNew: true,
   238  						},
   239  
   240  						"protocol": &schema.Schema{
   241  							Type:     schema.TypeString,
   242  							Default:  "tcp",
   243  							Optional: true,
   244  							ForceNew: true,
   245  						},
   246  					},
   247  				},
   248  				Set: resourceDockerPortsHash,
   249  			},
   250  
   251  			"host": &schema.Schema{
   252  				Type:     schema.TypeSet,
   253  				Optional: true,
   254  				ForceNew: true,
   255  				Elem: &schema.Resource{
   256  					Schema: map[string]*schema.Schema{
   257  						"ip": &schema.Schema{
   258  							Type:     schema.TypeString,
   259  							Required: true,
   260  							ForceNew: true,
   261  						},
   262  
   263  						"host": &schema.Schema{
   264  							Type:     schema.TypeString,
   265  							Required: true,
   266  							ForceNew: true,
   267  						},
   268  					},
   269  				},
   270  				Set: resourceDockerHostsHash,
   271  			},
   272  
   273  			"env": &schema.Schema{
   274  				Type:     schema.TypeSet,
   275  				Optional: true,
   276  				ForceNew: true,
   277  				Elem:     &schema.Schema{Type: schema.TypeString},
   278  				Set:      schema.HashString,
   279  			},
   280  
   281  			"links": &schema.Schema{
   282  				Type:     schema.TypeSet,
   283  				Optional: true,
   284  				ForceNew: true,
   285  				Elem:     &schema.Schema{Type: schema.TypeString},
   286  				Set:      schema.HashString,
   287  			},
   288  
   289  			"ip_address": &schema.Schema{
   290  				Type:     schema.TypeString,
   291  				Computed: true,
   292  			},
   293  
   294  			"ip_prefix_length": &schema.Schema{
   295  				Type:     schema.TypeInt,
   296  				Computed: true,
   297  			},
   298  
   299  			"gateway": &schema.Schema{
   300  				Type:     schema.TypeString,
   301  				Computed: true,
   302  			},
   303  
   304  			"bridge": &schema.Schema{
   305  				Type:     schema.TypeString,
   306  				Computed: true,
   307  			},
   308  
   309  			"privileged": &schema.Schema{
   310  				Type:     schema.TypeBool,
   311  				Optional: true,
   312  				ForceNew: true,
   313  			},
   314  
   315  			"destroy_grace_seconds": &schema.Schema{
   316  				Type:     schema.TypeInt,
   317  				Optional: true,
   318  			},
   319  
   320  			"labels": &schema.Schema{
   321  				Type:     schema.TypeMap,
   322  				Optional: true,
   323  				ForceNew: true,
   324  			},
   325  
   326  			"memory": &schema.Schema{
   327  				Type:     schema.TypeInt,
   328  				Optional: true,
   329  				ForceNew: true,
   330  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   331  					value := v.(int)
   332  					if value < 0 {
   333  						es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k))
   334  					}
   335  					return
   336  				},
   337  			},
   338  
   339  			"memory_swap": &schema.Schema{
   340  				Type:     schema.TypeInt,
   341  				Optional: true,
   342  				ForceNew: true,
   343  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   344  					value := v.(int)
   345  					if value < -1 {
   346  						es = append(es, fmt.Errorf("%q must be greater than or equal to -1", k))
   347  					}
   348  					return
   349  				},
   350  			},
   351  
   352  			"cpu_shares": &schema.Schema{
   353  				Type:     schema.TypeInt,
   354  				Optional: true,
   355  				ForceNew: true,
   356  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   357  					value := v.(int)
   358  					if value < 0 {
   359  						es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k))
   360  					}
   361  					return
   362  				},
   363  			},
   364  
   365  			"log_driver": &schema.Schema{
   366  				Type:     schema.TypeString,
   367  				Optional: true,
   368  				ForceNew: true,
   369  				Default:  "json-file",
   370  				ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
   371  					value := v.(string)
   372  					if !regexp.MustCompile(`^(json-file|syslog|journald|gelf|fluentd)$`).MatchString(value) {
   373  						es = append(es, fmt.Errorf(
   374  							"%q must be one of \"json-file\", \"syslog\", \"journald\", \"gelf\", or \"fluentd\"", k))
   375  					}
   376  					return
   377  				},
   378  			},
   379  
   380  			"log_opts": &schema.Schema{
   381  				Type:     schema.TypeMap,
   382  				Optional: true,
   383  				ForceNew: true,
   384  			},
   385  
   386  			"network_mode": &schema.Schema{
   387  				Type:     schema.TypeString,
   388  				Optional: true,
   389  				ForceNew: true,
   390  			},
   391  
   392  			"networks": &schema.Schema{
   393  				Type:     schema.TypeSet,
   394  				Optional: true,
   395  				ForceNew: true,
   396  				Elem:     &schema.Schema{Type: schema.TypeString},
   397  				Set:      schema.HashString,
   398  			},
   399  
   400  			"upload": &schema.Schema{
   401  				Type:     schema.TypeSet,
   402  				Optional: true,
   403  				ForceNew: true,
   404  				Elem: &schema.Resource{
   405  					Schema: map[string]*schema.Schema{
   406  						"content": &schema.Schema{
   407  							Type:     schema.TypeString,
   408  							Required: true,
   409  							// This is intentional. The container is mutated once, and never updated later.
   410  							// New configuration forces a new deployment, even with the same binaries.
   411  							ForceNew: true,
   412  						},
   413  						"file": &schema.Schema{
   414  							Type:     schema.TypeString,
   415  							Required: true,
   416  							ForceNew: true,
   417  						},
   418  					},
   419  				},
   420  				Set: resourceDockerUploadHash,
   421  			},
   422  		},
   423  	}
   424  }
   425  
   426  func resourceDockerCapabilitiesHash(v interface{}) int {
   427  	var buf bytes.Buffer
   428  	m := v.(map[string]interface{})
   429  
   430  	if v, ok := m["add"]; ok {
   431  		buf.WriteString(fmt.Sprintf("%v-", v))
   432  	}
   433  
   434  	if v, ok := m["remove"]; ok {
   435  		buf.WriteString(fmt.Sprintf("%v-", v))
   436  	}
   437  
   438  	return hashcode.String(buf.String())
   439  }
   440  
   441  func resourceDockerPortsHash(v interface{}) int {
   442  	var buf bytes.Buffer
   443  	m := v.(map[string]interface{})
   444  
   445  	buf.WriteString(fmt.Sprintf("%v-", m["internal"].(int)))
   446  
   447  	if v, ok := m["external"]; ok {
   448  		buf.WriteString(fmt.Sprintf("%v-", v.(int)))
   449  	}
   450  
   451  	if v, ok := m["ip"]; ok {
   452  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   453  	}
   454  
   455  	if v, ok := m["protocol"]; ok {
   456  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   457  	}
   458  
   459  	return hashcode.String(buf.String())
   460  }
   461  
   462  func resourceDockerHostsHash(v interface{}) int {
   463  	var buf bytes.Buffer
   464  	m := v.(map[string]interface{})
   465  
   466  	if v, ok := m["ip"]; ok {
   467  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   468  	}
   469  
   470  	if v, ok := m["host"]; ok {
   471  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   472  	}
   473  
   474  	return hashcode.String(buf.String())
   475  }
   476  
   477  func resourceDockerVolumesHash(v interface{}) int {
   478  	var buf bytes.Buffer
   479  	m := v.(map[string]interface{})
   480  
   481  	if v, ok := m["from_container"]; ok {
   482  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   483  	}
   484  
   485  	if v, ok := m["container_path"]; ok {
   486  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   487  	}
   488  
   489  	if v, ok := m["host_path"]; ok {
   490  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   491  	}
   492  
   493  	if v, ok := m["volume_name"]; ok {
   494  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   495  	}
   496  
   497  	if v, ok := m["read_only"]; ok {
   498  		buf.WriteString(fmt.Sprintf("%v-", v.(bool)))
   499  	}
   500  
   501  	return hashcode.String(buf.String())
   502  }
   503  
   504  func resourceDockerUploadHash(v interface{}) int {
   505  	var buf bytes.Buffer
   506  	m := v.(map[string]interface{})
   507  
   508  	if v, ok := m["content"]; ok {
   509  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   510  	}
   511  
   512  	if v, ok := m["file"]; ok {
   513  		buf.WriteString(fmt.Sprintf("%v-", v.(string)))
   514  	}
   515  
   516  	return hashcode.String(buf.String())
   517  }