github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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: validateDockerContainerPath, 191 }, 192 193 "volume_name": &schema.Schema{ 194 Type: schema.TypeString, 195 Optional: true, 196 ForceNew: true, 197 }, 198 199 "read_only": &schema.Schema{ 200 Type: schema.TypeBool, 201 Optional: true, 202 ForceNew: true, 203 }, 204 }, 205 }, 206 Set: resourceDockerVolumesHash, 207 }, 208 209 "ports": &schema.Schema{ 210 Type: schema.TypeSet, 211 Optional: true, 212 ForceNew: true, 213 Elem: &schema.Resource{ 214 Schema: map[string]*schema.Schema{ 215 "internal": &schema.Schema{ 216 Type: schema.TypeInt, 217 Required: true, 218 ForceNew: true, 219 }, 220 221 "external": &schema.Schema{ 222 Type: schema.TypeInt, 223 Optional: true, 224 ForceNew: true, 225 }, 226 227 "ip": &schema.Schema{ 228 Type: schema.TypeString, 229 Optional: true, 230 ForceNew: true, 231 }, 232 233 "protocol": &schema.Schema{ 234 Type: schema.TypeString, 235 Default: "tcp", 236 Optional: true, 237 ForceNew: true, 238 }, 239 }, 240 }, 241 Set: resourceDockerPortsHash, 242 }, 243 244 "host": &schema.Schema{ 245 Type: schema.TypeSet, 246 Optional: true, 247 ForceNew: true, 248 Elem: &schema.Resource{ 249 Schema: map[string]*schema.Schema{ 250 "ip": &schema.Schema{ 251 Type: schema.TypeString, 252 Required: true, 253 ForceNew: true, 254 }, 255 256 "host": &schema.Schema{ 257 Type: schema.TypeString, 258 Required: true, 259 ForceNew: true, 260 }, 261 }, 262 }, 263 Set: resourceDockerHostsHash, 264 }, 265 266 "env": &schema.Schema{ 267 Type: schema.TypeSet, 268 Optional: true, 269 ForceNew: true, 270 Elem: &schema.Schema{Type: schema.TypeString}, 271 Set: schema.HashString, 272 }, 273 274 "links": &schema.Schema{ 275 Type: schema.TypeSet, 276 Optional: true, 277 ForceNew: true, 278 Elem: &schema.Schema{Type: schema.TypeString}, 279 Set: schema.HashString, 280 }, 281 282 "ip_address": &schema.Schema{ 283 Type: schema.TypeString, 284 Computed: true, 285 }, 286 287 "ip_prefix_length": &schema.Schema{ 288 Type: schema.TypeInt, 289 Computed: true, 290 }, 291 292 "gateway": &schema.Schema{ 293 Type: schema.TypeString, 294 Computed: true, 295 }, 296 297 "bridge": &schema.Schema{ 298 Type: schema.TypeString, 299 Computed: true, 300 }, 301 302 "privileged": &schema.Schema{ 303 Type: schema.TypeBool, 304 Optional: true, 305 ForceNew: true, 306 }, 307 308 "destroy_grace_seconds": &schema.Schema{ 309 Type: schema.TypeInt, 310 Optional: true, 311 }, 312 313 "labels": &schema.Schema{ 314 Type: schema.TypeMap, 315 Optional: true, 316 ForceNew: true, 317 }, 318 319 "memory": &schema.Schema{ 320 Type: schema.TypeInt, 321 Optional: true, 322 ForceNew: true, 323 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 324 value := v.(int) 325 if value < 0 { 326 es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k)) 327 } 328 return 329 }, 330 }, 331 332 "memory_swap": &schema.Schema{ 333 Type: schema.TypeInt, 334 Optional: true, 335 ForceNew: true, 336 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 337 value := v.(int) 338 if value < -1 { 339 es = append(es, fmt.Errorf("%q must be greater than or equal to -1", k)) 340 } 341 return 342 }, 343 }, 344 345 "cpu_shares": &schema.Schema{ 346 Type: schema.TypeInt, 347 Optional: true, 348 ForceNew: true, 349 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 350 value := v.(int) 351 if value < 0 { 352 es = append(es, fmt.Errorf("%q must be greater than or equal to 0", k)) 353 } 354 return 355 }, 356 }, 357 358 "log_driver": &schema.Schema{ 359 Type: schema.TypeString, 360 Optional: true, 361 ForceNew: true, 362 Default: "json-file", 363 ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { 364 value := v.(string) 365 if !regexp.MustCompile(`^(json-file|syslog|journald|gelf|fluentd)$`).MatchString(value) { 366 es = append(es, fmt.Errorf( 367 "%q must be one of \"json-file\", \"syslog\", \"journald\", \"gelf\", or \"fluentd\"", k)) 368 } 369 return 370 }, 371 }, 372 373 "log_opts": &schema.Schema{ 374 Type: schema.TypeMap, 375 Optional: true, 376 ForceNew: true, 377 }, 378 379 "network_alias": &schema.Schema{ 380 Type: schema.TypeSet, 381 Optional: true, 382 ForceNew: true, 383 Elem: &schema.Schema{Type: schema.TypeString}, 384 Set: schema.HashString, 385 }, 386 387 "network_mode": &schema.Schema{ 388 Type: schema.TypeString, 389 Optional: true, 390 ForceNew: true, 391 }, 392 393 "networks": &schema.Schema{ 394 Type: schema.TypeSet, 395 Optional: true, 396 ForceNew: true, 397 Elem: &schema.Schema{Type: schema.TypeString}, 398 Set: schema.HashString, 399 }, 400 401 "upload": &schema.Schema{ 402 Type: schema.TypeSet, 403 Optional: true, 404 ForceNew: true, 405 Elem: &schema.Resource{ 406 Schema: map[string]*schema.Schema{ 407 "content": &schema.Schema{ 408 Type: schema.TypeString, 409 Required: true, 410 // This is intentional. The container is mutated once, and never updated later. 411 // New configuration forces a new deployment, even with the same binaries. 412 ForceNew: true, 413 }, 414 "file": &schema.Schema{ 415 Type: schema.TypeString, 416 Required: true, 417 ForceNew: true, 418 }, 419 }, 420 }, 421 Set: resourceDockerUploadHash, 422 }, 423 }, 424 } 425 } 426 427 func resourceDockerCapabilitiesHash(v interface{}) int { 428 var buf bytes.Buffer 429 m := v.(map[string]interface{}) 430 431 if v, ok := m["add"]; ok { 432 buf.WriteString(fmt.Sprintf("%v-", v)) 433 } 434 435 if v, ok := m["remove"]; ok { 436 buf.WriteString(fmt.Sprintf("%v-", v)) 437 } 438 439 return hashcode.String(buf.String()) 440 } 441 442 func resourceDockerPortsHash(v interface{}) int { 443 var buf bytes.Buffer 444 m := v.(map[string]interface{}) 445 446 buf.WriteString(fmt.Sprintf("%v-", m["internal"].(int))) 447 448 if v, ok := m["external"]; ok { 449 buf.WriteString(fmt.Sprintf("%v-", v.(int))) 450 } 451 452 if v, ok := m["ip"]; ok { 453 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 454 } 455 456 if v, ok := m["protocol"]; ok { 457 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 458 } 459 460 return hashcode.String(buf.String()) 461 } 462 463 func resourceDockerHostsHash(v interface{}) int { 464 var buf bytes.Buffer 465 m := v.(map[string]interface{}) 466 467 if v, ok := m["ip"]; ok { 468 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 469 } 470 471 if v, ok := m["host"]; ok { 472 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 473 } 474 475 return hashcode.String(buf.String()) 476 } 477 478 func resourceDockerVolumesHash(v interface{}) int { 479 var buf bytes.Buffer 480 m := v.(map[string]interface{}) 481 482 if v, ok := m["from_container"]; ok { 483 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 484 } 485 486 if v, ok := m["container_path"]; ok { 487 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 488 } 489 490 if v, ok := m["host_path"]; ok { 491 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 492 } 493 494 if v, ok := m["volume_name"]; ok { 495 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 496 } 497 498 if v, ok := m["read_only"]; ok { 499 buf.WriteString(fmt.Sprintf("%v-", v.(bool))) 500 } 501 502 return hashcode.String(buf.String()) 503 } 504 505 func resourceDockerUploadHash(v interface{}) int { 506 var buf bytes.Buffer 507 m := v.(map[string]interface{}) 508 509 if v, ok := m["content"]; ok { 510 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 511 } 512 513 if v, ok := m["file"]; ok { 514 buf.WriteString(fmt.Sprintf("%v-", v.(string))) 515 } 516 517 return hashcode.String(buf.String()) 518 } 519 520 func validateDockerContainerPath(v interface{}, k string) (ws []string, errors []error) { 521 522 value := v.(string) 523 if !regexp.MustCompile(`^[a-zA-Z]:\\|^/`).MatchString(value) { 524 errors = append(errors, fmt.Errorf("%q must be an absolute path", k)) 525 } 526 527 return 528 }