github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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 }