From f7512ca29fcf2b5ef690214cd9859c738a4c86ba Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Tue, 17 Feb 2015 16:28:33 +0000 Subject: [PATCH 001/167] Initial commit. This adds the initial bits of a Docker provider. Docker's API is huge and only a small subset is currently implemented, but this is expected to grow over time. Currently it's enough to satisfy the use cases of probably 95% of Docker users. I'm preparing this initial pull request as a preview step for feedback. My ideal scenario would be to develop this within a branch in the main repository; the more eyes and testing and pitching in on the code, the better (this would avoid a merge request-to-the-merge-request scenario, as I figure this will be built up over the longer term, even before a merge into master). Unit tests do not exist yet. Right now I've just been focused on getting initial functionality ported over. I've been testing each option extensively via the Docker inspect capabilities. This code (C)2014-2015 Akamai Technologies, Inc. --- CONTRIBUTING.md | 4 +- builtin/bins/provider-docker/main.go | 12 + builtin/bins/provider-docker/main_test.go | 1 + builtin/providers/docker/config.go | 24 ++ builtin/providers/docker/provider.go | 34 +++ .../docker/resource_docker_container.go | 222 ++++++++++++++ .../docker/resource_docker_container_funcs.go | 282 ++++++++++++++++++ .../providers/docker/resource_docker_image.go | 31 ++ .../docker/resource_docker_image_funcs.go | 177 +++++++++++ config.go | 2 +- 10 files changed, 786 insertions(+), 3 deletions(-) create mode 100644 builtin/bins/provider-docker/main.go create mode 100644 builtin/bins/provider-docker/main_test.go create mode 100644 builtin/providers/docker/config.go create mode 100644 builtin/providers/docker/provider.go create mode 100644 builtin/providers/docker/resource_docker_container.go create mode 100644 builtin/providers/docker/resource_docker_container_funcs.go create mode 100644 builtin/providers/docker/resource_docker_image.go create mode 100644 builtin/providers/docker/resource_docker_image_funcs.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 87f5ca66d..f5554557f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,8 +53,8 @@ If you have never worked with Go before, you will have to complete the following steps in order to be able to compile and test Terraform (or use the Vagrantfile in this repo to stand up a dev VM). -1. Install Go. Make sure the Go version is at least Go 1.2. Terraform will not work with anything less than - Go 1.2. On a Mac, you can `brew install go` to install Go 1.2. +1. Install Go. Make sure the Go version is at least Go 1.4. Terraform will not work with anything less than + Go 1.4. On a Mac, you can `brew install go` to install Go 1.4. 2. Set and export the `GOPATH` environment variable and update your `PATH`. For example, you can add to your `.bash_profile`. diff --git a/builtin/bins/provider-docker/main.go b/builtin/bins/provider-docker/main.go new file mode 100644 index 000000000..a54af4c02 --- /dev/null +++ b/builtin/bins/provider-docker/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/docker" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: docker.Provider, + }) +} diff --git a/builtin/bins/provider-docker/main_test.go b/builtin/bins/provider-docker/main_test.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/builtin/bins/provider-docker/main_test.go @@ -0,0 +1 @@ +package main diff --git a/builtin/providers/docker/config.go b/builtin/providers/docker/config.go new file mode 100644 index 000000000..40355b24f --- /dev/null +++ b/builtin/providers/docker/config.go @@ -0,0 +1,24 @@ +package docker + +import dc "github.com/fsouza/go-dockerclient" + +type Config struct { + DockerHost string + SkipPull bool +} + +type Data struct { + DockerImages map[string]*dc.APIImages +} + +// NewClient() returns a new Docker client. +func (c *Config) NewClient() (*dc.Client, error) { + return dc.NewClient(c.DockerHost) +} + +// NewData() returns a new data struct. +func (c *Config) NewData() *Data { + return &Data{ + DockerImages: map[string]*dc.APIImages{}, + } +} diff --git a/builtin/providers/docker/provider.go b/builtin/providers/docker/provider.go new file mode 100644 index 000000000..d01ec385c --- /dev/null +++ b/builtin/providers/docker/provider.go @@ -0,0 +1,34 @@ +package docker + +import ( + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "docker_host": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("DOCKER_HOST", "unix:/run/docker.sock"), + Description: "The Docker daemon endpoint", + }, + }, + + ResourcesMap: map[string]*schema.Resource{ + "docker_container": resourceDockerContainer(), + "docker_image": resourceDockerImage(), + }, + + ConfigureFunc: providerConfigure, + } +} + +func providerConfigure(d *schema.ResourceData) (interface{}, error) { + config := Config{ + DockerHost: d.Get("docker_host").(string), + } + + return &config, nil +} diff --git a/builtin/providers/docker/resource_docker_container.go b/builtin/providers/docker/resource_docker_container.go new file mode 100644 index 000000000..50b501ca2 --- /dev/null +++ b/builtin/providers/docker/resource_docker_container.go @@ -0,0 +1,222 @@ +package docker + +import ( + "bytes" + "fmt" + + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDockerContainer() *schema.Resource { + return &schema.Resource{ + Create: resourceDockerContainerCreate, + Read: resourceDockerContainerRead, + Update: resourceDockerContainerUpdate, + Delete: resourceDockerContainerDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + // Indicates whether the container must be running. + // + // An assumption is made that configured containers + // should be running; if not, they should not be in + // the configuration. Therefore a stopped container + // should be started. Set to false to have the + // provider leave the container alone. + // + // Actively-debugged containers are likely to be + // stopped and started manually, and Docker has + // some provisions for restarting containers that + // stop. The utility here comes from the fact that + // this will delete and re-create the container + // following the principle that the containers + // should be pristine when started. + "must_run": &schema.Schema{ + Type: schema.TypeBool, + Default: true, + Optional: true, + }, + + // ForceNew is not true for image because we need to + // sane this against Docker image IDs, as each image + // can have multiple names/tags attached do it. + "image": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "hostname": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "domainname": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "command": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "dns": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: stringSetHash, + }, + + "publish_all_ports": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "volumes": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: getVolumesElem(), + Set: resourceDockerVolumesHash, + }, + + "ports": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: getPortsElem(), + Set: resourceDockerPortsHash, + }, + + "env": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: stringSetHash, + }, + }, + } +} + +func getVolumesElem() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from_container": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "container_path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "host_path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "read_only": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func getPortsElem() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "internal": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "external": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "protocol": &schema.Schema{ + Type: schema.TypeString, + Default: "tcp", + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceDockerPortsHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + buf.WriteString(fmt.Sprintf("%v-", m["internal"].(int))) + + if v, ok := m["external"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(int))) + } + + if v, ok := m["ip"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + + if v, ok := m["protocol"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + + return hashcode.String(buf.String()) +} + +func resourceDockerVolumesHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + if v, ok := m["from_container"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + + if v, ok := m["container_path"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + + if v, ok := m["host_path"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(string))) + } + + if v, ok := m["read_only"]; ok { + buf.WriteString(fmt.Sprintf("%v-", v.(bool))) + } + + return hashcode.String(buf.String()) +} + +func stringSetHash(v interface{}) int { + return hashcode.String(v.(string)) +} diff --git a/builtin/providers/docker/resource_docker_container_funcs.go b/builtin/providers/docker/resource_docker_container_funcs.go new file mode 100644 index 000000000..d0daa08bb --- /dev/null +++ b/builtin/providers/docker/resource_docker_container_funcs.go @@ -0,0 +1,282 @@ +package docker + +import ( + "errors" + "fmt" + "strconv" + "strings" + + dc "github.com/fsouza/go-dockerclient" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + client, err := config.NewClient() + if err != nil { + return fmt.Errorf("Unable to connect to Docker: %s", err) + } + + data := config.NewData() + + if err := fetchLocalImages(data, client); err != nil { + return err + } + + image := d.Get("image").(string) + if _, ok := data.DockerImages[image]; !ok { + if _, ok := data.DockerImages[image+":latest"]; !ok { + return fmt.Errorf("Unable to find image %s", image) + } else { + image = image + ":latest" + } + } + + // The awesome, wonderful, splendiferous, sensical + // Docker API now lets you specify a HostConfig in + // CreateContainerOptions, but in my testing it still only + // actually applies HostConfig options set in StartContainer. + // How cool is that? + createOpts := dc.CreateContainerOptions{ + Name: d.Get("name").(string), + Config: &dc.Config{ + Image: image, + Hostname: d.Get("hostname").(string), + Domainname: d.Get("domainname").(string), + }, + } + + if v, ok := d.GetOk("env"); ok { + createOpts.Config.Env = stringSetToStringSlice(v.(*schema.Set)) + } + + if v, ok := d.GetOk("command"); ok { + createOpts.Config.Cmd = stringListToStringSlice(v.([]interface{})) + } + + exposedPorts := map[dc.Port]struct{}{} + portBindings := map[dc.Port][]dc.PortBinding{} + + if v, ok := d.GetOk("ports"); ok { + exposedPorts, portBindings = portSetToDockerPorts(v.(*schema.Set)) + } + if len(exposedPorts) != 0 { + createOpts.Config.ExposedPorts = exposedPorts + } + + volumes := map[string]struct{}{} + binds := []string{} + volumesFrom := []string{} + + if v, ok := d.GetOk("volumes"); ok { + volumes, binds, volumesFrom, err = volumeSetToDockerVolumes(v.(*schema.Set)) + if err != nil { + return fmt.Errorf("Unable to parse volumes: %s", err) + } + } + if len(volumes) != 0 { + createOpts.Config.Volumes = volumes + } + + var retContainer *dc.Container + if retContainer, err = client.CreateContainer(createOpts); err != nil { + return fmt.Errorf("Unable to create container: %s", err) + } + if retContainer == nil { + return fmt.Errorf("Returned container is nil") + } + + d.SetId(retContainer.ID) + + hostConfig := &dc.HostConfig{ + PublishAllPorts: d.Get("publish_all_ports").(bool), + } + + if len(portBindings) != 0 { + hostConfig.PortBindings = portBindings + } + + if len(binds) != 0 { + hostConfig.Binds = binds + } + if len(volumesFrom) != 0 { + hostConfig.VolumesFrom = volumesFrom + } + + if v, ok := d.GetOk("dns"); ok { + hostConfig.DNS = stringSetToStringSlice(v.(*schema.Set)) + } + + if err := client.StartContainer(retContainer.ID, hostConfig); err != nil { + return fmt.Errorf("Unable to start container: %s", err) + } + + return resourceDockerContainerRead(d, meta) +} + +func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + client, err := config.NewClient() + if err != nil { + return fmt.Errorf("Unable to connect to Docker: %s", err) + } + + apiContainer, err := fetchDockerContainer(d.Get("name").(string), client) + if err != nil { + return err + } + + if apiContainer == nil { + // This container doesn't exist anymore + d.SetId("") + + return nil + } + + container, err := client.InspectContainer(apiContainer.ID) + if err != nil { + return fmt.Errorf("Error inspecting container %s: %s", apiContainer.ID, err) + } + + if d.Get("must_run").(bool) && !container.State.Running { + return resourceDockerContainerDelete(d, meta) + } + + return nil +} + +func resourceDockerContainerUpdate(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceDockerContainerDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + client, err := config.NewClient() + if err != nil { + return fmt.Errorf("Unable to connect to Docker: %s", err) + } + + removeOpts := dc.RemoveContainerOptions{ + ID: d.Id(), + RemoveVolumes: true, + Force: true, + } + + if err := client.RemoveContainer(removeOpts); err != nil { + return fmt.Errorf("Error deleting container %s: %s", d.Id(), err) + } + + d.SetId("") + return nil +} + +func stringListToStringSlice(stringList []interface{}) []string { + ret := []string{} + for _, v := range stringList { + ret = append(ret, v.(string)) + } + return ret +} + +func stringSetToStringSlice(stringSet *schema.Set) []string { + ret := []string{} + if stringSet == nil { + return ret + } + for _, envVal := range stringSet.List() { + ret = append(ret, envVal.(string)) + } + return ret +} + +func fetchDockerContainer(name string, client *dc.Client) (*dc.APIContainers, error) { + apiContainers, err := client.ListContainers(dc.ListContainersOptions{All: true}) + + if err != nil { + return nil, fmt.Errorf("Error fetching container information from Docker: %s\n", err) + } + + for _, apiContainer := range apiContainers { + // Sometimes the Docker API prefixes container names with / + // like it does in these commands. But if there's no + // set name, it just uses the ID without a /...ugh. + var dockerContainerName string + if len(apiContainer.Names) > 0 { + dockerContainerName = strings.TrimLeft(apiContainer.Names[0], "/") + } else { + dockerContainerName = apiContainer.ID + } + + if dockerContainerName == name { + return &apiContainer, nil + } + } + + return nil, nil +} + +func portSetToDockerPorts(ports *schema.Set) (map[dc.Port]struct{}, map[dc.Port][]dc.PortBinding) { + retExposedPorts := map[dc.Port]struct{}{} + retPortBindings := map[dc.Port][]dc.PortBinding{} + + for _, portInt := range ports.List() { + port := portInt.(map[string]interface{}) + internal := port["internal"].(int) + protocol := port["protocol"].(string) + + exposedPort := dc.Port(strconv.Itoa(internal) + "/" + protocol) + retExposedPorts[exposedPort] = struct{}{} + + external, extOk := port["external"].(int) + ip, ipOk := port["ip"].(string) + + if extOk { + portBinding := dc.PortBinding{ + HostPort: strconv.Itoa(external), + } + if ipOk { + portBinding.HostIP = ip + } + retPortBindings[exposedPort] = append(retPortBindings[exposedPort], portBinding) + } + } + + return retExposedPorts, retPortBindings +} + +func volumeSetToDockerVolumes(volumes *schema.Set) (map[string]struct{}, []string, []string, error) { + retVolumeMap := map[string]struct{}{} + retHostConfigBinds := []string{} + retVolumeFromContainers := []string{} + + for _, volumeInt := range volumes.List() { + volume := volumeInt.(map[string]interface{}) + fromContainer := volume["from_container"].(string) + containerPath := volume["container_path"].(string) + hostPath := volume["host_path"].(string) + readOnly := volume["read_only"].(bool) + + switch { + case len(fromContainer) == 0 && len(containerPath) == 0: + return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Volume entry without container path or source container") + case len(fromContainer) != 0 && len(containerPath) != 0: + return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, errors.New("Both a container and a path specified in a volume entry") + case len(fromContainer) != 0: + retVolumeFromContainers = append(retVolumeFromContainers, fromContainer) + case len(hostPath) != 0: + readWrite := "rw" + if readOnly { + readWrite = "ro" + } + retVolumeMap[containerPath] = struct{}{} + retHostConfigBinds = append(retHostConfigBinds, hostPath+":"+containerPath+":"+readWrite) + default: + retVolumeMap[containerPath] = struct{}{} + } + } + + return retVolumeMap, retHostConfigBinds, retVolumeFromContainers, nil +} diff --git a/builtin/providers/docker/resource_docker_image.go b/builtin/providers/docker/resource_docker_image.go new file mode 100644 index 000000000..54822d738 --- /dev/null +++ b/builtin/providers/docker/resource_docker_image.go @@ -0,0 +1,31 @@ +package docker + +import ( + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDockerImage() *schema.Resource { + return &schema.Resource{ + Create: resourceDockerImageCreate, + Read: resourceDockerImageRead, + Update: resourceDockerImageUpdate, + Delete: resourceDockerImageDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "keep_updated": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + + "latest": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} diff --git a/builtin/providers/docker/resource_docker_image_funcs.go b/builtin/providers/docker/resource_docker_image_funcs.go new file mode 100644 index 000000000..a34f21994 --- /dev/null +++ b/builtin/providers/docker/resource_docker_image_funcs.go @@ -0,0 +1,177 @@ +package docker + +import ( + "fmt" + "strings" + + dc "github.com/fsouza/go-dockerclient" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + apiImage, err := findImage(d, config) + if err != nil { + return fmt.Errorf("Unable to read Docker image into resource: %s", err) + } + + d.SetId(apiImage.ID + d.Get("name").(string)) + d.Set("latest", apiImage.ID) + + return nil +} + +func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + apiImage, err := findImage(d, config) + if err != nil { + return fmt.Errorf("Unable to read Docker image into resource: %s", err) + } + + d.Set("latest", apiImage.ID) + + return nil +} + +func resourceDockerImageUpdate(d *schema.ResourceData, meta interface{}) error { + // We need to re-read in case switching parameters affects + // the value of "latest" or others + + return resourceDockerImageRead(d, meta) +} + +func resourceDockerImageDelete(d *schema.ResourceData, meta interface{}) error { + d.SetId("") + return nil +} + +func fetchLocalImages(data *Data, client *dc.Client) error { + images, err := client.ListImages(dc.ListImagesOptions{All: false}) + if err != nil { + return fmt.Errorf("Unable to list Docker images: %s", err) + } + + // Docker uses different nomenclatures in different places...sometimes a short + // ID, sometimes long, etc. So we store both in the map so we can always find + // the same image object. We store the tags, too. + for i, image := range images { + data.DockerImages[image.ID[:12]] = &images[i] + data.DockerImages[image.ID] = &images[i] + for _, repotag := range image.RepoTags { + data.DockerImages[repotag] = &images[i] + } + } + + return nil +} + +func pullImage(data *Data, client *dc.Client, image string) error { + // TODO: Test local registry handling. It should be working + // based on the code that was ported over + + pullOpts := dc.PullImageOptions{} + + splitImageName := strings.Split(image, ":") + switch { + + // It's in registry:port/repo:tag format + case len(splitImageName) == 3: + splitPortRepo := strings.Split(splitImageName[1], "/") + pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0] + pullOpts.Repository = splitPortRepo[1] + pullOpts.Tag = splitImageName[2] + + // It's either registry:port/repo or repo:tag with default registry + case len(splitImageName) == 2: + splitPortRepo := strings.Split(splitImageName[1], "/") + switch len(splitPortRepo) { + + // registry:port/repo + case 2: + pullOpts.Registry = splitImageName[0] + ":" + splitPortRepo[0] + pullOpts.Repository = splitPortRepo[1] + pullOpts.Tag = "latest" + + // repo:tag + case 1: + pullOpts.Repository = splitImageName[0] + pullOpts.Tag = splitImageName[1] + } + + default: + pullOpts.Repository = image + } + + if err := client.PullImage(pullOpts, dc.AuthConfiguration{}); err != nil { + return fmt.Errorf("Error pulling image %s: %s\n", image, err) + } + + return fetchLocalImages(data, client) +} + +func getImageTag(image string) string { + splitImageName := strings.Split(image, ":") + switch { + + // It's in registry:port/repo:tag format + case len(splitImageName) == 3: + return splitImageName[2] + + // It's either registry:port/repo or repo:tag with default registry + case len(splitImageName) == 2: + splitPortRepo := strings.Split(splitImageName[1], "/") + if len(splitPortRepo) == 2 { + return "" + } else { + return splitImageName[1] + } + } + + return "" +} + +func findImage(d *schema.ResourceData, config *Config) (*dc.APIImages, error) { + client, err := config.NewClient() + if err != nil { + return nil, fmt.Errorf("Unable to connect to Docker: %s", err) + } + + data := config.NewData() + + if err := fetchLocalImages(data, client); err != nil { + return nil, err + } + + imageName := d.Get("name").(string) + if imageName == "" { + return nil, fmt.Errorf("Empty image name is not allowed") + } + + searchLocal := func() *dc.APIImages { + if apiImage, ok := data.DockerImages[imageName]; ok { + return apiImage + } + if apiImage, ok := data.DockerImages[imageName+":latest"]; ok { + imageName = imageName + ":latest" + return apiImage + } + return nil + } + + foundImage := searchLocal() + + if d.Get("keep_updated").(bool) || foundImage == nil { + if err := pullImage(data, client, imageName); err != nil { + return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err) + } + } + + foundImage = searchLocal() + if foundImage != nil { + return foundImage, nil + } + + return nil, fmt.Errorf("Unable to find or pull image %s", imageName) +} diff --git a/config.go b/config.go index 583d7ddb2..648223888 100644 --- a/config.go +++ b/config.go @@ -179,7 +179,7 @@ func (c *Config) discoverSingle(glob string, m *map[string]string) error { continue } - log.Printf("[DEBUG] Discoverd plugin: %s = %s", parts[2], match) + log.Printf("[DEBUG] Discovered plugin: %s = %s", parts[2], match) (*m)[parts[2]] = match } From b64a919d83b44fcba5a6e754401e0429115e1153 Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Thu, 26 Mar 2015 16:45:23 -0500 Subject: [PATCH 002/167] provider/aws: Add tags to Route53 hosted zones --- .../aws/resource_aws_route53_zone.go | 34 +++++++- builtin/providers/aws/tags_route53.go | 87 +++++++++++++++++++ builtin/providers/aws/tags_route53_test.go | 85 ++++++++++++++++++ 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 builtin/providers/aws/tags_route53.go create mode 100644 builtin/providers/aws/tags_route53_test.go diff --git a/builtin/providers/aws/resource_aws_route53_zone.go b/builtin/providers/aws/resource_aws_route53_zone.go index 6d9914b7f..e6c8be571 100644 --- a/builtin/providers/aws/resource_aws_route53_zone.go +++ b/builtin/providers/aws/resource_aws_route53_zone.go @@ -16,6 +16,7 @@ func resourceAwsRoute53Zone() *schema.Resource { return &schema.Resource{ Create: resourceAwsRoute53ZoneCreate, Read: resourceAwsRoute53ZoneRead, + Update: resourceAwsRoute53ZoneUpdate, Delete: resourceAwsRoute53ZoneDelete, Schema: map[string]*schema.Schema{ @@ -29,6 +30,8 @@ func resourceAwsRoute53Zone() *schema.Resource { Type: schema.TypeString, Computed: true, }, + + "tags": tagsSchema(), }, } } @@ -72,7 +75,7 @@ func resourceAwsRoute53ZoneCreate(d *schema.ResourceData, meta interface{}) erro if err != nil { return err } - return nil + return resourceAwsRoute53ZoneUpdate(d, meta) } func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error { @@ -87,9 +90,38 @@ func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error return err } + // get tags + req := &route53.ListTagsForResourceRequest{ + ResourceID: aws.String(d.Id()), + ResourceType: aws.String("hostedzone"), + } + + resp, err := r53.ListTagsForResource(req) + if err != nil { + return err + } + + var tags []route53.Tag + if resp.ResourceTagSet != nil { + tags = resp.ResourceTagSet.Tags + } + d.Set("tags", tagsToMapR53(tags)) + return nil } +func resourceAwsRoute53ZoneUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).r53conn + + if err := setTagsR53(conn, d); err != nil { + return err + } else { + d.SetPartial("tags") + } + + return resourceAwsRoute53ZoneRead(d, meta) +} + func resourceAwsRoute53ZoneDelete(d *schema.ResourceData, meta interface{}) error { r53 := meta.(*AWSClient).r53conn diff --git a/builtin/providers/aws/tags_route53.go b/builtin/providers/aws/tags_route53.go new file mode 100644 index 000000000..374c4bc32 --- /dev/null +++ b/builtin/providers/aws/tags_route53.go @@ -0,0 +1,87 @@ +package aws + +import ( + "log" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/route53" + "github.com/hashicorp/terraform/helper/schema" +) + +// setTags is a helper to set the tags for a resource. It expects the +// tags field to be named "tags" +func setTagsR53(conn *route53.Route53, d *schema.ResourceData) error { + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffTagsR53(tagsFromMapR53(o), tagsFromMapR53(n)) + + // Set tags + r := make([]string, len(remove)) + for i, t := range remove { + r[i] = *t.Key + } + log.Printf("[DEBUG] Changing tags: \n\tadding: %#v\n\tremoving:", create, remove) + req := &route53.ChangeTagsForResourceRequest{ + AddTags: create, + RemoveTagKeys: r, + ResourceID: aws.String(d.Id()), + ResourceType: aws.String("hostedzone"), + } + resp, err := conn.ChangeTagsForResource(req) + log.Printf("\n\t****\nresp:\n%#v", resp) + if err != nil { + log.Printf("\n\t****\nerror:\n%#v", err) + return err + } + } + + return nil +} + +// diffTags takes our tags locally and the ones remotely and returns +// the set of tags that must be created, and the set of tags that must +// be destroyed. +func diffTagsR53(oldTags, newTags []route53.Tag) ([]route53.Tag, []route53.Tag) { + // First, we're creating everything we have + create := make(map[string]interface{}) + for _, t := range newTags { + create[*t.Key] = *t.Value + } + + // Build the list of what to remove + var remove []route53.Tag + for _, t := range oldTags { + old, ok := create[*t.Key] + if !ok || old != *t.Value { + // Delete it! + remove = append(remove, t) + } + } + + return tagsFromMapR53(create), remove +} + +// tagsFromMap returns the tags for the given map of data. +func tagsFromMapR53(m map[string]interface{}) []route53.Tag { + result := make([]route53.Tag, 0, len(m)) + for k, v := range m { + result = append(result, route53.Tag{ + Key: aws.String(k), + Value: aws.String(v.(string)), + }) + } + + return result +} + +// tagsToMap turns the list of tags into a map. +func tagsToMapR53(ts []route53.Tag) map[string]string { + result := make(map[string]string) + for _, t := range ts { + result[*t.Key] = *t.Value + } + + return result +} diff --git a/builtin/providers/aws/tags_route53_test.go b/builtin/providers/aws/tags_route53_test.go new file mode 100644 index 000000000..79ce1e513 --- /dev/null +++ b/builtin/providers/aws/tags_route53_test.go @@ -0,0 +1,85 @@ +package aws + +import ( + "fmt" + "reflect" + "testing" + + "github.com/hashicorp/aws-sdk-go/gen/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestDiffTagsR53(t *testing.T) { + cases := []struct { + Old, New map[string]interface{} + Create, Remove map[string]string + }{ + // Basic add/remove + { + Old: map[string]interface{}{ + "foo": "bar", + }, + New: map[string]interface{}{ + "bar": "baz", + }, + Create: map[string]string{ + "bar": "baz", + }, + Remove: map[string]string{ + "foo": "bar", + }, + }, + + // Modify + { + Old: map[string]interface{}{ + "foo": "bar", + }, + New: map[string]interface{}{ + "foo": "baz", + }, + Create: map[string]string{ + "foo": "baz", + }, + Remove: map[string]string{ + "foo": "bar", + }, + }, + } + + for i, tc := range cases { + c, r := diffTags(tagsFromMap(tc.Old), tagsFromMap(tc.New)) + cm := tagsToMap(c) + rm := tagsToMap(r) + if !reflect.DeepEqual(cm, tc.Create) { + t.Fatalf("%d: bad create: %#v", i, cm) + } + if !reflect.DeepEqual(rm, tc.Remove) { + t.Fatalf("%d: bad remove: %#v", i, rm) + } + } +} + +// testAccCheckTags can be used to check the tags on a resource. +func testAccCheckTagsR53( + ts *[]ec2.Tag, key string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + m := tagsToMap(*ts) + v, ok := m[key] + if value != "" && !ok { + return fmt.Errorf("Missing tag: %s", key) + } else if value == "" && ok { + return fmt.Errorf("Extra tag: %s", key) + } + if value == "" { + return nil + } + + if v != value { + return fmt.Errorf("%s: bad value: %s", key, v) + } + + return nil + } +} From 12585b19638bd5eb06a5edf88bb3f0ad7e16c39e Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Fri, 27 Mar 2015 15:41:42 -0500 Subject: [PATCH 003/167] provider/aws: Finish Tag support for Route 53 zone --- .../aws/resource_aws_route53_zone_test.go | 43 +++++++++++++++++-- builtin/providers/aws/tags_route53.go | 4 +- builtin/providers/aws/tags_route53_test.go | 12 +++--- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/builtin/providers/aws/resource_aws_route53_zone_test.go b/builtin/providers/aws/resource_aws_route53_zone_test.go index fa78634cf..d6e4af2f2 100644 --- a/builtin/providers/aws/resource_aws_route53_zone_test.go +++ b/builtin/providers/aws/resource_aws_route53_zone_test.go @@ -63,6 +63,9 @@ func TestCleanChangeID(t *testing.T) { } func TestAccRoute53Zone(t *testing.T) { + var zone route53.HostedZone + var td route53.ResourceTagSet + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -71,7 +74,9 @@ func TestAccRoute53Zone(t *testing.T) { resource.TestStep{ Config: testAccRoute53ZoneConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckRoute53ZoneExists("aws_route53_zone.main"), + testAccCheckRoute53ZoneExists("aws_route53_zone.main", &zone), + testAccLoadTagsR53(&zone, &td), + testAccCheckTagsR53(&td.Tags, "foo", "bar"), ), }, }, @@ -93,7 +98,7 @@ func testAccCheckRoute53ZoneDestroy(s *terraform.State) error { return nil } -func testAccCheckRoute53ZoneExists(n string) resource.TestCheckFunc { +func testAccCheckRoute53ZoneExists(n string, zone *route53.HostedZone) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -105,10 +110,37 @@ func testAccCheckRoute53ZoneExists(n string) resource.TestCheckFunc { } conn := testAccProvider.Meta().(*AWSClient).r53conn - _, err := conn.GetHostedZone(&route53.GetHostedZoneRequest{ID: aws.String(rs.Primary.ID)}) + resp, err := conn.GetHostedZone(&route53.GetHostedZoneRequest{ID: aws.String(rs.Primary.ID)}) if err != nil { return fmt.Errorf("Hosted zone err: %v", err) } + *zone = *resp.HostedZone + return nil + } +} + +func testAccLoadTagsR53(zone *route53.HostedZone, td *route53.ResourceTagSet) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).r53conn + + zone := cleanZoneID(*zone.ID) + req := &route53.ListTagsForResourceRequest{ + ResourceID: aws.String(zone), + ResourceType: aws.String("hostedzone"), + } + + resp, err := conn.ListTagsForResource(req) + if err != nil { + return err + } + + var tags []route53.Tag + if resp.ResourceTagSet != nil { + tags = resp.ResourceTagSet.Tags + } + + *td = *resp.ResourceTagSet + return nil } } @@ -116,5 +148,10 @@ func testAccCheckRoute53ZoneExists(n string) resource.TestCheckFunc { const testAccRoute53ZoneConfig = ` resource "aws_route53_zone" "main" { name = "hashicorp.com" + + tags { + foo = "bar" + Name = "tf-route53-tag-test" + } } ` diff --git a/builtin/providers/aws/tags_route53.go b/builtin/providers/aws/tags_route53.go index 374c4bc32..8e31fc373 100644 --- a/builtin/providers/aws/tags_route53.go +++ b/builtin/providers/aws/tags_route53.go @@ -22,7 +22,7 @@ func setTagsR53(conn *route53.Route53, d *schema.ResourceData) error { for i, t := range remove { r[i] = *t.Key } - log.Printf("[DEBUG] Changing tags: \n\tadding: %#v\n\tremoving:", create, remove) + log.Printf("[DEBUG] Changing tags: \n\tadding: %#v\n\tremoving:%#v", create, remove) req := &route53.ChangeTagsForResourceRequest{ AddTags: create, RemoveTagKeys: r, @@ -30,9 +30,7 @@ func setTagsR53(conn *route53.Route53, d *schema.ResourceData) error { ResourceType: aws.String("hostedzone"), } resp, err := conn.ChangeTagsForResource(req) - log.Printf("\n\t****\nresp:\n%#v", resp) if err != nil { - log.Printf("\n\t****\nerror:\n%#v", err) return err } } diff --git a/builtin/providers/aws/tags_route53_test.go b/builtin/providers/aws/tags_route53_test.go index 79ce1e513..40a4154f3 100644 --- a/builtin/providers/aws/tags_route53_test.go +++ b/builtin/providers/aws/tags_route53_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "github.com/hashicorp/aws-sdk-go/gen/ec2" + "github.com/hashicorp/aws-sdk-go/gen/route53" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -49,9 +49,9 @@ func TestDiffTagsR53(t *testing.T) { } for i, tc := range cases { - c, r := diffTags(tagsFromMap(tc.Old), tagsFromMap(tc.New)) - cm := tagsToMap(c) - rm := tagsToMap(r) + c, r := diffTagsR53(tagsFromMapR53(tc.Old), tagsFromMapR53(tc.New)) + cm := tagsToMapR53(c) + rm := tagsToMapR53(r) if !reflect.DeepEqual(cm, tc.Create) { t.Fatalf("%d: bad create: %#v", i, cm) } @@ -63,9 +63,9 @@ func TestDiffTagsR53(t *testing.T) { // testAccCheckTags can be used to check the tags on a resource. func testAccCheckTagsR53( - ts *[]ec2.Tag, key string, value string) resource.TestCheckFunc { + ts *[]route53.Tag, key string, value string) resource.TestCheckFunc { return func(s *terraform.State) error { - m := tagsToMap(*ts) + m := tagsToMapR53(*ts) v, ok := m[key] if value != "" && !ok { return fmt.Errorf("Missing tag: %s", key) From ce8ec26d0803ce31c40c9403a1acc0a2add8847c Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Fri, 27 Mar 2015 16:05:54 -0500 Subject: [PATCH 004/167] cleanups --- builtin/providers/aws/resource_aws_route53_zone_test.go | 5 +---- builtin/providers/aws/tags_route53.go | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/builtin/providers/aws/resource_aws_route53_zone_test.go b/builtin/providers/aws/resource_aws_route53_zone_test.go index d6e4af2f2..0669f88b1 100644 --- a/builtin/providers/aws/resource_aws_route53_zone_test.go +++ b/builtin/providers/aws/resource_aws_route53_zone_test.go @@ -134,13 +134,10 @@ func testAccLoadTagsR53(zone *route53.HostedZone, td *route53.ResourceTagSet) re return err } - var tags []route53.Tag if resp.ResourceTagSet != nil { - tags = resp.ResourceTagSet.Tags + *td = *resp.ResourceTagSet } - *td = *resp.ResourceTagSet - return nil } } diff --git a/builtin/providers/aws/tags_route53.go b/builtin/providers/aws/tags_route53.go index 8e31fc373..e5251d02a 100644 --- a/builtin/providers/aws/tags_route53.go +++ b/builtin/providers/aws/tags_route53.go @@ -29,7 +29,8 @@ func setTagsR53(conn *route53.Route53, d *schema.ResourceData) error { ResourceID: aws.String(d.Id()), ResourceType: aws.String("hostedzone"), } - resp, err := conn.ChangeTagsForResource(req) + + _, err := conn.ChangeTagsForResource(req) if err != nil { return err } From d6303c91adf213f4293fd42832d7b91781c57254 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 27 Mar 2015 15:18:52 -0700 Subject: [PATCH 005/167] providers/docker: support DOCKER_CERT_PATH --- builtin/providers/docker/config.go | 22 ++++++++++++++++++---- builtin/providers/docker/provider.go | 14 +++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/builtin/providers/docker/config.go b/builtin/providers/docker/config.go index 40355b24f..ed13314a2 100644 --- a/builtin/providers/docker/config.go +++ b/builtin/providers/docker/config.go @@ -1,10 +1,15 @@ package docker -import dc "github.com/fsouza/go-dockerclient" +import ( + "path/filepath" + + dc "github.com/fsouza/go-dockerclient" +) type Config struct { - DockerHost string - SkipPull bool + Host string + CertPath string + SkipPull bool } type Data struct { @@ -13,7 +18,16 @@ type Data struct { // NewClient() returns a new Docker client. func (c *Config) NewClient() (*dc.Client, error) { - return dc.NewClient(c.DockerHost) + // If there is no cert information, then just return the direct client + if c.CertPath == "" { + return dc.NewClient(c.Host) + } + + // If there is cert information, load it and use it. + ca := filepath.Join(c.CertPath, "ca.pem") + cert := filepath.Join(c.CertPath, "cert.pem") + key := filepath.Join(c.CertPath, "key.pem") + return dc.NewTLSClient(c.Host, cert, key, ca) } // NewData() returns a new data struct. diff --git a/builtin/providers/docker/provider.go b/builtin/providers/docker/provider.go index d01ec385c..77da4bf15 100644 --- a/builtin/providers/docker/provider.go +++ b/builtin/providers/docker/provider.go @@ -8,11 +8,18 @@ import ( func Provider() terraform.ResourceProvider { return &schema.Provider{ Schema: map[string]*schema.Schema{ - "docker_host": &schema.Schema{ + "host": &schema.Schema{ Type: schema.TypeString, Required: true, DefaultFunc: schema.EnvDefaultFunc("DOCKER_HOST", "unix:/run/docker.sock"), - Description: "The Docker daemon endpoint", + Description: "The Docker daemon address", + }, + + "cert_path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("DOCKER_CERT_PATH", nil), + Description: "Path to directory with Docker TLS config", }, }, @@ -27,7 +34,8 @@ func Provider() terraform.ResourceProvider { func providerConfigure(d *schema.ResourceData) (interface{}, error) { config := Config{ - DockerHost: d.Get("docker_host").(string), + Host: d.Get("host").(string), + CertPath: d.Get("cert_path").(string), } return &config, nil From 3601e9f5ee21de7f954271f6cbaceaf597ec73d8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 27 Mar 2015 15:22:33 -0700 Subject: [PATCH 006/167] providers/docker: docker_image acceptance test --- builtin/providers/docker/provider_test.go | 36 +++++++++++++++++++ .../docker/resource_docker_image_test.go | 32 +++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 builtin/providers/docker/provider_test.go create mode 100644 builtin/providers/docker/resource_docker_image_test.go diff --git a/builtin/providers/docker/provider_test.go b/builtin/providers/docker/provider_test.go new file mode 100644 index 000000000..d09104889 --- /dev/null +++ b/builtin/providers/docker/provider_test.go @@ -0,0 +1,36 @@ +package docker + +import ( + "os/exec" + "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "docker": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + cmd := exec.Command("docker", "version") + if err := cmd.Run(); err != nil { + t.Fatalf("Docker must be available: %s", err) + } +} diff --git a/builtin/providers/docker/resource_docker_image_test.go b/builtin/providers/docker/resource_docker_image_test.go new file mode 100644 index 000000000..d43c81efc --- /dev/null +++ b/builtin/providers/docker/resource_docker_image_test.go @@ -0,0 +1,32 @@ +package docker + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDockerImage_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDockerImageConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "docker_image.foo", + "latest", + "d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07"), + ), + }, + }, + }) +} + +const testAccDockerImageConfig = ` +resource "docker_image" "foo" { + name = "ubuntu:trusty-20150320" + keep_updated = true +} +` From 7c253155c1a858c3f87d92feae04411decda1b0f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 27 Mar 2015 15:33:17 -0700 Subject: [PATCH 007/167] providers/docker: container acceptance tests --- .../docker/resource_docker_container_test.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 builtin/providers/docker/resource_docker_container_test.go diff --git a/builtin/providers/docker/resource_docker_container_test.go b/builtin/providers/docker/resource_docker_container_test.go new file mode 100644 index 000000000..9e2ef6419 --- /dev/null +++ b/builtin/providers/docker/resource_docker_container_test.go @@ -0,0 +1,36 @@ +package docker + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDockerContainer_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDockerContainerConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "docker_image.foo", + "latest", + "d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07"), + ), + }, + }, + }) +} + +const testAccDockerContainerConfig = ` +resource "docker_image" "foo" { + name = "ubuntu:trusty-20150320" +} + +resource "docker_container" "foo" { + name = "tf-test" + image = "${docker_image.foo.latest}" +} +` From 09333e5e769d62a52bc4097556779871b46c1098 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 28 Mar 2015 18:37:20 -0700 Subject: [PATCH 008/167] providres/docker: cache client --- builtin/providers/docker/config.go | 15 ++++------ builtin/providers/docker/provider.go | 2 +- .../docker/resource_docker_container_funcs.go | 27 ++++-------------- .../docker/resource_docker_image_funcs.go | 28 ++++++++----------- 4 files changed, 24 insertions(+), 48 deletions(-) diff --git a/builtin/providers/docker/config.go b/builtin/providers/docker/config.go index ed13314a2..199182744 100644 --- a/builtin/providers/docker/config.go +++ b/builtin/providers/docker/config.go @@ -6,14 +6,11 @@ import ( dc "github.com/fsouza/go-dockerclient" ) +// Config is the structure that stores the configuration to talk to a +// Docker API compatible host. type Config struct { Host string CertPath string - SkipPull bool -} - -type Data struct { - DockerImages map[string]*dc.APIImages } // NewClient() returns a new Docker client. @@ -30,9 +27,7 @@ func (c *Config) NewClient() (*dc.Client, error) { return dc.NewTLSClient(c.Host, cert, key, ca) } -// NewData() returns a new data struct. -func (c *Config) NewData() *Data { - return &Data{ - DockerImages: map[string]*dc.APIImages{}, - } +// Data ia structure for holding data that we fetch from Docker. +type Data struct { + DockerImages map[string]*dc.APIImages } diff --git a/builtin/providers/docker/provider.go b/builtin/providers/docker/provider.go index 77da4bf15..15bc840a4 100644 --- a/builtin/providers/docker/provider.go +++ b/builtin/providers/docker/provider.go @@ -38,5 +38,5 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { CertPath: d.Get("cert_path").(string), } - return &config, nil + return config.NewClient() } diff --git a/builtin/providers/docker/resource_docker_container_funcs.go b/builtin/providers/docker/resource_docker_container_funcs.go index d0daa08bb..17a8e4eed 100644 --- a/builtin/providers/docker/resource_docker_container_funcs.go +++ b/builtin/providers/docker/resource_docker_container_funcs.go @@ -11,16 +11,11 @@ import ( ) func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) + var err error + client := meta.(*dc.Client) - client, err := config.NewClient() - if err != nil { - return fmt.Errorf("Unable to connect to Docker: %s", err) - } - - data := config.NewData() - - if err := fetchLocalImages(data, client); err != nil { + var data Data + if err := fetchLocalImages(&data, client); err != nil { return err } @@ -116,12 +111,7 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err } func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - client, err := config.NewClient() - if err != nil { - return fmt.Errorf("Unable to connect to Docker: %s", err) - } + client := meta.(*dc.Client) apiContainer, err := fetchDockerContainer(d.Get("name").(string), client) if err != nil { @@ -152,12 +142,7 @@ func resourceDockerContainerUpdate(d *schema.ResourceData, meta interface{}) err } func resourceDockerContainerDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - client, err := config.NewClient() - if err != nil { - return fmt.Errorf("Unable to connect to Docker: %s", err) - } + client := meta.(*dc.Client) removeOpts := dc.RemoveContainerOptions{ ID: d.Id(), diff --git a/builtin/providers/docker/resource_docker_image_funcs.go b/builtin/providers/docker/resource_docker_image_funcs.go index a34f21994..2c7470db0 100644 --- a/builtin/providers/docker/resource_docker_image_funcs.go +++ b/builtin/providers/docker/resource_docker_image_funcs.go @@ -9,9 +9,8 @@ import ( ) func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - apiImage, err := findImage(d, config) + client := meta.(*dc.Client) + apiImage, err := findImage(d, client) if err != nil { return fmt.Errorf("Unable to read Docker image into resource: %s", err) } @@ -23,9 +22,8 @@ func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error { } func resourceDockerImageRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - apiImage, err := findImage(d, config) + client := meta.(*dc.Client) + apiImage, err := findImage(d, client) if err != nil { return fmt.Errorf("Unable to read Docker image into resource: %s", err) } @@ -53,6 +51,10 @@ func fetchLocalImages(data *Data, client *dc.Client) error { return fmt.Errorf("Unable to list Docker images: %s", err) } + if data.DockerImages == nil { + data.DockerImages = make(map[string]*dc.APIImages) + } + // Docker uses different nomenclatures in different places...sometimes a short // ID, sometimes long, etc. So we store both in the map so we can always find // the same image object. We store the tags, too. @@ -132,15 +134,9 @@ func getImageTag(image string) string { return "" } -func findImage(d *schema.ResourceData, config *Config) (*dc.APIImages, error) { - client, err := config.NewClient() - if err != nil { - return nil, fmt.Errorf("Unable to connect to Docker: %s", err) - } - - data := config.NewData() - - if err := fetchLocalImages(data, client); err != nil { +func findImage(d *schema.ResourceData, client *dc.Client) (*dc.APIImages, error) { + var data Data + if err := fetchLocalImages(&data, client); err != nil { return nil, err } @@ -163,7 +159,7 @@ func findImage(d *schema.ResourceData, config *Config) (*dc.APIImages, error) { foundImage := searchLocal() if d.Get("keep_updated").(bool) || foundImage == nil { - if err := pullImage(data, client, imageName); err != nil { + if err := pullImage(&data, client, imageName); err != nil { return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err) } } From a7a5d2e5649775019fbb2d97c213a58b193a3739 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 28 Mar 2015 18:45:36 -0700 Subject: [PATCH 009/167] providers/docker: make container test better --- .../docker/resource_docker_container_test.go | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/builtin/providers/docker/resource_docker_container_test.go b/builtin/providers/docker/resource_docker_container_test.go index 9e2ef6419..48302d096 100644 --- a/builtin/providers/docker/resource_docker_container_test.go +++ b/builtin/providers/docker/resource_docker_container_test.go @@ -1,9 +1,12 @@ package docker import ( + "fmt" "testing" + dc "github.com/fsouza/go-dockerclient" "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" ) func TestAccDockerContainer_basic(t *testing.T) { @@ -14,16 +17,40 @@ func TestAccDockerContainer_basic(t *testing.T) { resource.TestStep{ Config: testAccDockerContainerConfig, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "docker_image.foo", - "latest", - "d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07"), + testAccContainerRunning("docker_container.foo"), ), }, }, }) } +func testAccContainerRunning(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + client := testAccProvider.Meta().(*dc.Client) + containers, err := client.ListContainers(dc.ListContainersOptions{}) + if err != nil { + return err + } + + for _, c := range containers { + if c.ID == rs.Primary.ID { + return nil + } + } + + return fmt.Errorf("Container not found: %s", rs.Primary.ID) + } +} + const testAccDockerContainerConfig = ` resource "docker_image" "foo" { name = "ubuntu:trusty-20150320" From 2f0235b23daee1cceeacf8f845e4c69f8b92d79b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 28 Mar 2015 19:05:17 -0700 Subject: [PATCH 010/167] website: docker docs --- website/source/assets/stylesheets/_docs.scss | 1 + .../docs/providers/docker/index.html.markdown | 47 +++++++++++ .../docker/r/container.html.markdown | 77 +++++++++++++++++++ .../providers/docker/r/image.html.markdown | 41 ++++++++++ website/source/layouts/docker.erb | 30 ++++++++ website/source/layouts/docs.erb | 6 +- 6 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 website/source/docs/providers/docker/index.html.markdown create mode 100644 website/source/docs/providers/docker/r/container.html.markdown create mode 100644 website/source/docs/providers/docker/r/image.html.markdown create mode 100644 website/source/layouts/docker.erb diff --git a/website/source/assets/stylesheets/_docs.scss b/website/source/assets/stylesheets/_docs.scss index f144f813a..27cbba873 100755 --- a/website/source/assets/stylesheets/_docs.scss +++ b/website/source/assets/stylesheets/_docs.scss @@ -10,6 +10,7 @@ body.layout-atlas, body.layout-consul, body.layout-dnsimple, body.layout-dme, +body.layout-docker, body.layout-cloudflare, body.layout-cloudstack, body.layout-google, diff --git a/website/source/docs/providers/docker/index.html.markdown b/website/source/docs/providers/docker/index.html.markdown new file mode 100644 index 000000000..73807c12e --- /dev/null +++ b/website/source/docs/providers/docker/index.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "docker" +page_title: "Provider: Docker" +sidebar_current: "docs-docker-index" +description: |- + The Docker provider is used to interact with Docker containers and images. +--- + +# Docker Provider + +The Docker provider is used to interact with Docker containers and images. +It uses the Docker API to manage the lifecycle of Docker containers. Because +the Docker provider uses the Docker API, it is immediatel compatible not +only with single server Docker but Swarm and any additional Docker-compatible +API hosts. + +Use the navigation to the left to read about the available resources. + +## Example Usage + +``` +# Configure the Docker provider +provider "docker" { + host = "tcp://127.0.0.1:1234/" +} + +# Create a container +resource "docker_container" "foo" { + image = "${docker_image.ubuntu.latest}" + name = "foo" +} + +resource "docker_image" "ubuntu" { + name = "ubuntu:latest" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `host` - (Required) This is the address to the Docker host. If this is + blank, the `DOCKER_HOST` environment variable will also be read. + +* `cert_path` - (Optional) Path to a directory with certificate information + for connecting to the Docker host via TLS. If this is blank, the + `DOCKER_CERT_PATH` will also be checked. diff --git a/website/source/docs/providers/docker/r/container.html.markdown b/website/source/docs/providers/docker/r/container.html.markdown new file mode 100644 index 000000000..418e35fc1 --- /dev/null +++ b/website/source/docs/providers/docker/r/container.html.markdown @@ -0,0 +1,77 @@ +--- +layout: "docker" +page_title: "Docker: docker_container" +sidebar_current: "docs-docker-resource-container" +description: |- + Manages the lifecycle of a Docker container. +--- + +# docker\_container + +Manages the lifecycle of a Docker container. + +## Example Usage + +``` +# Start a container +resource "docker_container" "ubuntu" { + name = "foo" + image = "${docker_image.ubuntu.latest}" +} + +# Find the latest Ubuntu precise image. +resource "docker_image" "ubuntu" { + image = "ubuntu:precise" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required, string) The name of the Docker container. +* `image` - (Required, string) The ID of the image to back this container. + The easiest way to get this value is to use the `docker_image` resource + as is shown in the example above. + +* `command` - (Optional, list of strings) The command to use to start the + container. +* `dns` - (Optional, set of strings) Set of DNS servers. +* `env` - (Optional, set of strings) Environmental variables to set. +* `hostname` - (Optional, string) Hostname of the container. +* `domainname` - (Optional, string) Domain name of the container. +* `must_run` - (Optional, bool) If true, then the Docker container will be + kept running. If false, then as long as the container exists, Terraform + assumes it is successful. +* `ports` - (Optional) See [Ports](#ports) below for details. +* `publish_all_ports` - (Optional, bool) Publish all ports of the container. +* `volumes` - (Optional) See [Volumes](#volumes) below for details. + + +## Ports + +`ports` is a block within the configuration that can be repeated to specify +the port mappings of the container. Each `ports` block supports +the following: + +* `internal` - (Required, int) Port within the container. +* `external` - (Required, int) Port exposed out of the container. +* `ip` - (Optional, string) IP address/mask that can access this port. +* `protocol` - (Optional, string) Protocol that can be used over this port, + defaults to TCP. + + +## Volumes + +`volumes` is a block within the configuration that can be repeated to specify +the volumes attached to a container. Each `volumes` block supports +the following: + +* `from_container` - (Optional, string) The container where the volume is + coming from. +* `container_path` - (Optional, string) The path in the container where the + volume will be mounted. +* `host_path` - (Optional, string) The path on the host where the volume + is coming from. +* `read_only` - (Optinal, bool) If true, this volume will be readonly. + Defaults to false. diff --git a/website/source/docs/providers/docker/r/image.html.markdown b/website/source/docs/providers/docker/r/image.html.markdown new file mode 100644 index 000000000..a2c896110 --- /dev/null +++ b/website/source/docs/providers/docker/r/image.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "docker" +page_title: "Docker: docker_image" +sidebar_current: "docs-docker-resource-image" +description: |- + Downloads and exports the ID of a Docker image. +--- + +# docker\_image + +Downloads and exports the ID of a Docker image. This can be used alongside +[docker\_container](/docs/providers/docker/r/container.html) +to programmatically get the latest image IDs without having to hardcode +them. + +## Example Usage + +``` +# Find the latest Ubuntu precise image. +resource "docker_image" "ubuntu" { + image = "ubuntu:precise" +} + +# Access it somewhere else with ${docker_image.ubuntu.latest} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Docker image, including any tags. +* `keep_updated` - (Optional) If true, then the Docker image will always + be updated on the host to the latest. If this is false, as long as an + image is downloaded with the correct tag, it won't be redownloaded if + there is a newer image. + +## Attributes Reference + +The following attributes are exported in addition to the above configuration: + +* `latest` (string) - The ID of the image. diff --git a/website/source/layouts/docker.erb b/website/source/layouts/docker.erb new file mode 100644 index 000000000..920e7aa43 --- /dev/null +++ b/website/source/layouts/docker.erb @@ -0,0 +1,30 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> +<% end %> diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index a0b31127a..5c314468c 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -130,7 +130,7 @@ > DigitalOcean - + > DNSMadeEasy @@ -140,6 +140,10 @@ DNSimple + > + Docker + + > Google Cloud From 118a5b9dfdf2e537d698df304fc6a889c0742cea Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 28 Mar 2015 19:06:48 -0700 Subject: [PATCH 011/167] providers/docker: ping docker server on startup --- builtin/providers/docker/provider.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/builtin/providers/docker/provider.go b/builtin/providers/docker/provider.go index 15bc840a4..2fe456e93 100644 --- a/builtin/providers/docker/provider.go +++ b/builtin/providers/docker/provider.go @@ -1,6 +1,8 @@ package docker import ( + "fmt" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) @@ -38,5 +40,15 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { CertPath: d.Get("cert_path").(string), } - return config.NewClient() + client, err := config.NewClient() + if err != nil { + return nil, fmt.Errorf("Error initializing Docker client: %s", err) + } + + err = client.Ping() + if err != nil { + return nil, fmt.Errorf("Error pinging Docker server: %s", err) + } + + return client, nil } From 755f8d0f4437632cc73bb574d490556875ac1a6c Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Mon, 30 Mar 2015 14:54:03 -0500 Subject: [PATCH 012/167] provider/aws: Fix acceptance test checks for AWS Security Group --- .../aws/resource_aws_security_group_test.go | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/builtin/providers/aws/resource_aws_security_group_test.go b/builtin/providers/aws/resource_aws_security_group_test.go index b1e4e8c82..067cda8a1 100644 --- a/builtin/providers/aws/resource_aws_security_group_test.go +++ b/builtin/providers/aws/resource_aws_security_group_test.go @@ -30,15 +30,15 @@ func TestAccAWSSecurityGroup_normal(t *testing.T) { resource.TestCheckResourceAttr( "aws_security_group.web", "description", "Used in the terraform acceptance tests"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.protocol", "tcp"), + "aws_security_group.web", "ingress.3629188364.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.from_port", "80"), + "aws_security_group.web", "ingress.3629188364.from_port", "80"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.to_port", "8000"), + "aws_security_group.web", "ingress.3629188364.to_port", "8000"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.cidr_blocks.#", "1"), + "aws_security_group.web", "ingress.3629188364.cidr_blocks.#", "1"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.cidr_blocks.0", "10.0.0.0/8"), + "aws_security_group.web", "ingress.3629188364.cidr_blocks.0", "10.0.0.0/8"), ), }, }, @@ -116,25 +116,25 @@ func TestAccAWSSecurityGroup_vpc(t *testing.T) { resource.TestCheckResourceAttr( "aws_security_group.web", "description", "Used in the terraform acceptance tests"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.protocol", "tcp"), + "aws_security_group.web", "ingress.3629188364.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.from_port", "80"), + "aws_security_group.web", "ingress.3629188364.from_port", "80"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.to_port", "8000"), + "aws_security_group.web", "ingress.3629188364.to_port", "8000"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.cidr_blocks.#", "1"), + "aws_security_group.web", "ingress.3629188364.cidr_blocks.#", "1"), resource.TestCheckResourceAttr( - "aws_security_group.web", "ingress.332851786.cidr_blocks.0", "10.0.0.0/8"), + "aws_security_group.web", "ingress.3629188364.cidr_blocks.0", "10.0.0.0/8"), resource.TestCheckResourceAttr( - "aws_security_group.web", "egress.332851786.protocol", "tcp"), + "aws_security_group.web", "egress.3629188364.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_security_group.web", "egress.332851786.from_port", "80"), + "aws_security_group.web", "egress.3629188364.from_port", "80"), resource.TestCheckResourceAttr( - "aws_security_group.web", "egress.332851786.to_port", "8000"), + "aws_security_group.web", "egress.3629188364.to_port", "8000"), resource.TestCheckResourceAttr( - "aws_security_group.web", "egress.332851786.cidr_blocks.#", "1"), + "aws_security_group.web", "egress.3629188364.cidr_blocks.#", "1"), resource.TestCheckResourceAttr( - "aws_security_group.web", "egress.332851786.cidr_blocks.0", "10.0.0.0/8"), + "aws_security_group.web", "egress.3629188364.cidr_blocks.0", "10.0.0.0/8"), testCheck, ), }, From a6411626bf443abe2d121d43dcaff83b3d948733 Mon Sep 17 00:00:00 2001 From: 7heo <7heo@users.noreply.github.com> Date: Tue, 31 Mar 2015 01:52:39 +0200 Subject: [PATCH 013/167] config: interprets '~' as the current user home dir in file() --- config/interpolate_funcs.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 8bb76c532..6fa9dd1cb 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/hashicorp/terraform/config/lang/ast" + "github.com/mitchellh/go-homedir" ) // Funcs is the mapping of built-in functions for configuration. @@ -57,7 +58,12 @@ func interpolationFuncFile() ast.Function { ArgTypes: []ast.Type{ast.TypeString}, ReturnType: ast.TypeString, Callback: func(args []interface{}) (interface{}, error) { - data, err := ioutil.ReadFile(args[0].(string)) + var path string + path, err := homedir.Expand(args[0].(string)) + if err != nil { + return "", err + } + data, err := ioutil.ReadFile(path) if err != nil { return "", err } From 8c1fb2a983241338e8e6ebb24d0b0edacd46d5d4 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 30 Mar 2015 19:01:55 -0500 Subject: [PATCH 014/167] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4209dc0d..2485613f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ IMPROVEMENTS: * **New config function: `split`** - Split a value based on a delimiter. This is useful for faking lists as parameters to modules. * **New resource: `digitalocean_ssh_key`** [GH-1074] + * config: Expand `~` with homedir in `file()` paths [GH-1338] * core: The serial of the state is only updated if there is an actual change. This will lower the amount of state changing on things like refresh. From 766b4902d46891c0ad723ff07c1b142a9b07b530 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 30 Mar 2015 19:03:01 -0500 Subject: [PATCH 015/167] remove extraneous var declaration just a go nitpick :) --- config/interpolate_funcs.go | 1 - 1 file changed, 1 deletion(-) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index 6fa9dd1cb..353c45500 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -58,7 +58,6 @@ func interpolationFuncFile() ast.Function { ArgTypes: []ast.Type{ast.TypeString}, ReturnType: ast.TypeString, Callback: func(args []interface{}) (interface{}, error) { - var path string path, err := homedir.Expand(args[0].(string)) if err != nil { return "", err From f9fa7480240a30d1f92c5ab4244ec87878f8612e Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 29 Oct 2014 16:52:36 -0500 Subject: [PATCH 016/167] crud for openstack servers v2 --- builtin/bins/provider-openstack/main.go | 12 + builtin/providers/openstack/config.go | 39 +++ builtin/providers/openstack/provider.go | 93 ++++++ .../resource_openstack_compute_instance.go | 297 ++++++++++++++++++ 4 files changed, 441 insertions(+) create mode 100644 builtin/bins/provider-openstack/main.go create mode 100644 builtin/providers/openstack/config.go create mode 100644 builtin/providers/openstack/provider.go create mode 100644 builtin/providers/openstack/resource_openstack_compute_instance.go diff --git a/builtin/bins/provider-openstack/main.go b/builtin/bins/provider-openstack/main.go new file mode 100644 index 000000000..f897f1c55 --- /dev/null +++ b/builtin/bins/provider-openstack/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/openstack" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: openstack.Provider, + }) +} diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go new file mode 100644 index 000000000..cb06a9848 --- /dev/null +++ b/builtin/providers/openstack/config.go @@ -0,0 +1,39 @@ +package openstack + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" +) + +type Config struct { + Region string + Username string + Password string + IdentityEndpoint string + TenantName string + + computeV2Client *gophercloud.ServiceClient +} + +func (c *Config) loadAndValidate() error { + ao := gophercloud.AuthOptions{ + Username: c.Username, + Password: c.Password, + IdentityEndpoint: c.IdentityEndpoint, + TenantName: c.TenantName, + } + + client, err := openstack.AuthenticatedClient(ao) + if err != nil { + return err + } + + c.computeV2Client, err = openstack.NewComputeV2(client, gophercloud.EndpointOpts{ + Region: c.Region, + }) + if err != nil { + return err + } + + return nil +} diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go new file mode 100644 index 000000000..a134c3d07 --- /dev/null +++ b/builtin/providers/openstack/provider.go @@ -0,0 +1,93 @@ +package openstack + +import ( + "os" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +// Provider returns a schema.Provider for OpenStack. +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: envDefaultFunc("OS_REGION"), + Description: descriptions["region"], + }, + + "auth_url": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: envDefaultFunc("OS_AUTH_URL"), + Description: descriptions["auth_url"], + }, + + "username": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: envDefaultFunc("OS_USERNAME"), + Description: descriptions["username"], + }, + + "tenant_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), + //Description: descriptions["tenantname"], + }, + + "password": &schema.Schema{ + Type: schema.TypeString, + Required: true, + DefaultFunc: envDefaultFunc("OS_PASSWORD"), + Description: descriptions["password"], + }, + }, + + ResourcesMap: map[string]*schema.Resource{ + "openstack_compute_instance": resourceComputeInstance(), + }, + + ConfigureFunc: configureProvider, + } +} + +func configureProvider(d *schema.ResourceData) (interface{}, error) { + config := Config{ + Region: d.Get("region").(string), + IdentityEndpoint: d.Get("auth_url").(string), + Username: d.Get("username").(string), + Password: d.Get("password").(string), + TenantName: d.Get("tenant_name").(string), + } + + if err := config.loadAndValidate(); err != nil { + return nil, err + } + + return &config, nil +} + +func envDefaultFunc(k string) schema.SchemaDefaultFunc { + return func() (interface{}, error) { + if v := os.Getenv(k); v != "" { + return v, nil + } + + return nil, nil + } +} + +var descriptions map[string]string + +func init() { + descriptions = map[string]string{ + "region": "The region where OpenStack operations will take place.", + "auth_url": "The endpoint against which to authenticate.", + "username": "The username with which to authenticate.", + "password": "The password with which to authenticate.", + } +} diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go new file mode 100644 index 000000000..07bf040e9 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -0,0 +1,297 @@ +package openstack + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/compute/v2/servers" +) + +func resourceComputeInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeInstanceCreate, + Read: resourceComputeInstanceRead, + Update: resourceComputeInstanceUpdate, + Delete: resourceComputeInstanceDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "image_ref": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "flavor_ref": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "security_groups": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + + "availability_zone": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "networks": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "fixed_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + + "metadata": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + }, + + "config_drive": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "access_ip_v4": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: false, + }, + + "access_ip_v6": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: false, + }, + }, + } +} + +func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + createOpts := &servers.CreateOpts{ + Name: d.Get("name").(string), + ImageRef: d.Get("image_ref").(string), + FlavorRef: d.Get("flavor_ref").(string), + //SecurityGroups []string + AvailabilityZone: d.Get("availability_zone").(string), + Networks: resourceInstanceNetworks(d), + Metadata: resourceInstanceMetadata(d), + ConfigDrive: d.Get("config_drive").(bool), + } + + log.Printf("[INFO] Requesting instance creation") + server, err := servers.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack server: %s", err) + } + log.Printf("[INFO] Instance ID: %s", server.ID) + + // Store the ID now + d.SetId(server.ID) + + // Wait for the instance to become running so we can get some attributes + // that aren't available until later. + log.Printf( + "[DEBUG] Waiting for instance (%s) to become running", + server.ID) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"BUILD"}, + Target: "ACTIVE", + Refresh: ServerStateRefreshFunc(osClient, server.ID), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + serverRaw, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for instance (%s) to become ready: %s", + server.ID, err) + } + + server = serverRaw.(*servers.Server) + + return resourceComputeInstanceRead(d, meta) +} + +func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + server, err := servers.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack server: %s", err) + } + + log.Printf("[DEBUG] Retreived Server %s: %+v", d.Id(), server) + + d.Set("name", server.Name) + d.Set("access_ip_v4", server.AccessIPv4) + d.Set("access_ip_v6", server.AccessIPv6) + + host := server.AccessIPv4 + if host == "" { + if publicAddressesRaw, ok := server.Addresses["public"]; ok { + publicAddresses := publicAddressesRaw.([]interface{}) + for _, paRaw := range publicAddresses { + pa := paRaw.(map[string]interface{}) + if pa["version"].(float64) == 4 { + host = pa["addr"].(string) + } + } + } + } + + log.Printf("host: %s", host) + + // Initialize the connection info + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": host, + }) + + d.Set("metadata", server.Metadata) + + return nil +} + +func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + var updateOpts servers.UpdateOpts + // If the Metadata has changed, then update that. + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + if d.HasChange("access_ip_v4") { + updateOpts.AccessIPv4 = d.Get("access_ip_v4").(string) + } + if d.HasChange("access_ip_v6") { + updateOpts.AccessIPv4 = d.Get("access_ip_v6").(string) + } + + // If there's nothing to update, don't waste an HTTP call. + if updateOpts != (servers.UpdateOpts{}) { + log.Printf("[DEBUG] Updating Server %s with options: %+v", d.Id(), updateOpts) + + _, err := servers.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating Openstack server: %s", err) + } + } + + return resourceComputeInstanceRead(d, meta) +} + +func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + err := servers.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack server: %s", err) + } + + // Wait for the instance to delete before moving on. + log.Printf( + "[DEBUG] Waiting for instance (%s) to delete", + d.Id()) + + stateConf := &resource.StateChangeConf{ + Target: "", + Refresh: ServerStateRefreshFunc(osClient, d.Id()), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for instance (%s) to delete: %s", + d.Id(), err) + } + + d.SetId("") + return nil +} + +// ServerStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch +// an OpenStack instance. +func ServerStateRefreshFunc(client *gophercloud.ServiceClient, instanceID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + s, err := servers.Get(client, instanceID).Extract() + if err != nil { + return nil, "", err + } + + return s, s.Status, nil + } +} + +func resourceInstanceNetworks(d *schema.ResourceData) []servers.Network { + rawNetworks := d.Get("networks").([]interface{}) + networks := make([]servers.Network, len(rawNetworks)) + for i, raw := range rawNetworks { + rawMap := raw.(map[string]interface{}) + networks[i] = servers.Network{ + UUID: rawMap["uuid"].(string), + Port: rawMap["port"].(string), + FixedIP: rawMap["fixed_ip"].(string), + } + } + return networks +} + +func resourceInstanceMetadata(d *schema.ResourceData) map[string]string { + m := make(map[string]string) + for key, val := range d.Get("metadata").(map[string]interface{}) { + m[key] = val.(string) + } + return m +} From cc9ee787ac8402ac0f44f1fbfc8d20ccd9878999 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Fri, 2 Jan 2015 12:40:42 -0700 Subject: [PATCH 017/167] update openstack server metadata --- .../resource_openstack_compute_instance.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index 07bf040e9..e4d1407d7 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -221,7 +221,21 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err _, err := servers.Update(osClient, d.Id(), updateOpts).Extract() if err != nil { - return fmt.Errorf("Error updating Openstack server: %s", err) + return fmt.Errorf("Error updating OpenStack server: %s", err) + } + } + + if d.HasChange("metadata") { + var metadataOpts servers.MetadataOpts + metadataOpts = make(servers.MetadataOpts) + newMetadata := d.Get("metadata").(map[string]interface{}) + for k, v := range newMetadata { + metadataOpts[k] = v.(string) + } + + _, err := servers.UpdateMetadata(osClient, d.Id(), metadataOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack server (%s) metadata: %s", d.Id(), err) } } From 3112103acf15c392565838496a187c2499b3be28 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 4 Jan 2015 09:52:49 -0700 Subject: [PATCH 018/167] server keypair --- .../resource_openstack_compute_instance.go | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index e4d1407d7..54dbe1b67 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) @@ -81,6 +82,7 @@ func resourceComputeInstance() *schema.Resource { "metadata": &schema.Schema{ Type: schema.TypeMap, Optional: true, + ForceNew: false, }, "config_drive": &schema.Schema{ @@ -102,6 +104,12 @@ func resourceComputeInstance() *schema.Resource { Optional: true, ForceNew: false, }, + + "key_pair": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, }, } } @@ -110,7 +118,9 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err config := meta.(*Config) osClient := config.computeV2Client - createOpts := &servers.CreateOpts{ + var createOpts servers.CreateOptsBuilder + + serverCreateOpts := &servers.CreateOpts{ Name: d.Get("name").(string), ImageRef: d.Get("image_ref").(string), FlavorRef: d.Get("flavor_ref").(string), @@ -121,6 +131,15 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err ConfigDrive: d.Get("config_drive").(bool), } + if kp, ok := d.Get("key_pair").(map[string]interface{}); ok && kp != nil { + if keyName, ok := kp["name"].(string); ok && keyName != "" { + createOpts = &keypairs.CreateOptsExt{ + serverCreateOpts, + keyName, + } + } + } + log.Printf("[INFO] Requesting instance creation") server, err := servers.Create(osClient, createOpts).Extract() if err != nil { From 48e92f8173063cb8214eef4a3175c547ad1965a1 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 4 Jan 2015 15:26:57 -0700 Subject: [PATCH 019/167] OS_REGION -> OS_REGION_NAME (thank you @hartzell) --- builtin/providers/openstack/provider.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index a134c3d07..356081d19 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -14,7 +14,7 @@ func Provider() terraform.ResourceProvider { "region": &schema.Schema{ Type: schema.TypeString, Required: true, - DefaultFunc: envDefaultFunc("OS_REGION"), + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), Description: descriptions["region"], }, @@ -61,7 +61,7 @@ func configureProvider(d *schema.ResourceData) (interface{}, error) { IdentityEndpoint: d.Get("auth_url").(string), Username: d.Get("username").(string), Password: d.Get("password").(string), - TenantName: d.Get("tenant_name").(string), + TenantName: d.Get("tenant_name").(string), } if err := config.loadAndValidate(); err != nil { From 0bb0dad58ce628b3030ea07c8ed3cfd3fdec2c4f Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 4 Jan 2015 15:27:54 -0700 Subject: [PATCH 020/167] provider test --- builtin/providers/openstack/provider_test.go | 51 ++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 builtin/providers/openstack/provider_test.go diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go new file mode 100644 index 000000000..2819a4575 --- /dev/null +++ b/builtin/providers/openstack/provider_test.go @@ -0,0 +1,51 @@ +package openstack + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +var testAccProviders map[string]terraform.ResourceProvider +var testAccProvider *schema.Provider + +func init() { + testAccProvider = Provider().(*schema.Provider) + testAccProviders = map[string]terraform.ResourceProvider{ + "openstack": testAccProvider, + } +} + +func TestProvider(t *testing.T) { + if err := Provider().(*schema.Provider).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = Provider() +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv("OS_REGION_NAME"); v == "" { + t.Fatal("OS_REGION_NAME must be set for acceptance tests") + } + + if v := os.Getenv("OS_AUTH_URL"); v == "" { + t.Fatal("OS_AUTH_URL must be set for acceptance tests") + } + + if v := os.Getenv("OS_USERNAME"); v == "" { + t.Fatal("OS_USERNAME must be set for acceptance tests") + } + + if v := os.Getenv("OS_TENANT_NAME"); v != "us-central1" { + t.Fatal("OS_TENANT_NAME must be set to us-central1 for acceptance tests") + } + + if v := os.Getenv("OS_PASSWORD"); v != "us-central1" { + t.Fatal("OS_PASSWORD must be set to us-central1 for acceptance tests") + } +} From f17649e9dc1a60a357c9e22ae9bc2c0617e73a20 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 5 Jan 2015 10:05:25 -0700 Subject: [PATCH 021/167] server resizing --- .../resource_openstack_compute_instance.go | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index 54dbe1b67..9075a08ae 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -214,6 +214,11 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error }) d.Set("metadata", server.Metadata) + newFlavor, ok := server.Flavor["id"].(string) + if !ok { + return fmt.Errorf("Error setting OpenStack server's flavor: %v", newFlavor) + } + d.Set("flavor_ref", newFlavor) return nil } @@ -258,6 +263,54 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } + if d.HasChange("flavor_ref") { + resizeOpts := &servers.ResizeOpts{ + FlavorRef: d.Get("flavor_ref").(string), + } + err := servers.Resize(osClient, d.Id(), resizeOpts).ExtractErr() + if err != nil { + return fmt.Errorf("Error resizing OpenStack server: %s", err) + } + + // Wait for the instance to finish resizing. + log.Printf("[DEBUG] Waiting for instance (%s) to finish resizing", d.Id()) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"RESIZE"}, + Target: "VERIFY_RESIZE", + Refresh: ServerStateRefreshFunc(osClient, d.Id()), + Timeout: 3 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for instance (%s) to resize: %s", d.Id(), err) + } + + // Confirm resize. + log.Printf("[DEBUG] Confirming resize") + err = servers.ConfirmResize(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error confirming resize of OpenStack server: %s", err) + } + + stateConf = &resource.StateChangeConf{ + Pending: []string{"VERIFY_RESIZE"}, + Target: "ACTIVE", + Refresh: ServerStateRefreshFunc(osClient, d.Id()), + Timeout: 3 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for instance (%s) to confirm resize: %s", d.Id(), err) + } + } + return resourceComputeInstanceRead(d, meta) } @@ -271,9 +324,7 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err } // Wait for the instance to delete before moving on. - log.Printf( - "[DEBUG] Waiting for instance (%s) to delete", - d.Id()) + log.Printf("[DEBUG] Waiting for instance (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ Target: "", From 04a9d47bcab1bd9fb6a1bf7d951deafdc3f06c5c Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 5 Jan 2015 12:16:33 -0700 Subject: [PATCH 022/167] add/update/remove sec groups from server --- .../resource_openstack_compute_instance.go | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index 9075a08ae..57afbb309 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -10,7 +10,9 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" + "github.com/rackspace/gophercloud/pagination" ) func resourceComputeInstance() *schema.Resource { @@ -121,10 +123,10 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err var createOpts servers.CreateOptsBuilder serverCreateOpts := &servers.CreateOpts{ - Name: d.Get("name").(string), - ImageRef: d.Get("image_ref").(string), - FlavorRef: d.Get("flavor_ref").(string), - //SecurityGroups []string + Name: d.Get("name").(string), + ImageRef: d.Get("image_ref").(string), + FlavorRef: d.Get("flavor_ref").(string), + SecurityGroups: resourceInstanceSecGroups(d), AvailabilityZone: d.Get("availability_zone").(string), Networks: resourceInstanceNetworks(d), Metadata: resourceInstanceMetadata(d), @@ -214,6 +216,22 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error }) d.Set("metadata", server.Metadata) + + var currentSG []string + err = secgroups.ListByServer(osClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { + secGrpList, err := secgroups.ExtractSecurityGroups(page) + if err != nil { + return false, fmt.Errorf("Error setting security groups for OpenStack server: %s", err) + } + + for _, sg := range secGrpList { + currentSG = append(currentSG, sg.Name) + } + + return true, nil + }) + d.Set("security_groups", currentSG) + newFlavor, ok := server.Flavor["id"].(string) if !ok { return fmt.Errorf("Error setting OpenStack server's flavor: %v", newFlavor) @@ -263,6 +281,33 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } + if d.HasChange("security_groups") { + oldSGRaw, newSGRaw := d.GetChange("security_groups") + oldSGSet, newSGSet := oldSGRaw.(*schema.Set), newSGRaw.(*schema.Set) + secgroupsToAdd := newSGSet.Difference(oldSGSet) + secgroupsToRemove := oldSGSet.Difference(newSGSet) + + log.Printf("[DEBUG] Security groups to add: %v", secgroupsToAdd) + + log.Printf("[DEBUG] Security groups to remove: %v", secgroupsToRemove) + + for _, g := range secgroupsToAdd.List() { + err := secgroups.AddServerToGroup(osClient, d.Id(), g.(string)).ExtractErr() + if err != nil { + return fmt.Errorf("Error adding security group to OpenStack server (%s): %s", d.Id(), err) + } + log.Printf("[DEBUG] Added security group (%s) to instance (%s)", g.(string), d.Id()) + } + + for _, g := range secgroupsToRemove.List() { + err := secgroups.RemoveServerFromGroup(osClient, d.Id(), g.(string)).ExtractErr() + if err != nil { + return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) + } + log.Printf("[DEBUG] Removed security group (%s) from instance (%s)", g.(string), d.Id()) + } + } + if d.HasChange("flavor_ref") { resizeOpts := &servers.ResizeOpts{ FlavorRef: d.Get("flavor_ref").(string), @@ -358,6 +403,15 @@ func ServerStateRefreshFunc(client *gophercloud.ServiceClient, instanceID string } } +func resourceInstanceSecGroups(d *schema.ResourceData) []string { + rawSecGroups := d.Get("security_groups").(*schema.Set) + secgroups := make([]string, rawSecGroups.Len()) + for i, raw := range rawSecGroups.List() { + secgroups[i] = raw.(string) + } + return secgroups +} + func resourceInstanceNetworks(d *schema.ResourceData) []servers.Network { rawNetworks := d.Get("networks").([]interface{}) networks := make([]servers.Network, len(rawNetworks)) From edc280a8dc6ef702a34880502ec41d5852a4bab4 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 7 Jan 2015 11:38:23 -0700 Subject: [PATCH 023/167] add/update admin pass on server; change key pair format from map to string --- .../resource_openstack_compute_instance.go | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index 57afbb309..a57fda13b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -93,6 +93,12 @@ func resourceComputeInstance() *schema.Resource { ForceNew: true, }, + "admin_pass": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "access_ip_v4": &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -108,7 +114,7 @@ func resourceComputeInstance() *schema.Resource { }, "key_pair": &schema.Schema{ - Type: schema.TypeMap, + Type: schema.TypeString, Optional: true, ForceNew: true, }, @@ -131,14 +137,13 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err Networks: resourceInstanceNetworks(d), Metadata: resourceInstanceMetadata(d), ConfigDrive: d.Get("config_drive").(bool), + AdminPass: d.Get("admin_pass").(string), } - if kp, ok := d.Get("key_pair").(map[string]interface{}); ok && kp != nil { - if keyName, ok := kp["name"].(string); ok && keyName != "" { - createOpts = &keypairs.CreateOptsExt{ - serverCreateOpts, - keyName, - } + if keyName, ok := d.Get("key_pair").(string); ok && keyName != "" { + createOpts = &keypairs.CreateOptsExt{ + serverCreateOpts, + keyName, } } @@ -308,6 +313,15 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } + if d.HasChange("admin_pass") { + if newPwd, ok := d.Get("admin_pass").(string); ok { + err := servers.ChangeAdminPassword(osClient, d.Id(), newPwd).ExtractErr() + if err != nil { + return fmt.Errorf("Error changing admin password of OpenStack server (%s): %s", d.Id(), err) + } + } + } + if d.HasChange("flavor_ref") { resizeOpts := &servers.ResizeOpts{ FlavorRef: d.Get("flavor_ref").(string), From 686cd4b02b683575d6609609e14c56bb7b5dce31 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 7 Jan 2015 12:19:30 -0700 Subject: [PATCH 024/167] add OpenStack to list of IaaS providers --- website/source/docs/providers/index.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/index.html.markdown b/website/source/docs/providers/index.html.markdown index f03c17c54..5365d0e86 100644 --- a/website/source/docs/providers/index.html.markdown +++ b/website/source/docs/providers/index.html.markdown @@ -14,7 +14,7 @@ etc. Almost any infrastructure noun can be represented as a resource in Terrafor Terraform is agnostic to the underlying platforms by supporting providers. A provider is responsible for understanding API interactions and exposing resources. Providers -generally are an IaaS (e.g. AWS, DigitalOcean, GCE), PaaS (e.g. Heroku, CloudFoundry), +generally are an IaaS (e.g. AWS, DigitalOcean, GCE, OpenStack), PaaS (e.g. Heroku, CloudFoundry), or SaaS services (e.g. Atlas, DNSimple, CloudFlare). Use the navigation to the left to read about the available providers. From 47955b1d446e70e27b72f77f4dffe0dc15c40796 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 7 Jan 2015 16:45:06 -0700 Subject: [PATCH 025/167] remove unneeded variables during server creation --- .../openstack/resource_openstack_compute_instance.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index a57fda13b..7ba4199fc 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -172,15 +172,13 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err MinTimeout: 3 * time.Second, } - serverRaw, err := stateConf.WaitForState() + _, err = stateConf.WaitForState() if err != nil { return fmt.Errorf( "Error waiting for instance (%s) to become ready: %s", server.ID, err) } - server = serverRaw.(*servers.Server) - return resourceComputeInstanceRead(d, meta) } From 01e41646d3796a04d35bba8312505b357938c33b Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 7 Jan 2015 16:45:57 -0700 Subject: [PATCH 026/167] add/get/delete keypairs --- builtin/providers/openstack/provider.go | 1 + .../resource_openstack_compute_keypair.go | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_compute_keypair.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 356081d19..2a5aad71b 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -49,6 +49,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "openstack_compute_instance": resourceComputeInstance(), + "openstack_compute_keypair": resourceComputeKeypair(), }, ConfigureFunc: configureProvider, diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair.go b/builtin/providers/openstack/resource_openstack_compute_keypair.go new file mode 100644 index 000000000..a8ca16e08 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_keypair.go @@ -0,0 +1,76 @@ +package openstack + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" +) + +func resourceComputeKeypair() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeKeypairCreate, + Read: resourceComputeKeypairRead, + Delete: resourceComputeKeypairDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "public_key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceComputeKeypairCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + createOpts := keypairs.CreateOpts{ + Name: d.Get("name").(string), + PublicKey: d.Get("public_key").(string), + } + + kp, err := keypairs.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack keypair: %s", err) + } + + d.SetId(kp.Name) + + return resourceComputeKeypairRead(d, meta) +} + +func resourceComputeKeypairRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + kp, err := keypairs.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack keypair: %s", err) + } + + d.Set("name", kp.Name) + d.Set("public_key", kp.PublicKey) + + return nil +} + +func resourceComputeKeypairDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + err := keypairs.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack keypair: %s", err) + } + d.SetId("") + return nil +} From 00ee96fb6fe4bd7289dd7f052ffbfee810ea49c2 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 7 Jan 2015 17:09:25 -0700 Subject: [PATCH 027/167] openstack servers and keypairs docs --- .../providers/openstack/index.html.markdown | 47 +++++++++++ .../r/compute_instance.html.markdown | 80 +++++++++++++++++++ .../openstack/r/compute_keypair.html.markdown | 37 +++++++++ website/source/layouts/openstack.erb | 29 +++++++ 4 files changed, 193 insertions(+) create mode 100644 website/source/docs/providers/openstack/index.html.markdown create mode 100644 website/source/docs/providers/openstack/r/compute_instance.html.markdown create mode 100644 website/source/docs/providers/openstack/r/compute_keypair.html.markdown create mode 100644 website/source/layouts/openstack.erb diff --git a/website/source/docs/providers/openstack/index.html.markdown b/website/source/docs/providers/openstack/index.html.markdown new file mode 100644 index 000000000..652523c5b --- /dev/null +++ b/website/source/docs/providers/openstack/index.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "openstack" +page_title: "Provider: OpenStack" +sidebar_current: "docs-openstack-index" +description: |- + The OpenStack provider is used to interact with the many resources supported by OpenStack. The provider needs to be configured with the proper credentials before it can be used. +--- + +# OpenStack Provider + +The OpenStack provider is used to interact with the +many resources supported by OpenStack. The provider needs to be configured +with the proper credentials before it can be used. + +Use the navigation to the left to read about the available resources. + +## Example Usage + +``` +# Configure the OpenStack Provider +provider "openstack" { + username = "admin" + tenant_name = "admin" + password = "pwd" + auth_url = "http://myauthurl:5000/v2.0" + region = "RegionOne" +} + +# Create a web server +resource "openstack_compute_instance" "test-server" { + ... +} +``` + +## Configuration Reference + +The following arguments are supported: + +* `username` - (Required) + +* `tenant_name` - (Required) + +* `password` - (Required) + +* `auth_url` - (Required) + +* `region` - (Required) diff --git a/website/source/docs/providers/openstack/r/compute_instance.html.markdown b/website/source/docs/providers/openstack/r/compute_instance.html.markdown new file mode 100644 index 000000000..f22624bf9 --- /dev/null +++ b/website/source/docs/providers/openstack/r/compute_instance.html.markdown @@ -0,0 +1,80 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_compute_instance" +sidebar_current: "docs-openstack-resource-compute-instance" +description: |- + Manages a VM instance resource within OpenStack. +--- + +# openstack\_compute\_instance + +Manages a VM instance resource within OpenStack. + +## Example Usage + +``` +resource "openstack_compute_instance" "test-server" { + name = "tf-test" + image_ref = "ad091b52-742f-469e-8f3c-fd81cadf0743" + flavor_ref = "3" + metadata { + this = "that" + } + key_pair = "my_key_pair_name" + security_groups = ["test-group-1"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A unique name for the resource. + +* `image_ref` - (Required) The image reference (ID) for the desired image for + the server. Changing this creates a new server. + +* `flavor_ref` - (Required) The flavor reference (ID) for the desired flavor + for the server. Changing this resizes the existing server. + +* `security_groups` - (Optional) An array of one or more security group names + to associate with the server. Changing this results in adding/removing + security groups from the existing server. + +* `availability_zone` - (Optional) The availability zone in which to create + the server. Changing this creates a new server. + +* `networks` - (Optional) An array of one or more networks to attach to the + instance. The network object structure is documented below. + +* `metadata` - (Optional) Metadata key/value pairs to make available from + within the instance. Changing this updates the existing server metadata. + +* `admin_pass` - (Optional) The administrative password to assign to the server. + Changing this changes the root password on the existing server. + +* `key_pair` - (Optional) The name of a key pair to put on the server. The key + pair must already be created and associated with the tenant's account. + Changing this creates a new server. + +The `network` block supports: + +* `uuid` - (Required unless `port` is provided) The network UUID to attach to + the server. + +* `port` - (Required unless `uuid` is provided) The port UUID of a network to + attach to the server. + +* `fixed_ip` - (Optional) Specifies a fixed IP address to be used on this + network. + +## Attributes Reference + +The following attributes are exported: + +* `name` - See Argument Reference above. +* `access_ip_v4` - See Argument Reference above. +* `access_ip_v6` - See Argument Reference above. +* `metadata` - See Argument Reference above. +* `security_groups` - See Argument Reference above. +* `flavor_ref` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/compute_keypair.html.markdown b/website/source/docs/providers/openstack/r/compute_keypair.html.markdown new file mode 100644 index 000000000..15459a0ec --- /dev/null +++ b/website/source/docs/providers/openstack/r/compute_keypair.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_compute_keypair" +sidebar_current: "docs-openstack-resource-compute-keypair" +description: |- + Manages a keypair resource within OpenStack. +--- + +# openstack\_compute\_keypair + +Manages a keypair resource within OpenStack. + +## Example Usage + +``` +resource "openstack_compute_keypair" "test-keypair" { + name = "my-keypair" + public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAjpC1hwiOCCmKEWxJ4qzTTsJbKzndLotBCz5PcwtUnflmU+gHJtWMZKpuEGVi29h0A/+ydKek1O18k10Ff+4tyFjiHDQAnOfgWf7+b1yK+qDip3X1C0UPMbwHlTfSGWLGZqd9LvEFx9k3h/M+VtMvwR1lJ9LUyTAImnNjWG7TaIPmui30HvM2UiFEmqkr4ijq45MyX2+fLIePLRIF61p4whjHAQYufqyno3BS48icQb4p6iVEZPo4AE2o9oIyQvj2mx4dk5Y8CgSETOZTYDOR3rU2fZTRDRgPJDH9FWvQjF5tA0p3d9CoWWd2s6GKKbfoUIi8R/Db1BSPJwkqB" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A unique name for the keypair. Changing this creates a new + keypair. + +* `public_key` - (Required) A pregenerated OpenSSH-formatted public key. + Changing this creates a new keypair. + +## Attributes Reference + +The following attributes are exported: + +* `name` - See Argument Reference above. +* `public_key` - See Argument Reference above. diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb new file mode 100644 index 000000000..7547a3b09 --- /dev/null +++ b/website/source/layouts/openstack.erb @@ -0,0 +1,29 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + + <%= yield %> + <% end %> From 23d425072ca0f9759e423a9f5b486848430a746d Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 8 Jan 2015 10:47:10 -0700 Subject: [PATCH 028/167] add/delete security groups and rules --- builtin/providers/openstack/provider.go | 6 +- .../openstack/resource_compute_secgroup.go | 103 ++++++++++++++++++ .../resource_compute_secgrouprule.go | 99 +++++++++++++++++ 3 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 builtin/providers/openstack/resource_compute_secgroup.go create mode 100644 builtin/providers/openstack/resource_compute_secgrouprule.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 2a5aad71b..b9bb5d29b 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -48,8 +48,10 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "openstack_compute_instance": resourceComputeInstance(), - "openstack_compute_keypair": resourceComputeKeypair(), + "openstack_compute_instance": resourceComputeInstance(), + "openstack_compute_keypair": resourceComputeKeypair(), + "openstack_compute_secgroup": resourceComputeSecGroup(), + "openstack_compute_secgrouprule": resourceComputeSecGroupRule(), }, ConfigureFunc: configureProvider, diff --git a/builtin/providers/openstack/resource_compute_secgroup.go b/builtin/providers/openstack/resource_compute_secgroup.go new file mode 100644 index 000000000..f6471eb81 --- /dev/null +++ b/builtin/providers/openstack/resource_compute_secgroup.go @@ -0,0 +1,103 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" +) + +func resourceComputeSecGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeSecGroupCreate, + Read: resourceComputeSecGroupRead, + Update: resourceComputeSecGroupUpdate, + Delete: resourceComputeSecGroupDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceComputeSecGroupCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + createOpts := secgroups.CreateOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + } + + sg, err := secgroups.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack security group: %s", err) + } + + d.SetId(sg.ID) + + return resourceComputeSecGroupRead(d, meta) +} + +func resourceComputeSecGroupRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + sg, err := secgroups.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack security group: %s", err) + } + + d.Set("name", sg.Name) + d.Set("description", sg.Description) + + return nil +} + +func resourceComputeSecGroupUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + var updateOpts secgroups.UpdateOpts + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + if d.HasChange("description") { + updateOpts.Description = d.Get("description").(string) + } + + // If there's nothing to update, don't waste an HTTP call. + if updateOpts != (secgroups.UpdateOpts{}) { + log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts) + + _, err := secgroups.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err) + } + } + + return resourceComputeSecGroupRead(d, meta) +} + +func resourceComputeSecGroupDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + err := secgroups.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack security group: %s", err) + } + d.SetId("") + return nil +} diff --git a/builtin/providers/openstack/resource_compute_secgrouprule.go b/builtin/providers/openstack/resource_compute_secgrouprule.go new file mode 100644 index 000000000..1063e2df0 --- /dev/null +++ b/builtin/providers/openstack/resource_compute_secgrouprule.go @@ -0,0 +1,99 @@ +package openstack + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" +) + +func resourceComputeSecGroupRule() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeSecGroupRuleCreate, + Read: resourceComputeSecGroupRuleRead, + Delete: resourceComputeSecGroupRuleDelete, + + Schema: map[string]*schema.Schema{ + "group_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "from_port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "to_port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "ip_protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "cidr": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "from_group_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceComputeSecGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + createOpts := secgroups.CreateRuleOpts{ + ParentGroupID: d.Get("group_id").(string), + FromPort: d.Get("from_port").(int), + ToPort: d.Get("to_port").(int), + IPProtocol: d.Get("ip_protocol").(string), + CIDR: d.Get("cidr").(string), + FromGroupID: d.Get("from_group_id").(string), + } + + sgr, err := secgroups.CreateRule(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack security group rule: %s", err) + } + + d.SetId(sgr.ID) + d.Set("group_id", sgr.ParentGroupID) + d.Set("from_port", sgr.FromPort) + d.Set("to_port", sgr.ToPort) + d.Set("ip_protocol", sgr.IPProtocol) + d.Set("cidr", sgr.IPRange.CIDR) + d.Set("from_group_id", d.Get("from_group_id").(string)) + + return resourceComputeSecGroupRuleRead(d, meta) +} + +func resourceComputeSecGroupRuleRead(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceComputeSecGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.computeV2Client + + err := secgroups.DeleteRule(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack security group rule: %s", err) + } + d.SetId("") + return nil +} From d7560de2ddf46b6d587284718a7e7fb934770865 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 8 Jan 2015 10:48:00 -0700 Subject: [PATCH 029/167] remove errant comment --- .../providers/openstack/resource_openstack_compute_instance.go | 1 - 1 file changed, 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index 7ba4199fc..e8aa71848 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -249,7 +249,6 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err osClient := config.computeV2Client var updateOpts servers.UpdateOpts - // If the Metadata has changed, then update that. if d.HasChange("name") { updateOpts.Name = d.Get("name").(string) } From fc344e90604a2639b31415c963a8c73eed171d59 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 8 Jan 2015 14:11:58 -0700 Subject: [PATCH 030/167] sec group and sec group rule docs --- .../r/compute_secgroup.html.markdown | 37 +++++++++++ .../r/compute_secgrouprule.html.markdown | 63 +++++++++++++++++++ website/source/layouts/openstack.erb | 6 ++ 3 files changed, 106 insertions(+) create mode 100644 website/source/docs/providers/openstack/r/compute_secgroup.html.markdown create mode 100644 website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown diff --git a/website/source/docs/providers/openstack/r/compute_secgroup.html.markdown b/website/source/docs/providers/openstack/r/compute_secgroup.html.markdown new file mode 100644 index 000000000..27045b62b --- /dev/null +++ b/website/source/docs/providers/openstack/r/compute_secgroup.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_compute_secgroup" +sidebar_current: "docs-openstack-resource-compute-secgroup" +description: |- + Manages a security group resource within OpenStack. +--- + +# openstack\_compute\_secgroup + +Manages a security group resource within OpenStack. + +## Example Usage + +``` +resource "openstack_compute_secgroup" "secgroup_1" { + name = "my_secgroup" + description = "my security group" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) A unique name for the security group. Changing this + updates the `name` of an existing security group. + +* `description` - (Required) A description for the security group. Changing this + updates the `description` of an existing security group. + +## Attributes Reference + +The following attributes are exported: + +* `name` - See Argument Reference above. +* `description` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown b/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown new file mode 100644 index 000000000..95b92981c --- /dev/null +++ b/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_compute_secgrouprule" +sidebar_current: "docs-openstack-resource-compute-secgrouprule" +description: |- + Manages a security group rule resource within OpenStack. +--- + +# openstack\_compute\_secgrouprule + +Manages a security group rule resource within OpenStack. + +## Example Usage + +``` +resource "openstack_compute_secgroup" "secgroup_1" { + name = "my_secgroup" + description = "my security group" +} + +resource "openstack_compute_secgrouprule" "secgrouprule_1" { + group_id = "${openstack_compute_secgroup.secgroup_1.id}" + from_port = 22 + to_port = 22 + ip_protocol = "TCP" + cidr = "0.0.0.0/0" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `group_id` - (Required) The ID of the group to which this rule will be added. + Changing this creates a new security group rule. + +* `from_port` - (Required) An integer representing the lower bound of the port + range to open. Changing this creates a new security group rule. + +* `to_port` - (Required) An integer representing the upper bound of the port + range to open. Changing this creates a new security group rule. + +* `ip_protocol` - (Required) The protocol type that will be allowed. Changing + this creates a new security group rule. + +* `cidr` - (Optional) Required is `from_group_id` is empty. The IP range that + will be the source of network traffic to the security group. Use 0.0.0.0./0 + to allow all IP addresses. Changing this creates a new security group rule. + +* `from_group_id - (Optional) Required is `cidr` is empty. The ID of a group + from which to forward traffic to the parent group. Changing + this creates a new security group rule. + +## Attributes Reference + +The following attributes are exported: + +* `group_id` - See Argument Reference above. +* `from_port` - See Argument Reference above. +* `to_port` - See Argument Reference above. +* `ip_protocol` - See Argument Reference above. +* `cidr` - See Argument Reference above. +* `from_group_id` - See Argument Reference above. diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb index 7547a3b09..69bc4ba2e 100644 --- a/website/source/layouts/openstack.erb +++ b/website/source/layouts/openstack.erb @@ -19,6 +19,12 @@ > openstack_compute_keypair + > + openstack_compute_keypair + + > + openstack_compute_keypair + From 457bbe661a92c0eee531a659979944373e03ff48 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 10 Jan 2015 14:58:41 -0700 Subject: [PATCH 031/167] resource_compute_secgroup -> resource_openstack_compute_secgroup --- ...compute_secgroup.go => resource_openstack_compute_secgroup.go} | 0 ...secgrouprule.go => resource_openstack_compute_secgrouprule.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename builtin/providers/openstack/{resource_compute_secgroup.go => resource_openstack_compute_secgroup.go} (100%) rename builtin/providers/openstack/{resource_compute_secgrouprule.go => resource_openstack_compute_secgrouprule.go} (100%) diff --git a/builtin/providers/openstack/resource_compute_secgroup.go b/builtin/providers/openstack/resource_openstack_compute_secgroup.go similarity index 100% rename from builtin/providers/openstack/resource_compute_secgroup.go rename to builtin/providers/openstack/resource_openstack_compute_secgroup.go diff --git a/builtin/providers/openstack/resource_compute_secgrouprule.go b/builtin/providers/openstack/resource_openstack_compute_secgrouprule.go similarity index 100% rename from builtin/providers/openstack/resource_compute_secgrouprule.go rename to builtin/providers/openstack/resource_openstack_compute_secgrouprule.go From 45c4868f902afe7ff2623ff0804d28bce39b3417 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 10 Jan 2015 14:59:59 -0700 Subject: [PATCH 032/167] fix comment --- .../providers/openstack/resource_openstack_compute_instance.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index e8aa71848..62d14b636 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -237,7 +237,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error newFlavor, ok := server.Flavor["id"].(string) if !ok { - return fmt.Errorf("Error setting OpenStack server's flavor: %v", newFlavor) + return fmt.Errorf("Error setting OpenStack server's flavor: %v", server.Flavor) } d.Set("flavor_ref", newFlavor) From 476107579034ebc5f1befbbb4044f4f472b16b21 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 10 Jan 2015 15:02:19 -0700 Subject: [PATCH 033/167] crud for neutron networks --- builtin/providers/openstack/config.go | 18 +- builtin/providers/openstack/provider.go | 2 + .../resource_openstack_networking_network.go | 188 ++++++++++++++++++ 3 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 builtin/providers/openstack/resource_openstack_networking_network.go diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index cb06a9848..d877fcd80 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -10,17 +10,18 @@ type Config struct { Username string Password string IdentityEndpoint string - TenantName string + TenantName string - computeV2Client *gophercloud.ServiceClient + computeV2Client *gophercloud.ServiceClient + networkingV2Client *gophercloud.ServiceClient } func (c *Config) loadAndValidate() error { ao := gophercloud.AuthOptions{ - Username: c.Username, - Password: c.Password, + Username: c.Username, + Password: c.Password, IdentityEndpoint: c.IdentityEndpoint, - TenantName: c.TenantName, + TenantName: c.TenantName, } client, err := openstack.AuthenticatedClient(ao) @@ -31,9 +32,10 @@ func (c *Config) loadAndValidate() error { c.computeV2Client, err = openstack.NewComputeV2(client, gophercloud.EndpointOpts{ Region: c.Region, }) - if err != nil { - return err - } + + c.networkingV2Client, err = openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ + Region: c.Region, + }) return nil } diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index b9bb5d29b..b9f756ef9 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -52,6 +52,8 @@ func Provider() terraform.ResourceProvider { "openstack_compute_keypair": resourceComputeKeypair(), "openstack_compute_secgroup": resourceComputeSecGroup(), "openstack_compute_secgrouprule": resourceComputeSecGroupRule(), + "openstack_networking_network": resourceNetworkingNetwork(), + "openstack_networking_subnet": resourceNetworkingSubnet(), }, ConfigureFunc: configureProvider, diff --git a/builtin/providers/openstack/resource_openstack_networking_network.go b/builtin/providers/openstack/resource_openstack_networking_network.go new file mode 100644 index 000000000..c819aa0f4 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_network.go @@ -0,0 +1,188 @@ +package openstack + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/networking/v2/networks" +) + +func resourceNetworkingNetwork() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkingNetworkCreate, + Read: resourceNetworkingNetworkRead, + Update: resourceNetworkingNetworkUpdate, + Delete: resourceNetworkingNetworkDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "admin_state_up": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "shared": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + }, + } +} + +func resourceNetworkingNetworkCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + createOpts := networks.CreateOpts{ + Name: d.Get("name").(string), + TenantID: d.Get("tenant_id").(string), + } + + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + createOpts.AdminStateUp = &asu + } + + sharedRaw := d.Get("shared").(string) + if sharedRaw != "" { + shared, err := strconv.ParseBool(sharedRaw) + if err != nil { + return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err) + } + createOpts.Shared = &shared + } + + log.Printf("[INFO] Requesting network creation") + n, err := networks.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack Neutron network: %s", err) + } + log.Printf("[INFO] Network ID: %s", n.ID) + + d.SetId(n.ID) + + return resourceNetworkingNetworkRead(d, meta) +} + +func resourceNetworkingNetworkRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + n, err := networks.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack Neutron Network: %s", err) + } + + log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n) + + if _, exists := d.GetOk("name"); exists { + if d.HasChange("name") { + d.Set("name", n.Name) + } + } else { + d.Set("name", "") + } + + if _, exists := d.GetOk("admin_state_up"); exists { + if d.HasChange("admin_state_up") { + d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) + } + } else { + d.Set("admin_state_up", "") + } + + if _, exists := d.GetOk("shared"); exists { + if d.HasChange("shared") { + d.Set("shared", strconv.FormatBool(n.Shared)) + } + } else { + d.Set("shared", "") + } + + if _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", n.TenantID) + } + } else { + d.Set("tenant_id", "") + } + + return nil +} + +func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + var updateOpts networks.UpdateOpts + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + if d.HasChange("admin_state_up") { + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + updateOpts.AdminStateUp = &asu + } + } + if d.HasChange("shared") { + sharedRaw := d.Get("shared").(string) + if sharedRaw != "" { + shared, err := strconv.ParseBool(sharedRaw) + if err != nil { + return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err) + } + updateOpts.Shared = &shared + } + } + if d.HasChange("tenant_id") { + updateOpts.TenantID = d.Get("tenant_id").(string) + } + + // If there's nothing to update, don't waste an HTTP call. + if updateOpts != (networks.UpdateOpts{}) { + log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts) + + _, err := networks.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) + } + } + + return resourceNetworkingNetworkRead(d, meta) +} + +func resourceNetworkingNetworkDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := networks.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err) + } + + d.SetId("") + return nil +} From 4424828d25520542e3fda8f7fcfdd6d610869ea9 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 10 Jan 2015 15:03:01 -0700 Subject: [PATCH 034/167] crud for neutron subnets --- .../resource_openstack_networking_subnet.go | 294 ++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_networking_subnet.go diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet.go b/builtin/providers/openstack/resource_openstack_networking_subnet.go new file mode 100644 index 000000000..bedad5572 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_subnet.go @@ -0,0 +1,294 @@ +package openstack + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/jrperritt/terraform/helper/hashcode" + "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" +) + +func resourceNetworkingSubnet() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkingSubnetCreate, + Read: resourceNetworkingSubnetRead, + Update: resourceNetworkingSubnetUpdate, + Delete: resourceNetworkingSubnetDelete, + + Schema: map[string]*schema.Schema{ + "network_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "cidr": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "allocation_pools": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "start": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "end": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "gateway_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "ip_version": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "enable_dhcp": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "dns_nameservers": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + + "host_routes": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: false, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_cidr": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "next_hop": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + } +} + +func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + createOpts := subnets.CreateOpts{ + NetworkID: d.Get("network_id").(string), + CIDR: d.Get("cidr").(string), + Name: d.Get("name").(string), + TenantID: d.Get("tenant_id").(string), + AllocationPools: resourceSubnetAllocationPools(d), + GatewayIP: d.Get("gateway_ip").(string), + IPVersion: d.Get("ip_version").(int), + DNSNameservers: resourceSubnetDNSNameservers(d), + HostRoutes: resourceSubnetHostRoutes(d), + } + + edRaw := d.Get("enable_dhcp").(string) + if edRaw != "" { + ed, err := strconv.ParseBool(edRaw) + if err != nil { + return fmt.Errorf("enable_dhcp, if provided, must be either 'true' or 'false'") + } + createOpts.EnableDHCP = &ed + } + + log.Printf("[INFO] Requesting subnet creation") + s, err := subnets.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err) + } + log.Printf("[INFO] Subnet ID: %s", s.ID) + + d.SetId(s.ID) + + return resourceNetworkingSubnetRead(d, meta) +} + +func resourceNetworkingSubnetRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + s, err := subnets.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack Neutron Subnet: %s", err) + } + + log.Printf("[DEBUG] Retreived Subnet %s: %+v", d.Id(), s) + + d.Set("newtork_id", s.NetworkID) + d.Set("cidr", s.CIDR) + d.Set("ip_version", s.IPVersion) + + if _, exists := d.GetOk("name"); exists { + if d.HasChange("name") { + d.Set("name", s.Name) + } + } else { + d.Set("name", "") + } + + if _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", s.Name) + } + } else { + d.Set("tenant_id", "") + } + + if _, exists := d.GetOk("allocation_pools"); exists { + d.Set("allocation_pools", s.AllocationPools) + } + + if _, exists := d.GetOk("gateway_ip"); exists { + if d.HasChange("gateway_ip") { + d.Set("gateway_ip", s.Name) + } + } else { + d.Set("gateway_ip", "") + } + + if _, exists := d.GetOk("enable_dhcp"); exists { + if d.HasChange("enable_dhcp") { + d.Set("enable_dhcp", strconv.FormatBool(s.EnableDHCP)) + } + } else { + d.Set("enable_dhcp", "") + } + + if _, exists := d.GetOk("dns_nameservers"); exists { + d.Set("dns_nameservers", s.DNSNameservers) + } + + if _, exists := d.GetOk("host_routes"); exists { + d.Set("host_routes", s.HostRoutes) + } + + return nil +} + +func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + var updateOpts subnets.UpdateOpts + + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + + if d.HasChange("gateway_ip") { + updateOpts.GatewayIP = d.Get("gateway_ip").(string) + } + + if d.HasChange("dns_nameservers") { + updateOpts.DNSNameservers = resourceSubnetDNSNameservers(d) + } + + if d.HasChange("host_routes") { + updateOpts.HostRoutes = resourceSubnetHostRoutes(d) + } + + if d.HasChange("enable_dhcp") { + edRaw := d.Get("enable_dhcp").(string) + if edRaw != "" { + ed, err := strconv.ParseBool(edRaw) + if err != nil { + return fmt.Errorf("enable_dhcp, if provided, must be either 'true' or 'false'") + } + updateOpts.EnableDHCP = &ed + } + } + + log.Printf("[DEBUG] Updating Subnet %s with options: %+v", d.Id(), updateOpts) + + _, err := subnets.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack Neutron Subnet: %s", err) + } + + return resourceNetworkingSubnetRead(d, meta) +} + +func resourceNetworkingSubnetDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := subnets.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack Neutron Subnet: %s", err) + } + + d.SetId("") + return nil +} + +func resourceSubnetAllocationPools(d *schema.ResourceData) []subnets.AllocationPool { + rawAPs := d.Get("allocation_pools").([]interface{}) + aps := make([]subnets.AllocationPool, len(rawAPs)) + for i, raw := range rawAPs { + aps[i] = raw.(subnets.AllocationPool) + } + return aps +} + +func resourceSubnetDNSNameservers(d *schema.ResourceData) []string { + rawDNSN := d.Get("dns_nameservers").(*schema.Set) + dnsn := make([]string, rawDNSN.Len()) + for i, raw := range rawDNSN.List() { + dnsn[i] = raw.(string) + } + return dnsn +} + +func resourceSubnetHostRoutes(d *schema.ResourceData) []subnets.HostRoute { + rawHR := d.Get("host_routes").([]interface{}) + hr := make([]subnets.HostRoute, len(rawHR)) + for i, raw := range rawHR { + hr[i] = raw.(subnets.HostRoute) + } + return hr +} From ac79c76b132ce5d0c00690d9f47c4af6b21627b3 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:53:34 -0700 Subject: [PATCH 035/167] add lb resources --- builtin/providers/openstack/provider.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index b9f756ef9..9426fc23a 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -52,6 +52,10 @@ func Provider() terraform.ResourceProvider { "openstack_compute_keypair": resourceComputeKeypair(), "openstack_compute_secgroup": resourceComputeSecGroup(), "openstack_compute_secgrouprule": resourceComputeSecGroupRule(), + "openstack_lb_member": resourceLBMember(), + "openstack_lb_monitor": resourceLBMonitor(), + "openstack_lb_pool": resourceLBPool(), + "openstack_lb_vip": resourceLBVip(), "openstack_networking_network": resourceNetworkingNetwork(), "openstack_networking_subnet": resourceNetworkingSubnet(), }, From 9077b6604528327ad4d4294d286682ccbdb1e00b Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:54:27 -0700 Subject: [PATCH 036/167] lb member operations --- .../openstack/resource_openstack_lb_member.go | 131 ++++++++++++++++++ .../openstack/r/lb_member.html.markdown | 52 +++++++ 2 files changed, 183 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_member.go create mode 100644 website/source/docs/providers/openstack/r/lb_member.html.markdown diff --git a/builtin/providers/openstack/resource_openstack_lb_member.go b/builtin/providers/openstack/resource_openstack_lb_member.go new file mode 100644 index 000000000..c628ee2b4 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_member.go @@ -0,0 +1,131 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" +) + +func resourceLBMember() *schema.Resource { + return &schema.Resource{ + Create: resourceLBMemberCreate, + Read: resourceLBMemberRead, + Update: resourceLBMemberUpdate, + Delete: resourceLBMemberDelete, + + Schema: map[string]*schema.Schema{ + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "address": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "pool_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "admin_state_up": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceLBMemberCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + createOpts := members.CreateOpts{ + //TenantID: d.Get("tenant_id").(string), + Address: d.Get("address").(string), + ProtocolPort: d.Get("port").(int), + PoolID: d.Get("pool_id").(string), + } + + log.Printf("[INFO] Requesting lb member creation") + p, err := members.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack LB member: %s", err) + } + log.Printf("[INFO] LB Member ID: %s", p.ID) + + d.SetId(p.ID) + + return resourceLBMemberRead(d, meta) +} + +func resourceLBMemberRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + p, err := members.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack LB Member: %s", err) + } + + log.Printf("[DEBUG] Retreived OpenStack LB Member %s: %+v", d.Id(), p) + + d.Set("address", p.Address) + d.Set("port", p.ProtocolPort) + d.Set("pool_id", p.PoolID) + + if _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", p.TenantID) + } + } else { + d.Set("tenant_id", "") + } + + return nil +} + +func resourceLBMemberUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + var updateOpts members.UpdateOpts + if d.HasChange("admin_state_up") { + updateOpts.AdminStateUp = d.Get("admin_state_up").(bool) + } + + log.Printf("[DEBUG] Updating OpenStack LB Member %s with options: %+v", d.Id(), updateOpts) + + _, err := members.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack LB Member: %s", err) + } + + return resourceLBMemberRead(d, meta) +} + +func resourceLBMemberDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := members.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack LB Member: %s", err) + } + + d.SetId("") + return nil +} diff --git a/website/source/docs/providers/openstack/r/lb_member.html.markdown b/website/source/docs/providers/openstack/r/lb_member.html.markdown new file mode 100644 index 000000000..0607ca840 --- /dev/null +++ b/website/source/docs/providers/openstack/r/lb_member.html.markdown @@ -0,0 +1,52 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_lb_member" +sidebar_current: "docs-openstack-resource-lb-member" +description: |- + Manages a load balancer member resource within OpenStack. +--- + +# openstack\_lb\_member + +Manages a load balancer member resource within OpenStack. + +## Example Usage + +``` +resource "openstack_lb_member" "node_1" { + address = "196.172.0.1" + port = 80 + pool_id = "$12345" + admin_state_up = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `address` - (Required) The IP address of the member. Changing this creates a + new member. + +* `port` - (Required) An integer representing the port on which the member is + hosted. Changing this creates a new member. + +* `pool_id` - (Required) The pool to which this member will belong. Changing + this creates a new member. + +* `admin_state_up` - (Optional) The administrative state of the member. + Acceptable values are 'true' and 'false'. Changing this value updates the + state of the existing member. + +* `tenant_id` - (Optional) The owner of the member. Required if admin wants to + create a pool member for another tenant. Changing this creates a new member. + +## Attributes Reference + +The following attributes are exported: + +* `address` - See Argument Reference above. +* `port` - See Argument Reference above. +* `pool_id` - See Argument Reference above. +* `admin_state_up` - See Argument Reference above. +* `tenant_id` - See Argument Reference above. From 966c8420d1bd8a5ab23c96d6d148e3a233100d1e Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:54:52 -0700 Subject: [PATCH 037/167] lb monitor operations --- .../resource_openstack_lb_monitor.go | 227 ++++++++++++++++++ .../openstack/r/lb_monitor.html.markdown | 76 ++++++ 2 files changed, 303 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_monitor.go create mode 100644 website/source/docs/providers/openstack/r/lb_monitor.html.markdown diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor.go b/builtin/providers/openstack/resource_openstack_lb_monitor.go new file mode 100644 index 000000000..b2cb98641 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_monitor.go @@ -0,0 +1,227 @@ +package openstack + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" +) + +func resourceLBMonitor() *schema.Resource { + return &schema.Resource{ + Create: resourceLBMonitorCreate, + Read: resourceLBMonitorRead, + Update: resourceLBMonitorUpdate, + Delete: resourceLBMonitorDelete, + + Schema: map[string]*schema.Schema{ + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "delay": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + + "timeout": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + + "max_retries": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + + "url_path": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "http_method": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "expected_codes": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "admin_state_up": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + }, + } +} + +func resourceLBMonitorCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + createOpts := monitors.CreateOpts{ + TenantID: d.Get("tenant_id").(string), + Type: d.Get("type").(string), + Delay: d.Get("delay").(int), + Timeout: d.Get("timeout").(int), + MaxRetries: d.Get("max_retries").(int), + URLPath: d.Get("url_path").(string), + ExpectedCodes: d.Get("expected_codes").(string), + HTTPMethod: d.Get("http_method").(string), + } + + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + createOpts.AdminStateUp = &asu + } + + log.Printf("[INFO] Requesting lb monitor creation") + m, err := monitors.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack LB Monitor: %s", err) + } + log.Printf("[INFO] LB Monitor ID: %s", m.ID) + + d.SetId(m.ID) + + return resourceLBMonitorRead(d, meta) +} + +func resourceLBMonitorRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + m, err := monitors.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack LB Monitor: %s", err) + } + + log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m) + + d.Set("type", m.Type) + d.Set("delay", m.Delay) + d.Set("timeout", m.Timeout) + d.Set("max_retries", m.MaxRetries) + + if _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", m.TenantID) + } + } else { + d.Set("tenant_id", "") + } + + if _, exists := d.GetOk("url_path"); exists { + if d.HasChange("url_path") { + d.Set("url_path", m.URLPath) + } + } else { + d.Set("url_path", "") + } + + if _, exists := d.GetOk("http_method"); exists { + if d.HasChange("http_method") { + d.Set("http_method", m.HTTPMethod) + } + } else { + d.Set("http_method", "") + } + + if _, exists := d.GetOk("expected_codes"); exists { + if d.HasChange("expected_codes") { + d.Set("expected_codes", m.ExpectedCodes) + } + } else { + d.Set("expected_codes", "") + } + + if _, exists := d.GetOk("admin_state_up"); exists { + if d.HasChange("admin_state_up") { + d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp)) + } + } else { + d.Set("admin_state_up", "") + } + + return nil +} + +func resourceLBMonitorUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + var updateOpts monitors.UpdateOpts + if d.HasChange("delay") { + updateOpts.Delay = d.Get("delay").(int) + } + if d.HasChange("timeout") { + updateOpts.Timeout = d.Get("timeout").(int) + } + if d.HasChange("max_retries") { + updateOpts.MaxRetries = d.Get("max_retries").(int) + } + if d.HasChange("url_path") { + updateOpts.URLPath = d.Get("url_path").(string) + } + if d.HasChange("http_method") { + updateOpts.HTTPMethod = d.Get("http_method").(string) + } + if d.HasChange("expected_codes") { + updateOpts.ExpectedCodes = d.Get("expected_codes").(string) + } + if d.HasChange("admin_state_up") { + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + updateOpts.AdminStateUp = &asu + } + } + + log.Printf("[DEBUG] Updating OpenStack LB Monitor %s with options: %+v", d.Id(), updateOpts) + + _, err := monitors.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack LB Monitor: %s", err) + } + + return resourceLBMonitorRead(d, meta) +} + +func resourceLBMonitorDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := monitors.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack LB Monitor: %s", err) + } + + d.SetId("") + return nil +} diff --git a/website/source/docs/providers/openstack/r/lb_monitor.html.markdown b/website/source/docs/providers/openstack/r/lb_monitor.html.markdown new file mode 100644 index 000000000..36b814274 --- /dev/null +++ b/website/source/docs/providers/openstack/r/lb_monitor.html.markdown @@ -0,0 +1,76 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_lb_monitor" +sidebar_current: "docs-openstack-resource-lb-monitor" +description: |- + Manages a load balancer monitor resource within OpenStack. +--- + +# openstack\_lb\_monitor + +Manages a load balancer monitor resource within OpenStack. + +## Example Usage + +``` +resource "openstack_lb_monitor" "monitor_1" { + type = "PING" + delay = 30 + timeout = 5 + max_retries = 3 + admin_state_up = "true" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `type` - (Required) The type of probe, which is PING, TCP, HTTP, or HTTPS, + that is sent by the monitor to verify the member state. Changing this + creates a new monitor. + +* `delay` - (Required) The time, in seconds, between sending probes to members. + Changing this creates a new monitor. + +* `timeout` - (Required) Maximum number of seconds for a monitor to wait for a + ping reply before it times out. The value must be less than the delay value. + Changing this updates the timeout of the existing monitor. + +* `max_retries` - (Required) Number of permissible ping failures before changing + the member's status to INACTIVE. Must be a number between 1 and 10. Changing + this updates the max_retries of the existing monitor. + +* `url_path` - (Optional) Required for HTTP(S) types. URI path that will be + accessed if monitor type is HTTP or HTTPS. Changing this updates the + url_path of the existing monitor. + +* `http_method` - (Optional) Required for HTTP(S) types. The HTTP method used + for requests by the monitor. If this attribute is not specified, it defaults + to "GET". Changing this updates the http_method of the existing monitor. + +* `expected_codes` - (Optional) equired for HTTP(S) types. Expected HTTP codes + for a passing HTTP(S) monitor. You can either specify a single status like + "200", or a range like "200-202". Changing this updates the expected_codes + of the existing monitor. + +* `admin_state_up` - (Optional) The administrative state of the monitor. + Acceptable values are "true" and "false". Changing this value updates the + state of the existing monitor. + +* `tenant_id` - (Optional) The owner of the monitor. Required if admin wants to + create a monitor for another tenant. Changing this creates a new monitor. + +## Attributes Reference + +The following attributes are exported: + +* `type` - See Argument Reference above. +* `delay` - See Argument Reference above. +* `timeout` - See Argument Reference above. +* `max_retries` - See Argument Reference above. +* `url_path` - See Argument Reference above. +* `http_method` - See Argument Reference above. +* `expected_codes` - See Argument Reference above. +* `admin_state_up` - See Argument Reference above. +* `tenant_id` - See Argument Reference above. From 7132b02a84035ea1d16ad246bb7d78b439ebb392 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:55:17 -0700 Subject: [PATCH 038/167] lb pool operations --- .../openstack/resource_openstack_lb_pool.go | 194 ++++++++++++++++++ .../openstack/r/lb_pool.html.markdown | 58 ++++++ 2 files changed, 252 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_pool.go create mode 100644 website/source/docs/providers/openstack/r/lb_pool.html.markdown diff --git a/builtin/providers/openstack/resource_openstack_lb_pool.go b/builtin/providers/openstack/resource_openstack_lb_pool.go new file mode 100644 index 000000000..0fb42463e --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_pool.go @@ -0,0 +1,194 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/jrperritt/terraform/helper/hashcode" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" +) + +func resourceLBPool() *schema.Resource { + return &schema.Resource{ + Create: resourceLBPoolCreate, + Read: resourceLBPoolRead, + Update: resourceLBPoolUpdate, + Delete: resourceLBPoolDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "lb_method": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "monitor_ids": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + }, + } +} + +func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + createOpts := pools.CreateOpts{ + Name: d.Get("name").(string), + Protocol: d.Get("protocol").(string), + SubnetID: d.Get("subnet_id").(string), + LBMethod: d.Get("lb_method").(string), + TenantID: d.Get("tenant_id").(string), + } + + log.Printf("[INFO] Requesting lb pool creation") + p, err := pools.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack LB pool: %s", err) + } + log.Printf("[INFO] LB Pool ID: %s", p.ID) + + d.SetId(p.ID) + + if mIDs := resourcePoolMonitorIDs(d); mIDs != nil { + for _, mID := range mIDs { + _, err := pools.AssociateMonitor(osClient, p.ID, mID).Extract() + if err != nil { + return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err) + } + } + } + + return resourceLBPoolRead(d, meta) +} + +func resourceLBPoolRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + p, err := pools.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack LB Pool: %s", err) + } + + log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p) + + d.Set("name", p.Name) + d.Set("protocol", p.Protocol) + d.Set("subnet_id", p.SubnetID) + d.Set("lb_method", p.LBMethod) + + if _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", p.TenantID) + } + } else { + d.Set("tenant_id", "") + } + + d.Set("monitor_ids", p.MonitorIDs) + + return nil +} + +func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + var updateOpts pools.UpdateOpts + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + if d.HasChange("lb_method") { + updateOpts.LBMethod = d.Get("lb_method").(string) + } + + log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts) + + _, err := pools.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack LB Pool: %s", err) + } + + if d.HasChange("monitor_ids") { + oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups") + oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set) + monitorsToAdd := newMIDsSet.Difference(oldMIDsSet) + monitorsToRemove := oldMIDsSet.Difference(newMIDsSet) + + log.Printf("[DEBUG] Monitors to add: %v", monitorsToAdd) + + log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove) + + for _, m := range monitorsToAdd.List() { + _, err := pools.AssociateMonitor(osClient, d.Id(), m.(string)).Extract() + if err != nil { + return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err) + } + log.Printf("[DEBUG] Associated monitor (%s) with pool (%s)", m.(string), d.Id()) + } + + for _, m := range monitorsToRemove.List() { + _, err := pools.DisassociateMonitor(osClient, d.Id(), m.(string)).Extract() + if err != nil { + return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err) + } + log.Printf("[DEBUG] Disassociated monitor (%s) from pool (%s)", m.(string), d.Id()) + } + } + + return resourceLBPoolRead(d, meta) +} + +func resourceLBPoolDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := pools.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err) + } + + d.SetId("") + return nil +} + +func resourcePoolMonitorIDs(d *schema.ResourceData) []string { + mIDsRaw := d.Get("monitor_ids").(*schema.Set) + mIDs := make([]string, mIDsRaw.Len()) + for i, raw := range mIDsRaw.List() { + mIDs[i] = raw.(string) + } + return mIDs +} diff --git a/website/source/docs/providers/openstack/r/lb_pool.html.markdown b/website/source/docs/providers/openstack/r/lb_pool.html.markdown new file mode 100644 index 000000000..90e3ed0c9 --- /dev/null +++ b/website/source/docs/providers/openstack/r/lb_pool.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_lb_pool" +sidebar_current: "docs-openstack-resource-lb-pool" +description: |- + Manages a load balancer pool resource within OpenStack. +--- + +# openstack\_lb\_pool + +Manages a load balancer pool resource within OpenStack. + +## Example Usage + +``` +resource "openstack_lb_pool" "pool_1" { + name = "tf_test_lb_pool" + protocol = "HTTP" + subnet_id = "12345" + lb_method = "ROUND_ROBIN" + monitor_id = "67890" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the pool. Changing this updates the name of + the existing pool. + +* `protocol` - (Required) The protocol used by the pool members, you can use + either 'TCP, 'HTTP', or 'HTTPS'. Changing this creates a new pool. + +* `subnet_id` - (Required) The network on which the members of the pool will be + located. Only members that are on this network can be added to the pool. + Changing this creates a new pool. + +* `lb_method` - (Required) The algorithm used to distribute load between the + members of the pool. The current specification supports 'ROUND_ROBIN' and + 'LEAST_CONNECTIONS' as valid values for this attribute. + +* `tenant_id` - (Optional) The owner of the pool. Required if admin wants to + create a pool member for another tenant. Changing this creates a new pool. + +* `monitor_ids` - (Optional) A list of IDs of monitors to associate with the + pool. + +## Attributes Reference + +The following attributes are exported: + +* `name` - See Argument Reference above. +* `protocol` - See Argument Reference above. +* `subnet_id` - See Argument Reference above. +* `lb_method` - See Argument Reference above. +* `tenant_id` - See Argument Reference above. +* `monitor_id` - See Argument Reference above. From c2230a8aaa1b67cbdcbb85b154b73cc53ff3528f Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:55:36 -0700 Subject: [PATCH 039/167] lb vip operations --- .../openstack/resource_openstack_lb_vip.go | 256 ++++++++++++++++++ .../openstack/r/lb_vip.html.markdown | 89 ++++++ 2 files changed, 345 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_vip.go create mode 100644 website/source/docs/providers/openstack/r/lb_vip.html.markdown diff --git a/builtin/providers/openstack/resource_openstack_lb_vip.go b/builtin/providers/openstack/resource_openstack_lb_vip.go new file mode 100644 index 000000000..405d01a19 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_vip.go @@ -0,0 +1,256 @@ +package openstack + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" +) + +func resourceLBVip() *schema.Resource { + return &schema.Resource{ + Create: resourceLBVipCreate, + Read: resourceLBVipRead, + Update: resourceLBVipUpdate, + Delete: resourceLBVipDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + + "pool_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "persistence": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + + "conn_limit": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: false, + }, + + "admin_state_up": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + }, + } +} + +func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + createOpts := vips.CreateOpts{ + Name: d.Get("name").(string), + SubnetID: d.Get("subnet_id").(string), + Protocol: d.Get("protocol").(string), + ProtocolPort: d.Get("port").(int), + PoolID: d.Get("pool_id").(string), + TenantID: d.Get("tenant_id").(string), + Address: d.Get("address").(string), + Description: d.Get("description").(string), + Persistence: resourceVipPersistence(d), + ConnLimit: gophercloud.MaybeInt(d.Get("conn_limit").(int)), + } + + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + createOpts.AdminStateUp = &asu + } + + log.Printf("[INFO] Requesting lb vip creation") + p, err := vips.Create(osClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack LB VIP: %s", err) + } + log.Printf("[INFO] LB VIP ID: %s", p.ID) + + d.SetId(p.ID) + + return resourceLBVipRead(d, meta) +} + +func resourceLBVipRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + p, err := vips.Get(osClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack LB VIP: %s", err) + } + + log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p) + + d.Set("name", p.Name) + d.Set("subnet_id", p.SubnetID) + d.Set("protocol", p.Protocol) + d.Set("port", p.ProtocolPort) + d.Set("pool_id", p.PoolID) + + if _, exists := d.GetOk("tenant_id"); exists { + if d.HasChange("tenant_id") { + d.Set("tenant_id", p.TenantID) + } + } else { + d.Set("tenant_id", "") + } + + if _, exists := d.GetOk("address"); exists { + if d.HasChange("address") { + d.Set("address", p.Address) + } + } else { + d.Set("address", "") + } + + if _, exists := d.GetOk("description"); exists { + if d.HasChange("description") { + d.Set("description", p.Description) + } + } else { + d.Set("description", "") + } + + if _, exists := d.GetOk("persistence"); exists { + if d.HasChange("persistence") { + d.Set("persistence", p.Description) + } + } + + if _, exists := d.GetOk("conn_limit"); exists { + if d.HasChange("conn_limit") { + d.Set("conn_limit", p.ConnLimit) + } + } else { + d.Set("conn_limit", "") + } + + if _, exists := d.GetOk("admin_state_up"); exists { + if d.HasChange("admin_state_up") { + d.Set("admin_state_up", strconv.FormatBool(p.AdminStateUp)) + } + } else { + d.Set("admin_state_up", "") + } + + return nil +} + +func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + var updateOpts vips.UpdateOpts + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + if d.HasChange("pool_id") { + updateOpts.PoolID = d.Get("pool_id").(string) + } + if d.HasChange("description") { + updateOpts.Description = d.Get("description").(string) + } + if d.HasChange("persistence") { + updateOpts.Persistence = resourceVipPersistence(d) + } + if d.HasChange("conn_limit") { + updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int)) + } + if d.HasChange("admin_state_up") { + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + updateOpts.AdminStateUp = &asu + } + } + + log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts) + + _, err := vips.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack LB VIP: %s", err) + } + + return resourceLBVipRead(d, meta) +} + +func resourceLBVipDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + osClient := config.networkingV2Client + + err := vips.Delete(osClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err) + } + + d.SetId("") + return nil +} + +func resourceVipPersistence(d *schema.ResourceData) *vips.SessionPersistence { + rawP := d.Get("persistence").(interface{}) + rawMap := rawP.(map[string]interface{}) + p := vips.SessionPersistence{ + Type: rawMap["type"].(string), + CookieName: rawMap["cookie_name"].(string), + } + return &p +} diff --git a/website/source/docs/providers/openstack/r/lb_vip.html.markdown b/website/source/docs/providers/openstack/r/lb_vip.html.markdown new file mode 100644 index 000000000..0eddcaa5f --- /dev/null +++ b/website/source/docs/providers/openstack/r/lb_vip.html.markdown @@ -0,0 +1,89 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_lb_vip" +sidebar_current: "docs-openstack-resource-lb-vip" +description: |- + Manages a load balancer vip resource within OpenStack. +--- + +# openstack\_lb\_vip + +Manages a load balancer vip resource within OpenStack. + +## Example Usage + +``` +resource "openstack_lb_vip" "vip_1" { + name = "tf_test_lb_vip" + subnet_id = "12345" + protocol = "HTTP" + port = 80 + pool_id = "67890" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the vip. Changing this updates the name of + the existing vip. + +* `subnet_id` - (Required) The network on which to allocate the vip's address. A + tenant can only create vips on networks authorized by policy (e.g. networks + that belong to them or networks that are shared). Changing this creates a + new vip. + +* `protocol` - (Required) The protocol - can be either 'TCP, 'HTTP', or + HTTPS'. Changing this creates a new vip. + +* `port` - (Required) The port on which to listen for client traffic. Changing + this creates a new vip. + +* `pool_id` - (Required) The ID of the pool with which the vip is associated. + Changing this updates the pool_id of the existing vip. + +* `tenant_id` - (Optional) The owner of the vip. Required if admin wants to + create a vip member for another tenant. Changing this creates a new vip. + +* `address` - (Optional) The IP address of the vip. Changing this creates a new + vip. + +* `description` - (Optional) Human-readable description for the vip. Changing + this updates the description of the existing vip. + +* `persistence` - (Optional) Omit this field to prevent session persistence. + The persistence object structure is documented below. Changing this updates + the persistence of the existing vip. + +* `conn_limit` - (Optional) The maximum number of connections allowed for the + vip. Default is -1, meaning no limit. Changing this updates the conn_limit + of the existing vip. + +* `admin_state_up` - (Optional) The administrative state of the vip. + Acceptable values are "true" and "false". Changing this value updates the + state of the existing vip. + +The `persistence` block supports: + +* `type` - (Required) The type of persistence mode. Valid values are "SOURCE_IP", + "HTTP_COOKIE", or "APP_COOKIE". + +* `cookie_name` - (Optional) The name of the cookie if persistence mode is set + appropriately. + +## Attributes Reference + +The following attributes are exported: + +* `name` - See Argument Reference above. +* `subnet_id` - See Argument Reference above. +* `protocol` - See Argument Reference above. +* `port` - See Argument Reference above. +* `pool_id` - See Argument Reference above. +* `tenant_id` - See Argument Reference above. +* `address` - See Argument Reference above. +* `description` - See Argument Reference above. +* `persistence` - See Argument Reference above. +* `conn_limit` - See Argument Reference above. +* `admin_state_up` - See Argument Reference above. From ef3ee11045e09925f40009acc72be023c1e7fdbe Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:56:52 -0700 Subject: [PATCH 040/167] neutron network operations --- .../resource_openstack_networking_network.go | 16 ++----- .../r/networking_network.html.markdown | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 website/source/docs/providers/openstack/r/networking_network.html.markdown diff --git a/builtin/providers/openstack/resource_openstack_networking_network.go b/builtin/providers/openstack/resource_openstack_networking_network.go index c819aa0f4..8f262e369 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network.go +++ b/builtin/providers/openstack/resource_openstack_networking_network.go @@ -38,7 +38,7 @@ func resourceNetworkingNetwork() *schema.Resource { "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: false, + ForceNew: true, }, }, } @@ -157,18 +157,12 @@ func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) e updateOpts.Shared = &shared } } - if d.HasChange("tenant_id") { - updateOpts.TenantID = d.Get("tenant_id").(string) - } - // If there's nothing to update, don't waste an HTTP call. - if updateOpts != (networks.UpdateOpts{}) { - log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts) + log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts) - _, err := networks.Update(osClient, d.Id(), updateOpts).Extract() - if err != nil { - return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) - } + _, err := networks.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) } return resourceNetworkingNetworkRead(d, meta) diff --git a/website/source/docs/providers/openstack/r/networking_network.html.markdown b/website/source/docs/providers/openstack/r/networking_network.html.markdown new file mode 100644 index 000000000..eb765cac0 --- /dev/null +++ b/website/source/docs/providers/openstack/r/networking_network.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_networking_network" +sidebar_current: "docs-openstack-resource-networking-network" +description: |- + Manages a Neutron network resource within OpenStack. +--- + +# openstack\_networking\_network + +Manages a Neutron network resource within OpenStack. + +## Example Usage + +``` +resource "openstack_networking_network" "network_1" { + name = "tf_test_network" + admin_state_up = "true" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Optional) The name of the network. Changing this updates the name of + the existing network. + +* `shared` - (Optional) Specifies whether the network resource can be accessed + by any tenant or not. Changing this updates the sharing capabalities of the + existing network. + +* `tenant_id` - (Optional) The owner of the newtork. Required if admin wants to + create a network for another tenant. Changing this creates a new network. + +* `admin_state_up` - (Optional) The administrative state of the network. + Acceptable values are "true" and "false". Changing this value updates the + state of the existing network. + +## Attributes Reference + +The following attributes are exported: + +* `name` - See Argument Reference above. +* `shared` - See Argument Reference above. +* `tenant_id` - See Argument Reference above. +* `admin_state_up` - See Argument Reference above. From 8e6e7909cbb5658978d9adf962d1b8927df0a4eb Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:57:47 -0700 Subject: [PATCH 041/167] neutron subnet operations --- .../resource_openstack_networking_subnet.go | 12 ++- .../r/networking_subnet.html.markdown | 92 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 website/source/docs/providers/openstack/r/networking_subnet.html.markdown diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet.go b/builtin/providers/openstack/resource_openstack_networking_subnet.go index bedad5572..0f52c0be2 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet.go @@ -270,7 +270,11 @@ func resourceSubnetAllocationPools(d *schema.ResourceData) []subnets.AllocationP rawAPs := d.Get("allocation_pools").([]interface{}) aps := make([]subnets.AllocationPool, len(rawAPs)) for i, raw := range rawAPs { - aps[i] = raw.(subnets.AllocationPool) + rawMap := raw.(map[string]interface{}) + aps[i] = subnets.AllocationPool{ + Start: rawMap["start"].(string), + End: rawMap["end"].(string), + } } return aps } @@ -288,7 +292,11 @@ func resourceSubnetHostRoutes(d *schema.ResourceData) []subnets.HostRoute { rawHR := d.Get("host_routes").([]interface{}) hr := make([]subnets.HostRoute, len(rawHR)) for i, raw := range rawHR { - hr[i] = raw.(subnets.HostRoute) + rawMap := raw.(map[string]interface{}) + hr[i] = subnets.HostRoute{ + DestinationCIDR: rawMap["destination_cidr"].(string), + NextHop: rawMap["next_hop"].(string), + } } return hr } diff --git a/website/source/docs/providers/openstack/r/networking_subnet.html.markdown b/website/source/docs/providers/openstack/r/networking_subnet.html.markdown new file mode 100644 index 000000000..f9ab4f6ed --- /dev/null +++ b/website/source/docs/providers/openstack/r/networking_subnet.html.markdown @@ -0,0 +1,92 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_networking_subnet" +sidebar_current: "docs-openstack-resource-networking-subnet" +description: |- + Manages a Neutron subnet resource within OpenStack. +--- + +# openstack\_networking\_subnet + +Manages a Neutron subnet resource within OpenStack. + +## Example Usage + +``` +resource "openstack_networking_network" "network_1" { + name = "tf_test_network" + admin_state_up = "true" +} + +resource "openstack_networking_subnet" "subnet_1" { + network_id = "${openstack_networking_network.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `network_id` - (Required) The UUID of the parent network. Changing this + creates a new subnet. + +* `cidr` - (Required) CIDR representing IP range for this subnet, based on IP + version. Changing this creates a new subnet. + +* `ip_version` - (Required) IP version, either 4 or 6. Changing this creates a + new subnet. + +* `name` - (Optional) The name of the subnet. Changing this updates the name of + the existing subnet. + +* `tenant_id` - (Optional) The owner of the subnet. Required if admin wants to + create a subnet for another tenant. Changing this creates a new subnet. + +* `allocation_pools` - (Optional) An array of sub-ranges of CIDR available for + dynamic allocation to ports. The allocation_pool object structure is + documented below. Changing this creates a new subnet. + +* `gateway_ip` - (Optional) Default gateway used by devices in this subnet. + Changing this updates the gateway IP of the existing subnet. + +* `enable_dhcp` - (Optional) The administrative state of the network. + Acceptable values are "true" and "false". Changing this value enables or + disables the DHCP capabilities of the existing subnet. + +* `dns_nameservers` - (Optional) An array of DNS name server names used by hosts + in this subnet. Changing this updates the DNS name servers for the existing + subnet. + +* `host_routes` - (Optional) An array of routes that should be used by devices + with IPs from this subnet (not including local subnet route). The host_route + object structure is documented below. Changing this updates the host routes + for the existing subnet. + +The `allocation_pools` block supports: + +* `start` - (Required) The starting address. + +* `end` - (Required) The ending address. + +The `host_routes` block supports: + +* `destination_cidr` - (Required) The destination CIDR. + +* `next_hop` - (Required) The next hop in the route. + +## Attributes Reference + +The following attributes are exported: + +* `network_id` - See Argument Reference above. +* `cidr` - See Argument Reference above. +* `ip_version` - See Argument Reference above. +* `name` - See Argument Reference above. +* `tenant_id` - See Argument Reference above. +* `allocation_pools` - See Argument Reference above. +* `gateway_ip` - See Argument Reference above. +* `enable_dhcp` - See Argument Reference above. +* `dns_nameservers` - See Argument Reference above. +* `host_routes` - See Argument Reference above. From dd4155fa80093b52a38cd678c967d383e4678fcd Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 11:58:52 -0700 Subject: [PATCH 042/167] set host in read function --- .../providers/openstack/resource_openstack_compute_instance.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index 62d14b636..e101e45f7 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -205,10 +205,12 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error pa := paRaw.(map[string]interface{}) if pa["version"].(float64) == 4 { host = pa["addr"].(string) + d.Set("access_ip_v4", host) } } } } + d.Set("host", host) log.Printf("host: %s", host) From 2a4570e382ea7e38e75f54e54684657dea5c33e3 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 12:00:39 -0700 Subject: [PATCH 043/167] add comment for changing 'networks' attribute --- .../docs/providers/openstack/r/compute_instance.html.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/providers/openstack/r/compute_instance.html.markdown b/website/source/docs/providers/openstack/r/compute_instance.html.markdown index f22624bf9..7de1cd1b9 100644 --- a/website/source/docs/providers/openstack/r/compute_instance.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance.html.markdown @@ -45,7 +45,8 @@ The following arguments are supported: the server. Changing this creates a new server. * `networks` - (Optional) An array of one or more networks to attach to the - instance. The network object structure is documented below. + instance. The network object structure is documented below. Changing this + creates a new server. * `metadata` - (Optional) Metadata key/value pairs to make available from within the instance. Changing this updates the existing server metadata. From bf92b1647fa1ce86e142a45a3fe972548e25bb11 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 12:01:20 -0700 Subject: [PATCH 044/167] fix typo --- .../providers/openstack/r/compute_secgrouprule.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown b/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown index 95b92981c..60ffb347c 100644 --- a/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown @@ -43,11 +43,11 @@ The following arguments are supported: * `ip_protocol` - (Required) The protocol type that will be allowed. Changing this creates a new security group rule. -* `cidr` - (Optional) Required is `from_group_id` is empty. The IP range that +* `cidr` - (Optional) Required if `from_group_id` is empty. The IP range that will be the source of network traffic to the security group. Use 0.0.0.0./0 to allow all IP addresses. Changing this creates a new security group rule. -* `from_group_id - (Optional) Required is `cidr` is empty. The ID of a group +* `from_group_id - (Optional) Required if `cidr` is empty. The ID of a group from which to forward traffic to the parent group. Changing this creates a new security group rule. From f4a22ae47ff7d75c392e81b237cb963df96f9fc9 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 14 Jan 2015 12:01:52 -0700 Subject: [PATCH 045/167] add neutron and lb resources --- website/source/layouts/openstack.erb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb index 69bc4ba2e..b7878887c 100644 --- a/website/source/layouts/openstack.erb +++ b/website/source/layouts/openstack.erb @@ -20,10 +20,28 @@ openstack_compute_keypair > - openstack_compute_keypair + openstack_compute_secgroup > - openstack_compute_keypair + openstack_compute_secgrouprule + + > + openstack_lb_member + + > + openstack_lb_monitor + + > + openstack_lb_pool + + > + openstack_lb_vip + + > + openstack_networking_network + + > + openstack_networking_subnet From cc1445d760495ee7ae0cbdd18a8a2c9d4e634048 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 18 Jan 2015 20:11:20 -0700 Subject: [PATCH 046/167] if update func called, there's something to update --- .../openstack/resource_openstack_compute_instance.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance.go index e101e45f7..5ef061c54 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance.go @@ -261,14 +261,11 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err updateOpts.AccessIPv4 = d.Get("access_ip_v6").(string) } - // If there's nothing to update, don't waste an HTTP call. - if updateOpts != (servers.UpdateOpts{}) { - log.Printf("[DEBUG] Updating Server %s with options: %+v", d.Id(), updateOpts) + log.Printf("[DEBUG] Updating Server %s with options: %+v", d.Id(), updateOpts) - _, err := servers.Update(osClient, d.Id(), updateOpts).Extract() - if err != nil { - return fmt.Errorf("Error updating OpenStack server: %s", err) - } + _, err := servers.Update(osClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack server: %s", err) } if d.HasChange("metadata") { From b1b693e46136da3a8dfc5fa00b4b15e1b1670ff7 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 25 Jan 2015 20:13:39 -0700 Subject: [PATCH 047/167] region is resource-specific; doesn't belong with auth --- builtin/providers/openstack/provider.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 9426fc23a..764266cd4 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -11,13 +11,6 @@ import ( func Provider() terraform.ResourceProvider { return &schema.Provider{ Schema: map[string]*schema.Schema{ - "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), - Description: descriptions["region"], - }, - "auth_url": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -36,7 +29,6 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeString, Optional: true, DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), - //Description: descriptions["tenantname"], }, "password": &schema.Schema{ From b0e8cd5dd3ac7f18d87cd9c0a6452a3be0c22da2 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 25 Jan 2015 20:59:01 -0700 Subject: [PATCH 048/167] a more general way of generating provider clients --- builtin/providers/openstack/clients.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 builtin/providers/openstack/clients.go diff --git a/builtin/providers/openstack/clients.go b/builtin/providers/openstack/clients.go new file mode 100644 index 000000000..b08804467 --- /dev/null +++ b/builtin/providers/openstack/clients.go @@ -0,0 +1,25 @@ +package openstack + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" +) + +func newClient(c *Config, service, region string, version int) (*gophercloud.ServiceClient, error) { + var serviceClient *gophercloud.ServiceClient + switch service { + case "compute": + if version == 2 { + serviceClient, err = openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{ + Region: region, + }) + } + case "networking": + if version == 2 { + serviceClient, err = openstack.NewNetworkV2(c.osClient, gophercloud.EndpointOpts{ + Region: region, + }) + } + } + return serviceClient, err +} From bfe492d4077ba2cedca46d9fc4b858d1afa7ed83 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 25 Jan 2015 21:00:57 -0700 Subject: [PATCH 049/167] add options for openstack identity v3 --- builtin/providers/openstack/config.go | 22 +++++----- builtin/providers/openstack/provider.go | 56 +++++++++++++++---------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index d877fcd80..215817bdd 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -6,22 +6,30 @@ import ( ) type Config struct { - Region string Username string + UserID string Password string + APIKey string IdentityEndpoint string + TenantID string TenantName string + DomainID string + DomainName string - computeV2Client *gophercloud.ServiceClient - networkingV2Client *gophercloud.ServiceClient + osClient *gophercloud.ProviderClient } func (c *Config) loadAndValidate() error { ao := gophercloud.AuthOptions{ Username: c.Username, + UserID: c.UserID, Password: c.Password, + APIKey: c.APIKey, IdentityEndpoint: c.IdentityEndpoint, + TenantID: c.TenantID, TenantName: c.TenantName, + DomainID: c.DomainID, + DomainName: c.DomainName, } client, err := openstack.AuthenticatedClient(ao) @@ -29,13 +37,7 @@ func (c *Config) loadAndValidate() error { return err } - c.computeV2Client, err = openstack.NewComputeV2(client, gophercloud.EndpointOpts{ - Region: c.Region, - }) - - c.networkingV2Client, err = openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ - Region: c.Region, - }) + c.osClient = client return nil } diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 764266cd4..8b3e37a58 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -15,27 +15,46 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeString, Required: true, DefaultFunc: envDefaultFunc("OS_AUTH_URL"), - Description: descriptions["auth_url"], }, - - "username": &schema.Schema{ + "user_name": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, DefaultFunc: envDefaultFunc("OS_USERNAME"), - Description: descriptions["username"], }, - + "user_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_USERID"), + }, + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_TENANT_ID"), + }, "tenant_name": &schema.Schema{ Type: schema.TypeString, Optional: true, DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), }, - "password": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, DefaultFunc: envDefaultFunc("OS_PASSWORD"), - Description: descriptions["password"], + }, + "api_key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_API_KEY"), + }, + "domain_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_DOMAIN_ID"), + }, + "domain_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_DOMAIN_NAME"), }, }, @@ -58,11 +77,15 @@ func Provider() terraform.ResourceProvider { func configureProvider(d *schema.ResourceData) (interface{}, error) { config := Config{ - Region: d.Get("region").(string), IdentityEndpoint: d.Get("auth_url").(string), - Username: d.Get("username").(string), + Username: d.Get("user_name").(string), + UserID: d.Get("user_id").(string), Password: d.Get("password").(string), + APIKey: d.Get("api_key").(string), + TenantID: d.Get("tenant_id").(string), TenantName: d.Get("tenant_name").(string), + DomainID: d.Get("domain_id").(string), + DomainName: d.Get("domain_name").(string), } if err := config.loadAndValidate(); err != nil { @@ -81,14 +104,3 @@ func envDefaultFunc(k string) schema.SchemaDefaultFunc { return nil, nil } } - -var descriptions map[string]string - -func init() { - descriptions = map[string]string{ - "region": "The region where OpenStack operations will take place.", - "auth_url": "The endpoint against which to authenticate.", - "username": "The username with which to authenticate.", - "password": "The password with which to authenticate.", - } -} From 8579c8693adf887ee97695383ff7a321d0478ac1 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 10:54:07 -0700 Subject: [PATCH 050/167] make 'region' resource-specific; create new client for each crud operation --- builtin/providers/openstack/clients.go | 25 ------ ...resource_openstack_compute_instance_v2.go} | 79 +++++++++++-------- ... resource_openstack_compute_keypair_v2.go} | 36 +++++++-- ...resource_openstack_compute_secgroup_v2.go} | 54 +++++++++---- ...urce_openstack_compute_secgrouprule_v2.go} | 31 +++++--- ....go => resource_openstack_lb_member_v1.go} | 48 ++++++++--- ...go => resource_openstack_lb_monitor_v1.go} | 52 ++++++++---- ...ol.go => resource_openstack_lb_pool_v1.go} | 56 +++++++++---- ...vip.go => resource_openstack_lb_vip_v1.go} | 53 ++++++++----- ...source_openstack_networking_network_v2.go} | 47 ++++++++--- ...esource_openstack_networking_subnet_v2.go} | 57 ++++++++----- 11 files changed, 357 insertions(+), 181 deletions(-) delete mode 100644 builtin/providers/openstack/clients.go rename builtin/providers/openstack/{resource_openstack_compute_instance.go => resource_openstack_compute_instance_v2.go} (83%) rename builtin/providers/openstack/{resource_openstack_compute_keypair.go => resource_openstack_compute_keypair_v2.go} (57%) rename builtin/providers/openstack/{resource_openstack_compute_secgroup.go => resource_openstack_compute_secgroup_v2.go} (55%) rename builtin/providers/openstack/{resource_openstack_compute_secgrouprule.go => resource_openstack_compute_secgrouprule_v2.go} (73%) rename builtin/providers/openstack/{resource_openstack_lb_member.go => resource_openstack_lb_member_v1.go} (65%) rename builtin/providers/openstack/{resource_openstack_lb_monitor.go => resource_openstack_lb_monitor_v1.go} (78%) rename builtin/providers/openstack/{resource_openstack_lb_pool.go => resource_openstack_lb_pool_v1.go} (72%) rename builtin/providers/openstack/{resource_openstack_lb_vip.go => resource_openstack_lb_vip_v1.go} (80%) rename builtin/providers/openstack/{resource_openstack_networking_network.go => resource_openstack_networking_network_v2.go} (74%) rename builtin/providers/openstack/{resource_openstack_networking_subnet.go => resource_openstack_networking_subnet_v2.go} (82%) diff --git a/builtin/providers/openstack/clients.go b/builtin/providers/openstack/clients.go deleted file mode 100644 index b08804467..000000000 --- a/builtin/providers/openstack/clients.go +++ /dev/null @@ -1,25 +0,0 @@ -package openstack - -import ( - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" -) - -func newClient(c *Config, service, region string, version int) (*gophercloud.ServiceClient, error) { - var serviceClient *gophercloud.ServiceClient - switch service { - case "compute": - if version == 2 { - serviceClient, err = openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{ - Region: region, - }) - } - case "networking": - if version == 2 { - serviceClient, err = openstack.NewNetworkV2(c.osClient, gophercloud.EndpointOpts{ - Region: region, - }) - } - } - return serviceClient, err -} diff --git a/builtin/providers/openstack/resource_openstack_compute_instance.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go similarity index 83% rename from builtin/providers/openstack/resource_openstack_compute_instance.go rename to builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 5ef061c54..cf434566d 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" @@ -23,24 +24,27 @@ func resourceComputeInstance() *schema.Resource { Delete: resourceComputeInstanceDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "image_ref": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "flavor_ref": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "security_groups": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -50,13 +54,11 @@ func resourceComputeInstance() *schema.Resource { return hashcode.String(v.(string)) }, }, - "availability_zone": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "networks": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -67,12 +69,10 @@ func resourceComputeInstance() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "port": &schema.Schema{ Type: schema.TypeString, Optional: true, }, - "fixed_ip": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -80,39 +80,33 @@ func resourceComputeInstance() *schema.Resource { }, }, }, - "metadata": &schema.Schema{ Type: schema.TypeMap, Optional: true, ForceNew: false, }, - "config_drive": &schema.Schema{ Type: schema.TypeBool, Optional: true, ForceNew: true, }, - "admin_pass": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "access_ip_v4": &schema.Schema{ Type: schema.TypeString, Computed: true, Optional: true, ForceNew: false, }, - "access_ip_v6": &schema.Schema{ Type: schema.TypeString, Computed: true, Optional: true, ForceNew: false, }, - "key_pair": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -124,7 +118,13 @@ func resourceComputeInstance() *schema.Resource { func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + var createOpts servers.CreateOptsBuilder @@ -148,7 +148,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err } log.Printf("[INFO] Requesting instance creation") - server, err := servers.Create(osClient, createOpts).Extract() + server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack server: %s", err) } @@ -166,7 +166,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err stateConf := &resource.StateChangeConf{ Pending: []string{"BUILD"}, Target: "ACTIVE", - Refresh: ServerStateRefreshFunc(osClient, server.ID), + Refresh: ServerStateRefreshFunc(computeClient, server.ID), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -184,9 +184,14 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - server, err := servers.Get(osClient, d.Id()).Extract() + server, err := servers.Get(computeClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack server: %s", err) } @@ -223,7 +228,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("metadata", server.Metadata) var currentSG []string - err = secgroups.ListByServer(osClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { + err = secgroups.ListByServer(computeClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { secGrpList, err := secgroups.ExtractSecurityGroups(page) if err != nil { return false, fmt.Errorf("Error setting security groups for OpenStack server: %s", err) @@ -248,7 +253,12 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } var updateOpts servers.UpdateOpts if d.HasChange("name") { @@ -263,7 +273,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err log.Printf("[DEBUG] Updating Server %s with options: %+v", d.Id(), updateOpts) - _, err := servers.Update(osClient, d.Id(), updateOpts).Extract() + _, err = servers.Update(computeClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack server: %s", err) } @@ -276,7 +286,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err metadataOpts[k] = v.(string) } - _, err := servers.UpdateMetadata(osClient, d.Id(), metadataOpts).Extract() + _, err := servers.UpdateMetadata(computeClient, d.Id(), metadataOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack server (%s) metadata: %s", d.Id(), err) } @@ -293,7 +303,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err log.Printf("[DEBUG] Security groups to remove: %v", secgroupsToRemove) for _, g := range secgroupsToAdd.List() { - err := secgroups.AddServerToGroup(osClient, d.Id(), g.(string)).ExtractErr() + err := secgroups.AddServerToGroup(computeClient, d.Id(), g.(string)).ExtractErr() if err != nil { return fmt.Errorf("Error adding security group to OpenStack server (%s): %s", d.Id(), err) } @@ -301,7 +311,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } for _, g := range secgroupsToRemove.List() { - err := secgroups.RemoveServerFromGroup(osClient, d.Id(), g.(string)).ExtractErr() + err := secgroups.RemoveServerFromGroup(computeClient, d.Id(), g.(string)).ExtractErr() if err != nil { return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) } @@ -311,7 +321,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if d.HasChange("admin_pass") { if newPwd, ok := d.Get("admin_pass").(string); ok { - err := servers.ChangeAdminPassword(osClient, d.Id(), newPwd).ExtractErr() + err := servers.ChangeAdminPassword(computeClient, d.Id(), newPwd).ExtractErr() if err != nil { return fmt.Errorf("Error changing admin password of OpenStack server (%s): %s", d.Id(), err) } @@ -322,7 +332,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err resizeOpts := &servers.ResizeOpts{ FlavorRef: d.Get("flavor_ref").(string), } - err := servers.Resize(osClient, d.Id(), resizeOpts).ExtractErr() + err := servers.Resize(computeClient, d.Id(), resizeOpts).ExtractErr() if err != nil { return fmt.Errorf("Error resizing OpenStack server: %s", err) } @@ -333,7 +343,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err stateConf := &resource.StateChangeConf{ Pending: []string{"RESIZE"}, Target: "VERIFY_RESIZE", - Refresh: ServerStateRefreshFunc(osClient, d.Id()), + Refresh: ServerStateRefreshFunc(computeClient, d.Id()), Timeout: 3 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -346,7 +356,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // Confirm resize. log.Printf("[DEBUG] Confirming resize") - err = servers.ConfirmResize(osClient, d.Id()).ExtractErr() + err = servers.ConfirmResize(computeClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error confirming resize of OpenStack server: %s", err) } @@ -354,7 +364,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err stateConf = &resource.StateChangeConf{ Pending: []string{"VERIFY_RESIZE"}, Target: "ACTIVE", - Refresh: ServerStateRefreshFunc(osClient, d.Id()), + Refresh: ServerStateRefreshFunc(computeClient, d.Id()), Timeout: 3 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -371,9 +381,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - err := servers.Delete(osClient, d.Id()).ExtractErr() + err = servers.Delete(computeClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack server: %s", err) } @@ -383,7 +398,7 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err stateConf := &resource.StateChangeConf{ Target: "", - Refresh: ServerStateRefreshFunc(osClient, d.Id()), + Refresh: ServerStateRefreshFunc(computeClient, d.Id()), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go similarity index 57% rename from builtin/providers/openstack/resource_openstack_compute_keypair.go rename to builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index a8ca16e08..d27a348d4 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" ) @@ -14,12 +16,17 @@ func resourceComputeKeypair() *schema.Resource { Delete: resourceComputeKeypairDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "public_key": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -31,14 +38,19 @@ func resourceComputeKeypair() *schema.Resource { func resourceComputeKeypairCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } createOpts := keypairs.CreateOpts{ Name: d.Get("name").(string), PublicKey: d.Get("public_key").(string), } - kp, err := keypairs.Create(osClient, createOpts).Extract() + kp, err := keypairs.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack keypair: %s", err) } @@ -50,9 +62,14 @@ func resourceComputeKeypairCreate(d *schema.ResourceData, meta interface{}) erro func resourceComputeKeypairRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - kp, err := keypairs.Get(osClient, d.Id()).Extract() + kp, err := keypairs.Get(computeClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack keypair: %s", err) } @@ -65,9 +82,14 @@ func resourceComputeKeypairRead(d *schema.ResourceData, meta interface{}) error func resourceComputeKeypairDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - err := keypairs.Delete(osClient, d.Id()).ExtractErr() + err = keypairs.Delete(computeClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack keypair: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go similarity index 55% rename from builtin/providers/openstack/resource_openstack_compute_secgroup.go rename to builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index f6471eb81..559f96cbf 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -5,6 +5,8 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) @@ -16,12 +18,17 @@ func resourceComputeSecGroup() *schema.Resource { Delete: resourceComputeSecGroupDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "description": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -33,14 +40,19 @@ func resourceComputeSecGroup() *schema.Resource { func resourceComputeSecGroupCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } createOpts := secgroups.CreateOpts{ Name: d.Get("name").(string), Description: d.Get("description").(string), } - sg, err := secgroups.Create(osClient, createOpts).Extract() + sg, err := secgroups.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack security group: %s", err) } @@ -52,9 +64,14 @@ func resourceComputeSecGroupCreate(d *schema.ResourceData, meta interface{}) err func resourceComputeSecGroupRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - sg, err := secgroups.Get(osClient, d.Id()).Extract() + sg, err := secgroups.Get(computeClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack security group: %s", err) } @@ -67,7 +84,12 @@ func resourceComputeSecGroupRead(d *schema.ResourceData, meta interface{}) error func resourceComputeSecGroupUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } var updateOpts secgroups.UpdateOpts if d.HasChange("name") { @@ -77,14 +99,11 @@ func resourceComputeSecGroupUpdate(d *schema.ResourceData, meta interface{}) err updateOpts.Description = d.Get("description").(string) } - // If there's nothing to update, don't waste an HTTP call. - if updateOpts != (secgroups.UpdateOpts{}) { - log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts) + log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts) - _, err := secgroups.Update(osClient, d.Id(), updateOpts).Extract() - if err != nil { - return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err) - } + _, err = secgroups.Update(computeClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err) } return resourceComputeSecGroupRead(d, meta) @@ -92,9 +111,14 @@ func resourceComputeSecGroupUpdate(d *schema.ResourceData, meta interface{}) err func resourceComputeSecGroupDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - err := secgroups.Delete(osClient, d.Id()).ExtractErr() + err = secgroups.Delete(computeClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack security group: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_compute_secgrouprule.go b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go similarity index 73% rename from builtin/providers/openstack/resource_openstack_compute_secgrouprule.go rename to builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go index 1063e2df0..77c14f6f8 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgrouprule.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) @@ -14,36 +16,37 @@ func resourceComputeSecGroupRule() *schema.Resource { Delete: resourceComputeSecGroupRuleDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "group_id": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "from_port": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: true, }, - "to_port": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: true, }, - "ip_protocol": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "cidr": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "from_group_id": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -55,7 +58,12 @@ func resourceComputeSecGroupRule() *schema.Resource { func resourceComputeSecGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } createOpts := secgroups.CreateRuleOpts{ ParentGroupID: d.Get("group_id").(string), @@ -66,7 +74,7 @@ func resourceComputeSecGroupRuleCreate(d *schema.ResourceData, meta interface{}) FromGroupID: d.Get("from_group_id").(string), } - sgr, err := secgroups.CreateRule(osClient, createOpts).Extract() + sgr, err := secgroups.CreateRule(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack security group rule: %s", err) } @@ -88,9 +96,14 @@ func resourceComputeSecGroupRuleRead(d *schema.ResourceData, meta interface{}) e func resourceComputeSecGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.computeV2Client + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } - err := secgroups.DeleteRule(osClient, d.Id()).ExtractErr() + err = secgroups.DeleteRule(computeClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack security group rule: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_member.go b/builtin/providers/openstack/resource_openstack_lb_member_v1.go similarity index 65% rename from builtin/providers/openstack/resource_openstack_lb_member.go rename to builtin/providers/openstack/resource_openstack_lb_member_v1.go index c628ee2b4..fdad89a00 100644 --- a/builtin/providers/openstack/resource_openstack_lb_member.go +++ b/builtin/providers/openstack/resource_openstack_lb_member_v1.go @@ -5,6 +5,8 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" ) @@ -16,30 +18,32 @@ func resourceLBMember() *schema.Resource { Delete: resourceLBMemberDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "address": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "port": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: true, }, - "pool_id": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "admin_state_up": &schema.Schema{ Type: schema.TypeBool, Required: true, @@ -51,7 +55,12 @@ func resourceLBMember() *schema.Resource { func resourceLBMemberCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } createOpts := members.CreateOpts{ //TenantID: d.Get("tenant_id").(string), @@ -61,7 +70,7 @@ func resourceLBMemberCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[INFO] Requesting lb member creation") - p, err := members.Create(osClient, createOpts).Extract() + p, err := members.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB member: %s", err) } @@ -74,9 +83,14 @@ func resourceLBMemberCreate(d *schema.ResourceData, meta interface{}) error { func resourceLBMemberRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - p, err := members.Get(osClient, d.Id()).Extract() + p, err := members.Get(networkingClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack LB Member: %s", err) } @@ -100,7 +114,12 @@ func resourceLBMemberRead(d *schema.ResourceData, meta interface{}) error { func resourceLBMemberUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } var updateOpts members.UpdateOpts if d.HasChange("admin_state_up") { @@ -109,7 +128,7 @@ func resourceLBMemberUpdate(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Updating OpenStack LB Member %s with options: %+v", d.Id(), updateOpts) - _, err := members.Update(osClient, d.Id(), updateOpts).Extract() + _, err = members.Update(networkingClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack LB Member: %s", err) } @@ -119,9 +138,14 @@ func resourceLBMemberUpdate(d *schema.ResourceData, meta interface{}) error { func resourceLBMemberDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - err := members.Delete(osClient, d.Id()).ExtractErr() + err = members.Delete(networkingClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack LB Member: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go similarity index 78% rename from builtin/providers/openstack/resource_openstack_lb_monitor.go rename to builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index b2cb98641..cdf380319 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -6,6 +6,8 @@ import ( "strconv" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" ) @@ -17,54 +19,52 @@ func resourceLBMonitor() *schema.Resource { Delete: resourceLBMonitorDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "type": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "delay": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: false, }, - "timeout": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: false, }, - "max_retries": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: false, }, - "url_path": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "http_method": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "expected_codes": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "admin_state_up": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -76,7 +76,12 @@ func resourceLBMonitor() *schema.Resource { func resourceLBMonitorCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } createOpts := monitors.CreateOpts{ TenantID: d.Get("tenant_id").(string), @@ -99,7 +104,7 @@ func resourceLBMonitorCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[INFO] Requesting lb monitor creation") - m, err := monitors.Create(osClient, createOpts).Extract() + m, err := monitors.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB Monitor: %s", err) } @@ -112,9 +117,14 @@ func resourceLBMonitorCreate(d *schema.ResourceData, meta interface{}) error { func resourceLBMonitorRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - m, err := monitors.Get(osClient, d.Id()).Extract() + m, err := monitors.Get(networkingClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack LB Monitor: %s", err) } @@ -171,7 +181,12 @@ func resourceLBMonitorRead(d *schema.ResourceData, meta interface{}) error { func resourceLBMonitorUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } var updateOpts monitors.UpdateOpts if d.HasChange("delay") { @@ -205,7 +220,7 @@ func resourceLBMonitorUpdate(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Updating OpenStack LB Monitor %s with options: %+v", d.Id(), updateOpts) - _, err := monitors.Update(osClient, d.Id(), updateOpts).Extract() + _, err = monitors.Update(networkingClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack LB Monitor: %s", err) } @@ -215,9 +230,14 @@ func resourceLBMonitorUpdate(d *schema.ResourceData, meta interface{}) error { func resourceLBMonitorDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - err := monitors.Delete(osClient, d.Id()).ExtractErr() + err = monitors.Delete(networkingClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack LB Monitor: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_pool.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go similarity index 72% rename from builtin/providers/openstack/resource_openstack_lb_pool.go rename to builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 0fb42463e..180bcb8fd 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -5,7 +5,9 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" - "github.com/jrperritt/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" ) @@ -17,18 +19,22 @@ func resourceLBPool() *schema.Resource { Delete: resourceLBPoolDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "protocol": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "subnet_id": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -40,13 +46,11 @@ func resourceLBPool() *schema.Resource { Required: true, ForceNew: false, }, - "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "monitor_ids": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -62,7 +66,12 @@ func resourceLBPool() *schema.Resource { func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } createOpts := pools.CreateOpts{ Name: d.Get("name").(string), @@ -73,7 +82,7 @@ func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[INFO] Requesting lb pool creation") - p, err := pools.Create(osClient, createOpts).Extract() + p, err := pools.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB pool: %s", err) } @@ -83,7 +92,7 @@ func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { if mIDs := resourcePoolMonitorIDs(d); mIDs != nil { for _, mID := range mIDs { - _, err := pools.AssociateMonitor(osClient, p.ID, mID).Extract() + _, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract() if err != nil { return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err) } @@ -95,9 +104,14 @@ func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { func resourceLBPoolRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - p, err := pools.Get(osClient, d.Id()).Extract() + p, err := pools.Get(networkingClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack LB Pool: %s", err) } @@ -124,7 +138,12 @@ func resourceLBPoolRead(d *schema.ResourceData, meta interface{}) error { func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } var updateOpts pools.UpdateOpts if d.HasChange("name") { @@ -136,7 +155,7 @@ func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts) - _, err := pools.Update(osClient, d.Id(), updateOpts).Extract() + _, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack LB Pool: %s", err) } @@ -152,7 +171,7 @@ func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove) for _, m := range monitorsToAdd.List() { - _, err := pools.AssociateMonitor(osClient, d.Id(), m.(string)).Extract() + _, err := pools.AssociateMonitor(networkingClient, d.Id(), m.(string)).Extract() if err != nil { return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err) } @@ -160,7 +179,7 @@ func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { } for _, m := range monitorsToRemove.List() { - _, err := pools.DisassociateMonitor(osClient, d.Id(), m.(string)).Extract() + _, err := pools.DisassociateMonitor(networkingClient, d.Id(), m.(string)).Extract() if err != nil { return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err) } @@ -173,9 +192,14 @@ func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { func resourceLBPoolDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - err := pools.Delete(osClient, d.Id()).ExtractErr() + err = pools.Delete(networkingClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_vip.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go similarity index 80% rename from builtin/providers/openstack/resource_openstack_lb_vip.go rename to builtin/providers/openstack/resource_openstack_lb_vip_v1.go index 405d01a19..ad95767cd 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" ) @@ -18,66 +19,62 @@ func resourceLBVip() *schema.Resource { Delete: resourceLBVipDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "name": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "subnet_id": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "protocol": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "port": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: true, }, - "pool_id": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, }, - "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "address": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "description": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "persistence": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "conn_limit": &schema.Schema{ Type: schema.TypeInt, Optional: true, ForceNew: false, }, - "admin_state_up": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -89,7 +86,12 @@ func resourceLBVip() *schema.Resource { func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } createOpts := vips.CreateOpts{ Name: d.Get("name").(string), @@ -114,7 +116,7 @@ func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[INFO] Requesting lb vip creation") - p, err := vips.Create(osClient, createOpts).Extract() + p, err := vips.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB VIP: %s", err) } @@ -127,9 +129,14 @@ func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { func resourceLBVipRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - p, err := vips.Get(osClient, d.Id()).Extract() + p, err := vips.Get(networkingClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack LB VIP: %s", err) } @@ -193,7 +200,12 @@ func resourceLBVipRead(d *schema.ResourceData, meta interface{}) error { func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } var updateOpts vips.UpdateOpts if d.HasChange("name") { @@ -224,7 +236,7 @@ func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts) - _, err := vips.Update(osClient, d.Id(), updateOpts).Extract() + _, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack LB VIP: %s", err) } @@ -234,9 +246,14 @@ func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { func resourceLBVipDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - err := vips.Delete(osClient, d.Id()).ExtractErr() + err = vips.Delete(networkingClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_networking_network.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go similarity index 74% rename from builtin/providers/openstack/resource_openstack_networking_network.go rename to builtin/providers/openstack/resource_openstack_networking_network_v2.go index 8f262e369..3ce51fbb0 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -6,6 +6,8 @@ import ( "strconv" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/networks" ) @@ -17,24 +19,27 @@ func resourceNetworkingNetwork() *schema.Resource { Delete: resourceNetworkingNetworkDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "name": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "admin_state_up": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "shared": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -46,7 +51,12 @@ func resourceNetworkingNetwork() *schema.Resource { func resourceNetworkingNetworkCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } createOpts := networks.CreateOpts{ Name: d.Get("name").(string), @@ -72,7 +82,7 @@ func resourceNetworkingNetworkCreate(d *schema.ResourceData, meta interface{}) e } log.Printf("[INFO] Requesting network creation") - n, err := networks.Create(osClient, createOpts).Extract() + n, err := networks.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron network: %s", err) } @@ -85,9 +95,14 @@ func resourceNetworkingNetworkCreate(d *schema.ResourceData, meta interface{}) e func resourceNetworkingNetworkRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - n, err := networks.Get(osClient, d.Id()).Extract() + n, err := networks.Get(networkingClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack Neutron Network: %s", err) } @@ -131,7 +146,12 @@ func resourceNetworkingNetworkRead(d *schema.ResourceData, meta interface{}) err func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } var updateOpts networks.UpdateOpts if d.HasChange("name") { @@ -160,7 +180,7 @@ func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts) - _, err := networks.Update(osClient, d.Id(), updateOpts).Extract() + _, err = networks.Update(networkingClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) } @@ -170,9 +190,14 @@ func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) e func resourceNetworkingNetworkDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - err := networks.Delete(osClient, d.Id()).ExtractErr() + err = networks.Delete(networkingClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go similarity index 82% rename from builtin/providers/openstack/resource_openstack_networking_subnet.go rename to builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index 0f52c0be2..ab337ec30 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -6,7 +6,9 @@ import ( "strconv" "github.com/hashicorp/terraform/helper/schema" - "github.com/jrperritt/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" ) @@ -18,30 +20,32 @@ func resourceNetworkingSubnet() *schema.Resource { Delete: resourceNetworkingSubnetDelete, Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, "network_id": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "cidr": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, - "name": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, - "allocation_pools": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -52,7 +56,6 @@ func resourceNetworkingSubnet() *schema.Resource { Type: schema.TypeString, Required: true, }, - "end": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -60,25 +63,21 @@ func resourceNetworkingSubnet() *schema.Resource { }, }, }, - "gateway_ip": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "ip_version": &schema.Schema{ Type: schema.TypeInt, Required: true, ForceNew: true, }, - "enable_dhcp": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, }, - "dns_nameservers": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -88,7 +87,6 @@ func resourceNetworkingSubnet() *schema.Resource { return hashcode.String(v.(string)) }, }, - "host_routes": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -99,7 +97,6 @@ func resourceNetworkingSubnet() *schema.Resource { Type: schema.TypeString, Required: true, }, - "next_hop": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -113,7 +110,12 @@ func resourceNetworkingSubnet() *schema.Resource { func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } createOpts := subnets.CreateOpts{ NetworkID: d.Get("network_id").(string), @@ -137,7 +139,7 @@ func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) er } log.Printf("[INFO] Requesting subnet creation") - s, err := subnets.Create(osClient, createOpts).Extract() + s, err := subnets.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err) } @@ -150,9 +152,14 @@ func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) er func resourceNetworkingSubnetRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - s, err := subnets.Get(osClient, d.Id()).Extract() + s, err := subnets.Get(networkingClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error retrieving OpenStack Neutron Subnet: %s", err) } @@ -212,7 +219,12 @@ func resourceNetworkingSubnetRead(d *schema.ResourceData, meta interface{}) erro func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } var updateOpts subnets.UpdateOpts @@ -245,7 +257,7 @@ func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Updating Subnet %s with options: %+v", d.Id(), updateOpts) - _, err := subnets.Update(osClient, d.Id(), updateOpts).Extract() + _, err = subnets.Update(networkingClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack Neutron Subnet: %s", err) } @@ -255,9 +267,14 @@ func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) er func resourceNetworkingSubnetDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - osClient := config.networkingV2Client + networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ + Region: d.Get("region").(string), + }) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } - err := subnets.Delete(osClient, d.Id()).ExtractErr() + err = subnets.Delete(networkingClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack Neutron Subnet: %s", err) } From dc99dd1f051a5001896c0ba36cb6f79f7fbc932f Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 11:09:27 -0700 Subject: [PATCH 051/167] add versioning to files --- builtin/providers/openstack/provider.go | 20 ++++----- .../resource_openstack_compute_instance_v2.go | 44 +++++++++---------- .../resource_openstack_compute_keypair_v2.go | 16 +++---- .../resource_openstack_compute_secgroup_v2.go | 22 +++++----- ...ource_openstack_compute_secgrouprule_v2.go | 16 +++---- .../resource_openstack_lb_member_v1.go | 22 +++++----- .../resource_openstack_lb_monitor_v1.go | 22 +++++----- .../resource_openstack_lb_pool_v1.go | 26 +++++------ .../openstack/resource_openstack_lb_vip_v1.go | 42 ++++++++++-------- ...esource_openstack_networking_network_v2.go | 22 +++++----- ...resource_openstack_networking_subnet_v2.go | 38 ++++++++-------- 11 files changed, 148 insertions(+), 142 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 8b3e37a58..27a6321fa 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -59,16 +59,16 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "openstack_compute_instance": resourceComputeInstance(), - "openstack_compute_keypair": resourceComputeKeypair(), - "openstack_compute_secgroup": resourceComputeSecGroup(), - "openstack_compute_secgrouprule": resourceComputeSecGroupRule(), - "openstack_lb_member": resourceLBMember(), - "openstack_lb_monitor": resourceLBMonitor(), - "openstack_lb_pool": resourceLBPool(), - "openstack_lb_vip": resourceLBVip(), - "openstack_networking_network": resourceNetworkingNetwork(), - "openstack_networking_subnet": resourceNetworkingSubnet(), + "openstack_compute_instance_v2": resourceComputeInstanceV2(), + "openstack_compute_keypair_v2": resourceComputeKeypairV2(), + "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), + "openstack_compute_secgrouprule_v2": resourceComputeSecGroupRuleV2(), + "openstack_lb_member_v1": resourceLBMemberV1(), + "openstack_lb_monitor_v1": resourceLBMonitorV1(), + "openstack_lb_pool_v1": resourceLBPoolV1(), + "openstack_lb_vip_v1": resourceLBVipV1(), + "openstack_networking_network_v2": resourceNetworkingNetworkV2(), + "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), }, ConfigureFunc: configureProvider, diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index cf434566d..38c3b9af7 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -16,12 +16,12 @@ import ( "github.com/rackspace/gophercloud/pagination" ) -func resourceComputeInstance() *schema.Resource { +func resourceComputeInstanceV2() *schema.Resource { return &schema.Resource{ - Create: resourceComputeInstanceCreate, - Read: resourceComputeInstanceRead, - Update: resourceComputeInstanceUpdate, - Delete: resourceComputeInstanceDelete, + Create: resourceComputeInstanceV2Create, + Read: resourceComputeInstanceV2Read, + Update: resourceComputeInstanceV2Update, + Delete: resourceComputeInstanceV2Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -116,7 +116,7 @@ func resourceComputeInstance() *schema.Resource { } } -func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -132,10 +132,10 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err Name: d.Get("name").(string), ImageRef: d.Get("image_ref").(string), FlavorRef: d.Get("flavor_ref").(string), - SecurityGroups: resourceInstanceSecGroups(d), + SecurityGroups: resourceInstanceSecGroupsV2(d), AvailabilityZone: d.Get("availability_zone").(string), - Networks: resourceInstanceNetworks(d), - Metadata: resourceInstanceMetadata(d), + Networks: resourceInstanceNetworksV2(d), + Metadata: resourceInstanceMetadataV2(d), ConfigDrive: d.Get("config_drive").(bool), AdminPass: d.Get("admin_pass").(string), } @@ -166,7 +166,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err stateConf := &resource.StateChangeConf{ Pending: []string{"BUILD"}, Target: "ACTIVE", - Refresh: ServerStateRefreshFunc(computeClient, server.ID), + Refresh: ServerV2StateRefreshFunc(computeClient, server.ID), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -179,10 +179,10 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err server.ID, err) } - return resourceComputeInstanceRead(d, meta) + return resourceComputeInstanceV2Read(d, meta) } -func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error { +func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -251,7 +251,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -343,7 +343,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err stateConf := &resource.StateChangeConf{ Pending: []string{"RESIZE"}, Target: "VERIFY_RESIZE", - Refresh: ServerStateRefreshFunc(computeClient, d.Id()), + Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), Timeout: 3 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -364,7 +364,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err stateConf = &resource.StateChangeConf{ Pending: []string{"VERIFY_RESIZE"}, Target: "ACTIVE", - Refresh: ServerStateRefreshFunc(computeClient, d.Id()), + Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), Timeout: 3 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -376,10 +376,10 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } - return resourceComputeInstanceRead(d, meta) + return resourceComputeInstanceV2Read(d, meta) } -func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error { +func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -398,7 +398,7 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err stateConf := &resource.StateChangeConf{ Target: "", - Refresh: ServerStateRefreshFunc(computeClient, d.Id()), + Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -417,7 +417,7 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err // ServerStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // an OpenStack instance. -func ServerStateRefreshFunc(client *gophercloud.ServiceClient, instanceID string) resource.StateRefreshFunc { +func ServerV2StateRefreshFunc(client *gophercloud.ServiceClient, instanceID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { s, err := servers.Get(client, instanceID).Extract() if err != nil { @@ -428,7 +428,7 @@ func ServerStateRefreshFunc(client *gophercloud.ServiceClient, instanceID string } } -func resourceInstanceSecGroups(d *schema.ResourceData) []string { +func resourceInstanceSecGroupsV2(d *schema.ResourceData) []string { rawSecGroups := d.Get("security_groups").(*schema.Set) secgroups := make([]string, rawSecGroups.Len()) for i, raw := range rawSecGroups.List() { @@ -437,7 +437,7 @@ func resourceInstanceSecGroups(d *schema.ResourceData) []string { return secgroups } -func resourceInstanceNetworks(d *schema.ResourceData) []servers.Network { +func resourceInstanceNetworksV2(d *schema.ResourceData) []servers.Network { rawNetworks := d.Get("networks").([]interface{}) networks := make([]servers.Network, len(rawNetworks)) for i, raw := range rawNetworks { @@ -451,7 +451,7 @@ func resourceInstanceNetworks(d *schema.ResourceData) []servers.Network { return networks } -func resourceInstanceMetadata(d *schema.ResourceData) map[string]string { +func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string { m := make(map[string]string) for key, val := range d.Get("metadata").(map[string]interface{}) { m[key] = val.(string) diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index d27a348d4..6fdaef631 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -9,11 +9,11 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" ) -func resourceComputeKeypair() *schema.Resource { +func resourceComputeKeypairV2() *schema.Resource { return &schema.Resource{ - Create: resourceComputeKeypairCreate, - Read: resourceComputeKeypairRead, - Delete: resourceComputeKeypairDelete, + Create: resourceComputeKeypairV2Create, + Read: resourceComputeKeypairV2Read, + Delete: resourceComputeKeypairV2Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -36,7 +36,7 @@ func resourceComputeKeypair() *schema.Resource { } } -func resourceComputeKeypairCreate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeKeypairV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -57,10 +57,10 @@ func resourceComputeKeypairCreate(d *schema.ResourceData, meta interface{}) erro d.SetId(kp.Name) - return resourceComputeKeypairRead(d, meta) + return resourceComputeKeypairV2Read(d, meta) } -func resourceComputeKeypairRead(d *schema.ResourceData, meta interface{}) error { +func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -80,7 +80,7 @@ func resourceComputeKeypairRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceComputeKeypairDelete(d *schema.ResourceData, meta interface{}) error { +func resourceComputeKeypairV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 559f96cbf..d6802e99d 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -10,12 +10,12 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) -func resourceComputeSecGroup() *schema.Resource { +func resourceComputeSecGroupV2() *schema.Resource { return &schema.Resource{ - Create: resourceComputeSecGroupCreate, - Read: resourceComputeSecGroupRead, - Update: resourceComputeSecGroupUpdate, - Delete: resourceComputeSecGroupDelete, + Create: resourceComputeSecGroupV2Create, + Read: resourceComputeSecGroupV2Read, + Update: resourceComputeSecGroupV2Update, + Delete: resourceComputeSecGroupV2Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -38,7 +38,7 @@ func resourceComputeSecGroup() *schema.Resource { } } -func resourceComputeSecGroupCreate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -59,10 +59,10 @@ func resourceComputeSecGroupCreate(d *schema.ResourceData, meta interface{}) err d.SetId(sg.ID) - return resourceComputeSecGroupRead(d, meta) + return resourceComputeSecGroupV2Read(d, meta) } -func resourceComputeSecGroupRead(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -82,7 +82,7 @@ func resourceComputeSecGroupRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceComputeSecGroupUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -106,10 +106,10 @@ func resourceComputeSecGroupUpdate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err) } - return resourceComputeSecGroupRead(d, meta) + return resourceComputeSecGroupV2Read(d, meta) } -func resourceComputeSecGroupDelete(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), diff --git a/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go index 77c14f6f8..9a890d673 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go @@ -9,11 +9,11 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) -func resourceComputeSecGroupRule() *schema.Resource { +func resourceComputeSecGroupRuleV2() *schema.Resource { return &schema.Resource{ - Create: resourceComputeSecGroupRuleCreate, - Read: resourceComputeSecGroupRuleRead, - Delete: resourceComputeSecGroupRuleDelete, + Create: resourceComputeSecGroupRuleV2Create, + Read: resourceComputeSecGroupRuleV2Read, + Delete: resourceComputeSecGroupRuleV2Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -56,7 +56,7 @@ func resourceComputeSecGroupRule() *schema.Resource { } } -func resourceComputeSecGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupRuleV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -87,14 +87,14 @@ func resourceComputeSecGroupRuleCreate(d *schema.ResourceData, meta interface{}) d.Set("cidr", sgr.IPRange.CIDR) d.Set("from_group_id", d.Get("from_group_id").(string)) - return resourceComputeSecGroupRuleRead(d, meta) + return resourceComputeSecGroupRuleV2Read(d, meta) } -func resourceComputeSecGroupRuleRead(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupRuleV2Read(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceComputeSecGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { +func resourceComputeSecGroupRuleV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), diff --git a/builtin/providers/openstack/resource_openstack_lb_member_v1.go b/builtin/providers/openstack/resource_openstack_lb_member_v1.go index fdad89a00..0a6f478b7 100644 --- a/builtin/providers/openstack/resource_openstack_lb_member_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_member_v1.go @@ -10,12 +10,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" ) -func resourceLBMember() *schema.Resource { +func resourceLBMemberV1() *schema.Resource { return &schema.Resource{ - Create: resourceLBMemberCreate, - Read: resourceLBMemberRead, - Update: resourceLBMemberUpdate, - Delete: resourceLBMemberDelete, + Create: resourceLBMemberV1Create, + Read: resourceLBMemberV1Read, + Update: resourceLBMemberV1Update, + Delete: resourceLBMemberV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -53,7 +53,7 @@ func resourceLBMember() *schema.Resource { } } -func resourceLBMemberCreate(d *schema.ResourceData, meta interface{}) error { +func resourceLBMemberV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -78,10 +78,10 @@ func resourceLBMemberCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(p.ID) - return resourceLBMemberRead(d, meta) + return resourceLBMemberV1Read(d, meta) } -func resourceLBMemberRead(d *schema.ResourceData, meta interface{}) error { +func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -112,7 +112,7 @@ func resourceLBMemberRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceLBMemberUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceLBMemberV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -133,10 +133,10 @@ func resourceLBMemberUpdate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error updating OpenStack LB Member: %s", err) } - return resourceLBMemberRead(d, meta) + return resourceLBMemberV1Read(d, meta) } -func resourceLBMemberDelete(d *schema.ResourceData, meta interface{}) error { +func resourceLBMemberV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index cdf380319..9d82e7e26 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -11,12 +11,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" ) -func resourceLBMonitor() *schema.Resource { +func resourceLBMonitorV1() *schema.Resource { return &schema.Resource{ - Create: resourceLBMonitorCreate, - Read: resourceLBMonitorRead, - Update: resourceLBMonitorUpdate, - Delete: resourceLBMonitorDelete, + Create: resourceLBMonitorV1Create, + Read: resourceLBMonitorV1Read, + Update: resourceLBMonitorV1Update, + Delete: resourceLBMonitorV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -74,7 +74,7 @@ func resourceLBMonitor() *schema.Resource { } } -func resourceLBMonitorCreate(d *schema.ResourceData, meta interface{}) error { +func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -112,10 +112,10 @@ func resourceLBMonitorCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(m.ID) - return resourceLBMonitorRead(d, meta) + return resourceLBMonitorV1Read(d, meta) } -func resourceLBMonitorRead(d *schema.ResourceData, meta interface{}) error { +func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -179,7 +179,7 @@ func resourceLBMonitorRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceLBMonitorUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -225,10 +225,10 @@ func resourceLBMonitorUpdate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error updating OpenStack LB Monitor: %s", err) } - return resourceLBMonitorRead(d, meta) + return resourceLBMonitorV1Read(d, meta) } -func resourceLBMonitorDelete(d *schema.ResourceData, meta interface{}) error { +func resourceLBMonitorV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 180bcb8fd..c5275c6e8 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -11,12 +11,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" ) -func resourceLBPool() *schema.Resource { +func resourceLBPoolV1() *schema.Resource { return &schema.Resource{ - Create: resourceLBPoolCreate, - Read: resourceLBPoolRead, - Update: resourceLBPoolUpdate, - Delete: resourceLBPoolDelete, + Create: resourceLBPoolV1Create, + Read: resourceLBPoolV1Read, + Update: resourceLBPoolV1Update, + Delete: resourceLBPoolV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -64,7 +64,7 @@ func resourceLBPool() *schema.Resource { } } -func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { +func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -90,7 +90,7 @@ func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(p.ID) - if mIDs := resourcePoolMonitorIDs(d); mIDs != nil { + if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil { for _, mID := range mIDs { _, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract() if err != nil { @@ -99,10 +99,10 @@ func resourceLBPoolCreate(d *schema.ResourceData, meta interface{}) error { } } - return resourceLBPoolRead(d, meta) + return resourceLBPoolV1Read(d, meta) } -func resourceLBPoolRead(d *schema.ResourceData, meta interface{}) error { +func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -136,7 +136,7 @@ func resourceLBPoolRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -187,10 +187,10 @@ func resourceLBPoolUpdate(d *schema.ResourceData, meta interface{}) error { } } - return resourceLBPoolRead(d, meta) + return resourceLBPoolV1Read(d, meta) } -func resourceLBPoolDelete(d *schema.ResourceData, meta interface{}) error { +func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -208,7 +208,7 @@ func resourceLBPoolDelete(d *schema.ResourceData, meta interface{}) error { return nil } -func resourcePoolMonitorIDs(d *schema.ResourceData) []string { +func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string { mIDsRaw := d.Get("monitor_ids").(*schema.Set) mIDs := make([]string, mIDsRaw.Len()) for i, raw := range mIDsRaw.List() { diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index ad95767cd..09c63bc41 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -11,12 +11,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" ) -func resourceLBVip() *schema.Resource { +func resourceLBVipV1() *schema.Resource { return &schema.Resource{ - Create: resourceLBVipCreate, - Read: resourceLBVipRead, - Update: resourceLBVipUpdate, - Delete: resourceLBVipDelete, + Create: resourceLBVipV1Create, + Read: resourceLBVipV1Read, + Update: resourceLBVipV1Update, + Delete: resourceLBVipV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -84,7 +84,7 @@ func resourceLBVip() *schema.Resource { } } -func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { +func resourceLBVipV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -102,7 +102,7 @@ func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { TenantID: d.Get("tenant_id").(string), Address: d.Get("address").(string), Description: d.Get("description").(string), - Persistence: resourceVipPersistence(d), + Persistence: resourceVipPersistenceV1(d), ConnLimit: gophercloud.MaybeInt(d.Get("conn_limit").(int)), } @@ -124,10 +124,10 @@ func resourceLBVipCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(p.ID) - return resourceLBVipRead(d, meta) + return resourceLBVipV1Read(d, meta) } -func resourceLBVipRead(d *schema.ResourceData, meta interface{}) error { +func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -198,7 +198,7 @@ func resourceLBVipRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -218,7 +218,7 @@ func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { updateOpts.Description = d.Get("description").(string) } if d.HasChange("persistence") { - updateOpts.Persistence = resourceVipPersistence(d) + updateOpts.Persistence = resourceVipPersistenceV1(d) } if d.HasChange("conn_limit") { updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int)) @@ -241,10 +241,10 @@ func resourceLBVipUpdate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error updating OpenStack LB VIP: %s", err) } - return resourceLBVipRead(d, meta) + return resourceLBVipV1Read(d, meta) } -func resourceLBVipDelete(d *schema.ResourceData, meta interface{}) error { +func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -262,12 +262,18 @@ func resourceLBVipDelete(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceVipPersistence(d *schema.ResourceData) *vips.SessionPersistence { +func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence { rawP := d.Get("persistence").(interface{}) rawMap := rawP.(map[string]interface{}) - p := vips.SessionPersistence{ - Type: rawMap["type"].(string), - CookieName: rawMap["cookie_name"].(string), + if rawMap != nil { + p := vips.SessionPersistence{} + if t, ok := rawMap["type"]; ok { + p.Type = t.(string) + } + if c, ok := rawMap["cookie_name"]; ok { + p.CookieName = c.(string) + } + return &p } - return &p + return nil } diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index 3ce51fbb0..3c33315c7 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -11,12 +11,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/networks" ) -func resourceNetworkingNetwork() *schema.Resource { +func resourceNetworkingNetworkV2() *schema.Resource { return &schema.Resource{ - Create: resourceNetworkingNetworkCreate, - Read: resourceNetworkingNetworkRead, - Update: resourceNetworkingNetworkUpdate, - Delete: resourceNetworkingNetworkDelete, + Create: resourceNetworkingNetworkV2Create, + Read: resourceNetworkingNetworkV2Read, + Update: resourceNetworkingNetworkV2Update, + Delete: resourceNetworkingNetworkV2Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -49,7 +49,7 @@ func resourceNetworkingNetwork() *schema.Resource { } } -func resourceNetworkingNetworkCreate(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -90,10 +90,10 @@ func resourceNetworkingNetworkCreate(d *schema.ResourceData, meta interface{}) e d.SetId(n.ID) - return resourceNetworkingNetworkRead(d, meta) + return resourceNetworkingNetworkV2Read(d, meta) } -func resourceNetworkingNetworkRead(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -144,7 +144,7 @@ func resourceNetworkingNetworkRead(d *schema.ResourceData, meta interface{}) err return nil } -func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -185,10 +185,10 @@ func resourceNetworkingNetworkUpdate(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err) } - return resourceNetworkingNetworkRead(d, meta) + return resourceNetworkingNetworkV2Read(d, meta) } -func resourceNetworkingNetworkDelete(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingNetworkV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index ab337ec30..af25c4497 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -12,12 +12,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" ) -func resourceNetworkingSubnet() *schema.Resource { +func resourceNetworkingSubnetV2() *schema.Resource { return &schema.Resource{ - Create: resourceNetworkingSubnetCreate, - Read: resourceNetworkingSubnetRead, - Update: resourceNetworkingSubnetUpdate, - Delete: resourceNetworkingSubnetDelete, + Create: resourceNetworkingSubnetV2Create, + Read: resourceNetworkingSubnetV2Read, + Update: resourceNetworkingSubnetV2Update, + Delete: resourceNetworkingSubnetV2Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -108,7 +108,7 @@ func resourceNetworkingSubnet() *schema.Resource { } } -func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -122,11 +122,11 @@ func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) er CIDR: d.Get("cidr").(string), Name: d.Get("name").(string), TenantID: d.Get("tenant_id").(string), - AllocationPools: resourceSubnetAllocationPools(d), + AllocationPools: resourceSubnetAllocationPoolsV2(d), GatewayIP: d.Get("gateway_ip").(string), IPVersion: d.Get("ip_version").(int), - DNSNameservers: resourceSubnetDNSNameservers(d), - HostRoutes: resourceSubnetHostRoutes(d), + DNSNameservers: resourceSubnetDNSNameserversV2(d), + HostRoutes: resourceSubnetHostRoutesV2(d), } edRaw := d.Get("enable_dhcp").(string) @@ -147,10 +147,10 @@ func resourceNetworkingSubnetCreate(d *schema.ResourceData, meta interface{}) er d.SetId(s.ID) - return resourceNetworkingSubnetRead(d, meta) + return resourceNetworkingSubnetV2Read(d, meta) } -func resourceNetworkingSubnetRead(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -217,7 +217,7 @@ func resourceNetworkingSubnetRead(d *schema.ResourceData, meta interface{}) erro return nil } -func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -237,11 +237,11 @@ func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) er } if d.HasChange("dns_nameservers") { - updateOpts.DNSNameservers = resourceSubnetDNSNameservers(d) + updateOpts.DNSNameservers = resourceSubnetDNSNameserversV2(d) } if d.HasChange("host_routes") { - updateOpts.HostRoutes = resourceSubnetHostRoutes(d) + updateOpts.HostRoutes = resourceSubnetHostRoutesV2(d) } if d.HasChange("enable_dhcp") { @@ -262,10 +262,10 @@ func resourceNetworkingSubnetUpdate(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Error updating OpenStack Neutron Subnet: %s", err) } - return resourceNetworkingSubnetRead(d, meta) + return resourceNetworkingSubnetV2Read(d, meta) } -func resourceNetworkingSubnetDelete(d *schema.ResourceData, meta interface{}) error { +func resourceNetworkingSubnetV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ Region: d.Get("region").(string), @@ -283,7 +283,7 @@ func resourceNetworkingSubnetDelete(d *schema.ResourceData, meta interface{}) er return nil } -func resourceSubnetAllocationPools(d *schema.ResourceData) []subnets.AllocationPool { +func resourceSubnetAllocationPoolsV2(d *schema.ResourceData) []subnets.AllocationPool { rawAPs := d.Get("allocation_pools").([]interface{}) aps := make([]subnets.AllocationPool, len(rawAPs)) for i, raw := range rawAPs { @@ -296,7 +296,7 @@ func resourceSubnetAllocationPools(d *schema.ResourceData) []subnets.AllocationP return aps } -func resourceSubnetDNSNameservers(d *schema.ResourceData) []string { +func resourceSubnetDNSNameserversV2(d *schema.ResourceData) []string { rawDNSN := d.Get("dns_nameservers").(*schema.Set) dnsn := make([]string, rawDNSN.Len()) for i, raw := range rawDNSN.List() { @@ -305,7 +305,7 @@ func resourceSubnetDNSNameservers(d *schema.ResourceData) []string { return dnsn } -func resourceSubnetHostRoutes(d *schema.ResourceData) []subnets.HostRoute { +func resourceSubnetHostRoutesV2(d *schema.ResourceData) []subnets.HostRoute { rawHR := d.Get("host_routes").([]interface{}) hr := make([]subnets.HostRoute, len(rawHR)) for i, raw := range rawHR { From 9b54c569cc2b20f0eb5a6a3c0f268d5ff2f6aa68 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 11:38:02 -0700 Subject: [PATCH 052/167] use 'Default' to forgo prompt --- builtin/providers/openstack/provider.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 27a6321fa..53f63f5d7 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -24,17 +24,17 @@ func Provider() terraform.ResourceProvider { "user_id": &schema.Schema{ Type: schema.TypeString, Optional: true, - DefaultFunc: envDefaultFunc("OS_USERID"), + Default: "", }, "tenant_id": &schema.Schema{ Type: schema.TypeString, Optional: true, - DefaultFunc: envDefaultFunc("OS_TENANT_ID"), + Default: "", }, "tenant_name": &schema.Schema{ Type: schema.TypeString, Optional: true, - DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), + Default: "", }, "password": &schema.Schema{ Type: schema.TypeString, @@ -44,17 +44,17 @@ func Provider() terraform.ResourceProvider { "api_key": &schema.Schema{ Type: schema.TypeString, Optional: true, - DefaultFunc: envDefaultFunc("OS_API_KEY"), + Default: "", }, "domain_id": &schema.Schema{ Type: schema.TypeString, Optional: true, - DefaultFunc: envDefaultFunc("OS_DOMAIN_ID"), + Default: "", }, "domain_name": &schema.Schema{ Type: schema.TypeString, Optional: true, - DefaultFunc: envDefaultFunc("OS_DOMAIN_NAME"), + Default: "", }, }, From 46a7949c9d01a4e3daab631fb029b0c09451ccd0 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 11:38:33 -0700 Subject: [PATCH 053/167] fix bug in SessionPersistence logic --- builtin/providers/openstack/resource_openstack_lb_vip_v1.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index 09c63bc41..9c49b3276 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -66,7 +66,7 @@ func resourceLBVipV1() *schema.Resource { ForceNew: false, }, "persistence": &schema.Schema{ - Type: schema.TypeString, + Type: schema.TypeMap, Optional: true, ForceNew: false, }, @@ -265,7 +265,7 @@ func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error { func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence { rawP := d.Get("persistence").(interface{}) rawMap := rawP.(map[string]interface{}) - if rawMap != nil { + if len(rawMap) != 0 { p := vips.SessionPersistence{} if t, ok := rawMap["type"]; ok { p.Type = t.(string) From 2214331b3cbdefffd324d6d23563b8aef170fe28 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 11:58:19 -0700 Subject: [PATCH 054/167] export 'region' from 'Read' operations --- .../openstack/resource_openstack_compute_instance_v2.go | 1 + .../openstack/resource_openstack_compute_keypair_v2.go | 1 + .../openstack/resource_openstack_compute_secgroup_v2.go | 1 + .../openstack/resource_openstack_compute_secgrouprule_v2.go | 1 + builtin/providers/openstack/resource_openstack_lb_member_v1.go | 1 + builtin/providers/openstack/resource_openstack_lb_monitor_v1.go | 1 + builtin/providers/openstack/resource_openstack_lb_pool_v1.go | 1 + builtin/providers/openstack/resource_openstack_lb_vip_v1.go | 1 + .../openstack/resource_openstack_networking_network_v2.go | 2 ++ .../openstack/resource_openstack_networking_subnet_v2.go | 1 + 10 files changed, 11 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 38c3b9af7..660679a0b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -198,6 +198,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err log.Printf("[DEBUG] Retreived Server %s: %+v", d.Id(), server) + d.Set("region", d.Get("region").(string)) d.Set("name", server.Name) d.Set("access_ip_v4", server.AccessIPv4) d.Set("access_ip_v6", server.AccessIPv6) diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index 6fdaef631..e2d189a07 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -74,6 +74,7 @@ func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Error retrieving OpenStack keypair: %s", err) } + d.Set("region", d.Get("region").(string)) d.Set("name", kp.Name) d.Set("public_key", kp.PublicKey) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index d6802e99d..c89686575 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -76,6 +76,7 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error retrieving OpenStack security group: %s", err) } + d.Set("region", d.Get("region").(string)) d.Set("name", sg.Name) d.Set("description", sg.Description) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go index 9a890d673..fce40e621 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go @@ -80,6 +80,7 @@ func resourceComputeSecGroupRuleV2Create(d *schema.ResourceData, meta interface{ } d.SetId(sgr.ID) + d.Set("region", d.Get("region").(string)) d.Set("group_id", sgr.ParentGroupID) d.Set("from_port", sgr.FromPort) d.Set("to_port", sgr.ToPort) diff --git a/builtin/providers/openstack/resource_openstack_lb_member_v1.go b/builtin/providers/openstack/resource_openstack_lb_member_v1.go index 0a6f478b7..6fab4df80 100644 --- a/builtin/providers/openstack/resource_openstack_lb_member_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_member_v1.go @@ -97,6 +97,7 @@ func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB Member %s: %+v", d.Id(), p) + d.Set("region", d.Get("region").(string)) d.Set("address", p.Address) d.Set("port", p.ProtocolPort) d.Set("pool_id", p.PoolID) diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index 9d82e7e26..7883aa7dc 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -131,6 +131,7 @@ func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m) + d.Set("region", d.Get("region").(string)) d.Set("type", m.Type) d.Set("delay", m.Delay) d.Set("timeout", m.Timeout) diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index c5275c6e8..219608eaf 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -118,6 +118,7 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p) + d.Set("region", d.Get("region").(string)) d.Set("name", p.Name) d.Set("protocol", p.Protocol) d.Set("subnet_id", p.SubnetID) diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index 9c49b3276..83ba829c5 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -143,6 +143,7 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p) + d.Set("region", d.Get("region").(string)) d.Set("name", p.Name) d.Set("subnet_id", p.SubnetID) d.Set("protocol", p.Protocol) diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index 3c33315c7..ebb95efa4 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -109,6 +109,8 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n) + d.Set("region", d.Get("region").(string)) + if _, exists := d.GetOk("name"); exists { if d.HasChange("name") { d.Set("name", n.Name) diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index af25c4497..d84f532db 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -166,6 +166,7 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Retreived Subnet %s: %+v", d.Id(), s) + d.Set("region", d.Get("region").(string)) d.Set("newtork_id", s.NetworkID) d.Set("cidr", s.CIDR) d.Set("ip_version", s.IPVersion) From d51ee3111e4b7b53d03537530857b2f9ef64be93 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 12:59:38 -0700 Subject: [PATCH 055/167] always need both name and description when updating --- .../openstack/resource_openstack_compute_secgroup_v2.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index c89686575..5f61c267f 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -92,12 +92,9 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error creating OpenStack compute client: %s", err) } - var updateOpts secgroups.UpdateOpts - if d.HasChange("name") { - updateOpts.Name = d.Get("name").(string) - } - if d.HasChange("description") { - updateOpts.Description = d.Get("description").(string) + updateOpts := secgroups.UpdateOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), } log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts) From 5d2fe153c0149bed505bb6fd817a563ea803e59e Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 15:45:05 -0700 Subject: [PATCH 056/167] go fmt --- builtin/providers/openstack/provider.go | 36 +++++++++---------- .../resource_openstack_compute_instance_v2.go | 22 +++++------- .../resource_openstack_compute_keypair_v2.go | 1 - .../resource_openstack_compute_secgroup_v2.go | 3 +- .../resource_openstack_lb_pool_v1.go | 2 +- ...esource_openstack_networking_network_v2.go | 7 ++-- ...resource_openstack_networking_subnet_v2.go | 3 +- 7 files changed, 32 insertions(+), 42 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 53f63f5d7..48029afc7 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -22,19 +22,19 @@ func Provider() terraform.ResourceProvider { DefaultFunc: envDefaultFunc("OS_USERNAME"), }, "user_id": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", }, "tenant_id": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", }, "tenant_name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", }, "password": &schema.Schema{ Type: schema.TypeString, @@ -42,19 +42,19 @@ func Provider() terraform.ResourceProvider { DefaultFunc: envDefaultFunc("OS_PASSWORD"), }, "api_key": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", }, "domain_id": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", }, "domain_name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "", + Type: schema.TypeString, + Optional: true, + Default: "", }, }, diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 660679a0b..5d87e2cb5 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -28,7 +28,6 @@ func resourceComputeInstanceV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, @@ -125,7 +124,6 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error creating OpenStack compute client: %s", err) } - var createOpts servers.CreateOptsBuilder serverCreateOpts := &servers.CreateOpts{ @@ -228,20 +226,16 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err d.Set("metadata", server.Metadata) - var currentSG []string err = secgroups.ListByServer(computeClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { secGrpList, err := secgroups.ExtractSecurityGroups(page) if err != nil { - return false, fmt.Errorf("Error setting security groups for OpenStack server: %s", err) + return false, fmt.Errorf("Error getting security groups for OpenStack server: %s", err) } - - for _, sg := range secGrpList { - currentSG = append(currentSG, sg.Name) + for i, sg := range secGrpList { + d.Set(fmt.Sprintf("security_groups.%d", i), sg.Name) } - return true, nil }) - d.Set("security_groups", currentSG) newFlavor, ok := server.Flavor["id"].(string) if !ok { @@ -272,11 +266,11 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e updateOpts.AccessIPv4 = d.Get("access_ip_v6").(string) } - log.Printf("[DEBUG] Updating Server %s with options: %+v", d.Id(), updateOpts) - - _, err = servers.Update(computeClient, d.Id(), updateOpts).Extract() - if err != nil { - return fmt.Errorf("Error updating OpenStack server: %s", err) + if updateOpts != (servers.UpdateOpts{}) { + _, err := servers.Update(computeClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack server: %s", err) + } } if d.HasChange("metadata") { diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index e2d189a07..11cca95ba 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -20,7 +20,6 @@ func resourceComputeKeypairV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 5f61c267f..7dcbb48fb 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -22,7 +22,6 @@ func resourceComputeSecGroupV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, @@ -93,7 +92,7 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e } updateOpts := secgroups.UpdateOpts{ - Name: d.Get("name").(string), + Name: d.Get("name").(string), Description: d.Get("description").(string), } diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 219608eaf..674110a8f 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -4,8 +4,8 @@ import ( "fmt" "log" - "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index ebb95efa4..3119baadd 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -20,10 +20,9 @@ func resourceNetworkingNetworkV2() *schema.Resource { Schema: map[string]*schema.Schema{ "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "name": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index d84f532db..ecb2a4ea3 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -5,8 +5,8 @@ import ( "log" "strconv" - "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" @@ -24,7 +24,6 @@ func resourceNetworkingSubnetV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "network_id": &schema.Schema{ Type: schema.TypeString, From d86cb6be1c540db76adf496a012a26d1a88d138b Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 18:28:01 -0700 Subject: [PATCH 057/167] fix diff bug in 'Read' functions --- .../resource_openstack_compute_instance_v2.go | 8 +++-- .../resource_openstack_compute_keypair_v2.go | 1 + .../resource_openstack_compute_secgroup_v2.go | 1 + .../resource_openstack_lb_member_v1.go | 6 ++-- .../resource_openstack_lb_monitor_v1.go | 30 ++++++---------- .../resource_openstack_lb_pool_v1.go | 6 ++-- .../openstack/resource_openstack_lb_vip_v1.go | 34 ++++++------------- ...esource_openstack_networking_network_v2.go | 25 +++++--------- ...resource_openstack_networking_subnet_v2.go | 25 +++++--------- 9 files changed, 51 insertions(+), 85 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 5d87e2cb5..f6a922f43 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -28,6 +28,7 @@ func resourceComputeInstanceV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, @@ -226,16 +227,19 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err d.Set("metadata", server.Metadata) + secGrpNum := 0 err = secgroups.ListByServer(computeClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { secGrpList, err := secgroups.ExtractSecurityGroups(page) if err != nil { return false, fmt.Errorf("Error getting security groups for OpenStack server: %s", err) } - for i, sg := range secGrpList { - d.Set(fmt.Sprintf("security_groups.%d", i), sg.Name) + for _, sg := range secGrpList { + d.Set(fmt.Sprintf("security_groups.%d", secGrpNum), sg.Name) + secGrpNum++ } return true, nil }) + d.Set("security_groups.#", secGrpNum) newFlavor, ok := server.Flavor["id"].(string) if !ok { diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index 11cca95ba..e2d189a07 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -20,6 +20,7 @@ func resourceComputeKeypairV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 7dcbb48fb..a79a6a88b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -22,6 +22,7 @@ func resourceComputeSecGroupV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/openstack/resource_openstack_lb_member_v1.go b/builtin/providers/openstack/resource_openstack_lb_member_v1.go index 6fab4df80..2d615ed76 100644 --- a/builtin/providers/openstack/resource_openstack_lb_member_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_member_v1.go @@ -102,10 +102,8 @@ func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { d.Set("port", p.ProtocolPort) d.Set("pool_id", p.PoolID) - if _, exists := d.GetOk("tenant_id"); exists { - if d.HasChange("tenant_id") { - d.Set("tenant_id", p.TenantID) - } + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", p.TenantID) } else { d.Set("tenant_id", "") } diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index 7883aa7dc..f6c097d23 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -137,42 +137,32 @@ func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { d.Set("timeout", m.Timeout) d.Set("max_retries", m.MaxRetries) - if _, exists := d.GetOk("tenant_id"); exists { - if d.HasChange("tenant_id") { - d.Set("tenant_id", m.TenantID) - } + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", m.TenantID) } else { d.Set("tenant_id", "") } - if _, exists := d.GetOk("url_path"); exists { - if d.HasChange("url_path") { - d.Set("url_path", m.URLPath) - } + if t, exists := d.GetOk("url_path"); exists && t != "" { + d.Set("url_path", m.URLPath) } else { d.Set("url_path", "") } - if _, exists := d.GetOk("http_method"); exists { - if d.HasChange("http_method") { - d.Set("http_method", m.HTTPMethod) - } + if t, exists := d.GetOk("http_method"); exists && t != "" { + d.Set("http_method", m.HTTPMethod) } else { d.Set("http_method", "") } - if _, exists := d.GetOk("expected_codes"); exists { - if d.HasChange("expected_codes") { - d.Set("expected_codes", m.ExpectedCodes) - } + if t, exists := d.GetOk("expected_codes"); exists && t != "" { + d.Set("expected_codes", m.ExpectedCodes) } else { d.Set("expected_codes", "") } - if _, exists := d.GetOk("admin_state_up"); exists { - if d.HasChange("admin_state_up") { - d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp)) - } + if t, exists := d.GetOk("admin_state_up"); exists && t != "" { + d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp)) } else { d.Set("admin_state_up", "") } diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 674110a8f..6ca79a802 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -124,10 +124,8 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { d.Set("subnet_id", p.SubnetID) d.Set("lb_method", p.LBMethod) - if _, exists := d.GetOk("tenant_id"); exists { - if d.HasChange("tenant_id") { - d.Set("tenant_id", p.TenantID) - } + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", p.TenantID) } else { d.Set("tenant_id", "") } diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index 83ba829c5..748ed8d6a 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -150,48 +150,36 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { d.Set("port", p.ProtocolPort) d.Set("pool_id", p.PoolID) - if _, exists := d.GetOk("tenant_id"); exists { - if d.HasChange("tenant_id") { - d.Set("tenant_id", p.TenantID) - } + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", p.TenantID) } else { d.Set("tenant_id", "") } - if _, exists := d.GetOk("address"); exists { - if d.HasChange("address") { - d.Set("address", p.Address) - } + if t, exists := d.GetOk("address"); exists && t != "" { + d.Set("address", p.Address) } else { d.Set("address", "") } - if _, exists := d.GetOk("description"); exists { - if d.HasChange("description") { - d.Set("description", p.Description) - } + if t, exists := d.GetOk("description"); exists && t != "" { + d.Set("description", p.Description) } else { d.Set("description", "") } - if _, exists := d.GetOk("persistence"); exists { - if d.HasChange("persistence") { + if t, exists := d.GetOk("persistence"); exists && t != "" { d.Set("persistence", p.Description) - } } - if _, exists := d.GetOk("conn_limit"); exists { - if d.HasChange("conn_limit") { - d.Set("conn_limit", p.ConnLimit) - } + if t, exists := d.GetOk("conn_limit"); exists && t != "" { + d.Set("conn_limit", p.ConnLimit) } else { d.Set("conn_limit", "") } - if _, exists := d.GetOk("admin_state_up"); exists { - if d.HasChange("admin_state_up") { - d.Set("admin_state_up", strconv.FormatBool(p.AdminStateUp)) - } + if t, exists := d.GetOk("admin_state_up"); exists && t != "" { + d.Set("admin_state_up", strconv.FormatBool(p.AdminStateUp)) } else { d.Set("admin_state_up", "") } diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index 3119baadd..92e02e031 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -23,6 +23,7 @@ func resourceNetworkingNetworkV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ Type: schema.TypeString, @@ -110,34 +111,26 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e d.Set("region", d.Get("region").(string)) - if _, exists := d.GetOk("name"); exists { - if d.HasChange("name") { - d.Set("name", n.Name) - } + if t, exists := d.GetOk("name"); exists && t != ""{ + d.Set("name", n.Name) } else { d.Set("name", "") } - if _, exists := d.GetOk("admin_state_up"); exists { - if d.HasChange("admin_state_up") { - d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) - } + if t, exists := d.GetOk("admin_state_up"); exists && t != "" { + d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) } else { d.Set("admin_state_up", "") } - if _, exists := d.GetOk("shared"); exists { - if d.HasChange("shared") { - d.Set("shared", strconv.FormatBool(n.Shared)) - } + if t, exists := d.GetOk("shared"); exists && t != "" { + d.Set("shared", strconv.FormatBool(n.Shared)) } else { d.Set("shared", "") } - if _, exists := d.GetOk("tenant_id"); exists { - if d.HasChange("tenant_id") { - d.Set("tenant_id", n.TenantID) - } + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", n.TenantID) } else { d.Set("tenant_id", "") } diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index ecb2a4ea3..0e72ce98d 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -24,6 +24,7 @@ func resourceNetworkingSubnetV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "network_id": &schema.Schema{ Type: schema.TypeString, @@ -170,18 +171,14 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er d.Set("cidr", s.CIDR) d.Set("ip_version", s.IPVersion) - if _, exists := d.GetOk("name"); exists { - if d.HasChange("name") { - d.Set("name", s.Name) - } + if t, exists := d.GetOk("name"); exists && t != "" { + d.Set("name", s.Name) } else { d.Set("name", "") } - if _, exists := d.GetOk("tenant_id"); exists { - if d.HasChange("tenant_id") { - d.Set("tenant_id", s.Name) - } + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", s.TenantID) } else { d.Set("tenant_id", "") } @@ -190,18 +187,14 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er d.Set("allocation_pools", s.AllocationPools) } - if _, exists := d.GetOk("gateway_ip"); exists { - if d.HasChange("gateway_ip") { - d.Set("gateway_ip", s.Name) - } + if t, exists := d.GetOk("gateway_ip"); exists && t != "" { + d.Set("gateway_ip", s.GatewayIP) } else { d.Set("gateway_ip", "") } - if _, exists := d.GetOk("enable_dhcp"); exists { - if d.HasChange("enable_dhcp") { - d.Set("enable_dhcp", strconv.FormatBool(s.EnableDHCP)) - } + if t, exists := d.GetOk("enable_dhcp"); exists && t != "" { + d.Set("enable_dhcp", strconv.FormatBool(s.EnableDHCP)) } else { d.Set("enable_dhcp", "") } From d80a02c12d9710ec934429839af6dbd23c5707a6 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 19:26:53 -0700 Subject: [PATCH 058/167] update docs to reflect resource region and versioning --- .../providers/openstack/index.html.markdown | 27 ++++++++----- ...down => compute_instance_v2.html.markdown} | 17 +++++--- ...kdown => compute_keypair_v2.html.markdown} | 18 ++++++--- ...down => compute_secgroup_v2.html.markdown} | 18 ++++++--- ... => compute_secgrouprule_v2.html.markdown} | 22 ++++++---- ...ml.markdown => lb_member_v1.html.markdown} | 20 ++++++---- ...l.markdown => lb_monitor_v1.html.markdown} | 18 ++++++--- ...html.markdown => lb_pool_v1.html.markdown} | 18 ++++++--- ....html.markdown => lb_vip_v1.html.markdown} | 18 ++++++--- ...wn => networking_network_v2.html.markdown} | 18 ++++++--- ...own => networking_subnet_v2.html.markdown} | 22 ++++++---- website/source/layouts/openstack.erb | 40 +++++++++---------- 12 files changed, 161 insertions(+), 95 deletions(-) rename website/source/docs/providers/openstack/r/{compute_instance.html.markdown => compute_instance_v2.html.markdown} (81%) rename website/source/docs/providers/openstack/r/{compute_keypair.html.markdown => compute_keypair_v2.html.markdown} (59%) rename website/source/docs/providers/openstack/r/{compute_secgroup.html.markdown => compute_secgroup_v2.html.markdown} (50%) rename website/source/docs/providers/openstack/r/{compute_secgrouprule.html.markdown => compute_secgrouprule_v2.html.markdown} (67%) rename website/source/docs/providers/openstack/r/{lb_member.html.markdown => lb_member_v1.html.markdown} (65%) rename website/source/docs/providers/openstack/r/{lb_monitor.html.markdown => lb_monitor_v1.html.markdown} (80%) rename website/source/docs/providers/openstack/r/{lb_pool.html.markdown => lb_pool_v1.html.markdown} (72%) rename website/source/docs/providers/openstack/r/{lb_vip.html.markdown => lb_vip_v1.html.markdown} (82%) rename website/source/docs/providers/openstack/r/{networking_network.html.markdown => networking_network_v2.html.markdown} (63%) rename website/source/docs/providers/openstack/r/{networking_subnet.html.markdown => networking_subnet_v2.html.markdown} (78%) diff --git a/website/source/docs/providers/openstack/index.html.markdown b/website/source/docs/providers/openstack/index.html.markdown index 652523c5b..da6d1fb79 100644 --- a/website/source/docs/providers/openstack/index.html.markdown +++ b/website/source/docs/providers/openstack/index.html.markdown @@ -19,15 +19,14 @@ Use the navigation to the left to read about the available resources. ``` # Configure the OpenStack Provider provider "openstack" { - username = "admin" + user_name = "admin" tenant_name = "admin" password = "pwd" auth_url = "http://myauthurl:5000/v2.0" - region = "RegionOne" } # Create a web server -resource "openstack_compute_instance" "test-server" { +resource "openstack_compute_instance_v2" "test-server" { ... } ``` @@ -36,12 +35,20 @@ resource "openstack_compute_instance" "test-server" { The following arguments are supported: -* `username` - (Required) - -* `tenant_name` - (Required) - -* `password` - (Required) - * `auth_url` - (Required) -* `region` - (Required) +* `user_name` - (Optional; Required for Identity V2) + +* `user_id` - (Optional) + +* `password` - (Optional; Required if not using `api_key`) + +* `api_key` - (Optional; Required if not using `password`) + +* `domain_id` - (Optional) + +* `domain_name` - (Optional) + +* `tenant_id` - (Optional) + +* `tenant_name` - (Optional) diff --git a/website/source/docs/providers/openstack/r/compute_instance.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown similarity index 81% rename from website/source/docs/providers/openstack/r/compute_instance.html.markdown rename to website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 7de1cd1b9..4429174fb 100644 --- a/website/source/docs/providers/openstack/r/compute_instance.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_compute_instance" -sidebar_current: "docs-openstack-resource-compute-instance" +page_title: "OpenStack: openstack_compute_instance_v2" +sidebar_current: "docs-openstack-resource-compute-instance-v2" description: |- - Manages a VM instance resource within OpenStack. + Manages a V2 VM instance resource within OpenStack. --- -# openstack\_compute\_instance +# openstack\_compute\_instance_v2 -Manages a VM instance resource within OpenStack. +Manages a V2 VM instance resource within OpenStack. ## Example Usage ``` -resource "openstack_compute_instance" "test-server" { +resource "openstack_compute_instance_v2" "test-server" { name = "tf-test" image_ref = "ad091b52-742f-469e-8f3c-fd81cadf0743" flavor_ref = "3" @@ -29,6 +29,10 @@ resource "openstack_compute_instance" "test-server" { The following arguments are supported: +* `region` - (Required) The region in which to create the server instance. If + omitted, the `OS_REGION_NAME` environment variable is used. Changing this + creates a new server. + * `name` - (Required) A unique name for the resource. * `image_ref` - (Required) The image reference (ID) for the desired image for @@ -73,6 +77,7 @@ The `network` block supports: The following attributes are exported: +* `region` - See Argument Reference above. * `name` - See Argument Reference above. * `access_ip_v4` - See Argument Reference above. * `access_ip_v6` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/compute_keypair.html.markdown b/website/source/docs/providers/openstack/r/compute_keypair_v2.html.markdown similarity index 59% rename from website/source/docs/providers/openstack/r/compute_keypair.html.markdown rename to website/source/docs/providers/openstack/r/compute_keypair_v2.html.markdown index 15459a0ec..0c3beae27 100644 --- a/website/source/docs/providers/openstack/r/compute_keypair.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_keypair_v2.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_compute_keypair" -sidebar_current: "docs-openstack-resource-compute-keypair" +page_title: "OpenStack: openstack_compute_keypair_v2" +sidebar_current: "docs-openstack-resource-compute-keypair-v2" description: |- - Manages a keypair resource within OpenStack. + Manages a V2 keypair resource within OpenStack. --- -# openstack\_compute\_keypair +# openstack\_compute\_keypair_v2 -Manages a keypair resource within OpenStack. +Manages a V2 keypair resource within OpenStack. ## Example Usage ``` -resource "openstack_compute_keypair" "test-keypair" { +resource "openstack_compute_keypair_v2" "test-keypair" { name = "my-keypair" public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAjpC1hwiOCCmKEWxJ4qzTTsJbKzndLotBCz5PcwtUnflmU+gHJtWMZKpuEGVi29h0A/+ydKek1O18k10Ff+4tyFjiHDQAnOfgWf7+b1yK+qDip3X1C0UPMbwHlTfSGWLGZqd9LvEFx9k3h/M+VtMvwR1lJ9LUyTAImnNjWG7TaIPmui30HvM2UiFEmqkr4ijq45MyX2+fLIePLRIF61p4whjHAQYufqyno3BS48icQb4p6iVEZPo4AE2o9oIyQvj2mx4dk5Y8CgSETOZTYDOR3rU2fZTRDRgPJDH9FWvQjF5tA0p3d9CoWWd2s6GKKbfoUIi8R/Db1BSPJwkqB" } @@ -23,6 +23,11 @@ resource "openstack_compute_keypair" "test-keypair" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Compute client. + Keypairs are associated with accounts, but a Compute client is needed to + create one. If omitted, the `OS_REGION_NAME` environment variable is used. + Changing this creates a new keypair. + * `name` - (Required) A unique name for the keypair. Changing this creates a new keypair. @@ -33,5 +38,6 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `name` - See Argument Reference above. * `public_key` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/compute_secgroup.html.markdown b/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown similarity index 50% rename from website/source/docs/providers/openstack/r/compute_secgroup.html.markdown rename to website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown index 27045b62b..86f546ed2 100644 --- a/website/source/docs/providers/openstack/r/compute_secgroup.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_compute_secgroup" -sidebar_current: "docs-openstack-resource-compute-secgroup" +page_title: "OpenStack: openstack_compute_secgroup_v2" +sidebar_current: "docs-openstack-resource-compute-secgroup-2" description: |- - Manages a security group resource within OpenStack. + Manages a V2 security group resource within OpenStack. --- -# openstack\_compute\_secgroup +# openstack\_compute\_secgroup_v2 -Manages a security group resource within OpenStack. +Manages a V2 security group resource within OpenStack. ## Example Usage ``` -resource "openstack_compute_secgroup" "secgroup_1" { +resource "openstack_compute_secgroup_v2" "secgroup_1" { name = "my_secgroup" description = "my security group" } @@ -23,6 +23,11 @@ resource "openstack_compute_secgroup" "secgroup_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Compute client. + A Compute client is needed to create a security group. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + security group. + * `name` - (Required) A unique name for the security group. Changing this updates the `name` of an existing security group. @@ -33,5 +38,6 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `name` - See Argument Reference above. * `description` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown b/website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown similarity index 67% rename from website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown rename to website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown index 60ffb347c..06385a6c8 100644 --- a/website/source/docs/providers/openstack/r/compute_secgrouprule.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown @@ -1,25 +1,25 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_compute_secgrouprule" -sidebar_current: "docs-openstack-resource-compute-secgrouprule" +page_title: "OpenStack: openstack_compute_secgrouprule_v2" +sidebar_current: "docs-openstack-resource-compute-secgrouprule-v2" description: |- - Manages a security group rule resource within OpenStack. + Manages a V2 security group rule resource within OpenStack. --- -# openstack\_compute\_secgrouprule +# openstack\_compute\_secgrouprule_v2 -Manages a security group rule resource within OpenStack. +Manages a V2 security group rule resource within OpenStack. ## Example Usage ``` -resource "openstack_compute_secgroup" "secgroup_1" { +resource "openstack_compute_secgroup_v2" "secgroup_1" { name = "my_secgroup" description = "my security group" } -resource "openstack_compute_secgrouprule" "secgrouprule_1" { - group_id = "${openstack_compute_secgroup.secgroup_1.id}" +resource "openstack_compute_secgrouprule_v2" "secgrouprule_1" { + group_id = "${openstack_compute_secgroup_v2.secgroup_1.id}" from_port = 22 to_port = 22 ip_protocol = "TCP" @@ -31,6 +31,11 @@ resource "openstack_compute_secgrouprule" "secgrouprule_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Compute client. + A Compute client is needed to create a security group rule. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + security group rule. + * `group_id` - (Required) The ID of the group to which this rule will be added. Changing this creates a new security group rule. @@ -55,6 +60,7 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `group_id` - See Argument Reference above. * `from_port` - See Argument Reference above. * `to_port` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/lb_member.html.markdown b/website/source/docs/providers/openstack/r/lb_member_v1.html.markdown similarity index 65% rename from website/source/docs/providers/openstack/r/lb_member.html.markdown rename to website/source/docs/providers/openstack/r/lb_member_v1.html.markdown index 0607ca840..2d1be74dc 100644 --- a/website/source/docs/providers/openstack/r/lb_member.html.markdown +++ b/website/source/docs/providers/openstack/r/lb_member_v1.html.markdown @@ -1,22 +1,22 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_lb_member" -sidebar_current: "docs-openstack-resource-lb-member" +page_title: "OpenStack: openstack_lb_member_v1" +sidebar_current: "docs-openstack-resource-lb-member-v1" description: |- - Manages a load balancer member resource within OpenStack. + Manages a V1 load balancer member resource within OpenStack. --- -# openstack\_lb\_member +# openstack\_lb\_member_v1 -Manages a load balancer member resource within OpenStack. +Manages a V1 load balancer member resource within OpenStack. ## Example Usage ``` -resource "openstack_lb_member" "node_1" { +resource "openstack_lb_member_v1" "node_1" { address = "196.172.0.1" port = 80 - pool_id = "$12345" + pool_id = "12345" admin_state_up = true } ``` @@ -25,6 +25,11 @@ resource "openstack_lb_member" "node_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Networking client. + A Networking client is needed to create an LB member. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + LB member. + * `address` - (Required) The IP address of the member. Changing this creates a new member. @@ -45,6 +50,7 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `address` - See Argument Reference above. * `port` - See Argument Reference above. * `pool_id` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/lb_monitor.html.markdown b/website/source/docs/providers/openstack/r/lb_monitor_v1.html.markdown similarity index 80% rename from website/source/docs/providers/openstack/r/lb_monitor.html.markdown rename to website/source/docs/providers/openstack/r/lb_monitor_v1.html.markdown index 36b814274..cbf6b2b87 100644 --- a/website/source/docs/providers/openstack/r/lb_monitor.html.markdown +++ b/website/source/docs/providers/openstack/r/lb_monitor_v1.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_lb_monitor" -sidebar_current: "docs-openstack-resource-lb-monitor" +page_title: "OpenStack: openstack_lb_monitor_v1" +sidebar_current: "docs-openstack-resource-lb-monitor-v1" description: |- - Manages a load balancer monitor resource within OpenStack. + Manages a V1 load balancer monitor resource within OpenStack. --- -# openstack\_lb\_monitor +# openstack\_lb\_monitor_v1 -Manages a load balancer monitor resource within OpenStack. +Manages a V1 load balancer monitor resource within OpenStack. ## Example Usage ``` -resource "openstack_lb_monitor" "monitor_1" { +resource "openstack_lb_monitor_v1" "monitor_1" { type = "PING" delay = 30 timeout = 5 @@ -26,6 +26,11 @@ resource "openstack_lb_monitor" "monitor_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Networking client. + A Networking client is needed to create an LB monitor. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + LB monitor. + * `type` - (Required) The type of probe, which is PING, TCP, HTTP, or HTTPS, that is sent by the monitor to verify the member state. Changing this creates a new monitor. @@ -65,6 +70,7 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `type` - See Argument Reference above. * `delay` - See Argument Reference above. * `timeout` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/lb_pool.html.markdown b/website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown similarity index 72% rename from website/source/docs/providers/openstack/r/lb_pool.html.markdown rename to website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown index 90e3ed0c9..f0de6917a 100644 --- a/website/source/docs/providers/openstack/r/lb_pool.html.markdown +++ b/website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_lb_pool" -sidebar_current: "docs-openstack-resource-lb-pool" +page_title: "OpenStack: openstack_lb_pool_v1" +sidebar_current: "docs-openstack-resource-lb-pool-v1" description: |- - Manages a load balancer pool resource within OpenStack. + Manages a V1 load balancer pool resource within OpenStack. --- -# openstack\_lb\_pool +# openstack\_lb\_pool_v1 -Manages a load balancer pool resource within OpenStack. +Manages a V1 load balancer pool resource within OpenStack. ## Example Usage ``` -resource "openstack_lb_pool" "pool_1" { +resource "openstack_lb_pool_v1" "pool_1" { name = "tf_test_lb_pool" protocol = "HTTP" subnet_id = "12345" @@ -26,6 +26,11 @@ resource "openstack_lb_pool" "pool_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Networking client. + A Networking client is needed to create an LB pool. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + LB pool. + * `name` - (Required) The name of the pool. Changing this updates the name of the existing pool. @@ -50,6 +55,7 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `name` - See Argument Reference above. * `protocol` - See Argument Reference above. * `subnet_id` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/lb_vip.html.markdown b/website/source/docs/providers/openstack/r/lb_vip_v1.html.markdown similarity index 82% rename from website/source/docs/providers/openstack/r/lb_vip.html.markdown rename to website/source/docs/providers/openstack/r/lb_vip_v1.html.markdown index 0eddcaa5f..7a9bc3d4b 100644 --- a/website/source/docs/providers/openstack/r/lb_vip.html.markdown +++ b/website/source/docs/providers/openstack/r/lb_vip_v1.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_lb_vip" -sidebar_current: "docs-openstack-resource-lb-vip" +page_title: "OpenStack: openstack_lb_vip_v1" +sidebar_current: "docs-openstack-resource-lb-vip-v1" description: |- - Manages a load balancer vip resource within OpenStack. + Manages a V1 load balancer vip resource within OpenStack. --- -# openstack\_lb\_vip +# openstack\_lb\_vip_v1 -Manages a load balancer vip resource within OpenStack. +Manages a V1 load balancer vip resource within OpenStack. ## Example Usage ``` -resource "openstack_lb_vip" "vip_1" { +resource "openstack_lb_vip_v1" "vip_1" { name = "tf_test_lb_vip" subnet_id = "12345" protocol = "HTTP" @@ -26,6 +26,11 @@ resource "openstack_lb_vip" "vip_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Networking client. + A Networking client is needed to create a VIP. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + VIP. + * `name` - (Required) The name of the vip. Changing this updates the name of the existing vip. @@ -76,6 +81,7 @@ The `persistence` block supports: The following attributes are exported: +* `region` - See Argument Reference above. * `name` - See Argument Reference above. * `subnet_id` - See Argument Reference above. * `protocol` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/networking_network.html.markdown b/website/source/docs/providers/openstack/r/networking_network_v2.html.markdown similarity index 63% rename from website/source/docs/providers/openstack/r/networking_network.html.markdown rename to website/source/docs/providers/openstack/r/networking_network_v2.html.markdown index eb765cac0..699cd1ae9 100644 --- a/website/source/docs/providers/openstack/r/networking_network.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_network_v2.html.markdown @@ -1,19 +1,19 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_networking_network" -sidebar_current: "docs-openstack-resource-networking-network" +page_title: "OpenStack: openstack_networking_network_v2" +sidebar_current: "docs-openstack-resource-networking-network-v2" description: |- - Manages a Neutron network resource within OpenStack. + Manages a V2 Neutron network resource within OpenStack. --- -# openstack\_networking\_network +# openstack\_networking\_network_v2 -Manages a Neutron network resource within OpenStack. +Manages a V2 Neutron network resource within OpenStack. ## Example Usage ``` -resource "openstack_networking_network" "network_1" { +resource "openstack_networking_network_v2" "network_1" { name = "tf_test_network" admin_state_up = "true" } @@ -23,6 +23,11 @@ resource "openstack_networking_network" "network_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Networking client. + A Networking client is needed to create a Neutron network. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + network. + * `name` - (Optional) The name of the network. Changing this updates the name of the existing network. @@ -41,6 +46,7 @@ The following arguments are supported: The following attributes are exported: +* `region` - See Argument Reference above. * `name` - See Argument Reference above. * `shared` - See Argument Reference above. * `tenant_id` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/networking_subnet.html.markdown b/website/source/docs/providers/openstack/r/networking_subnet_v2.html.markdown similarity index 78% rename from website/source/docs/providers/openstack/r/networking_subnet.html.markdown rename to website/source/docs/providers/openstack/r/networking_subnet_v2.html.markdown index f9ab4f6ed..a8243a817 100644 --- a/website/source/docs/providers/openstack/r/networking_subnet.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_subnet_v2.html.markdown @@ -1,25 +1,25 @@ --- layout: "openstack" -page_title: "OpenStack: openstack_networking_subnet" -sidebar_current: "docs-openstack-resource-networking-subnet" +page_title: "OpenStack: openstack_networking_subnet_v2" +sidebar_current: "docs-openstack-resource-networking-subnet-v2" description: |- - Manages a Neutron subnet resource within OpenStack. + Manages a V2 Neutron subnet resource within OpenStack. --- -# openstack\_networking\_subnet +# openstack\_networking\_subnet_v2 -Manages a Neutron subnet resource within OpenStack. +Manages a V2 Neutron subnet resource within OpenStack. ## Example Usage ``` -resource "openstack_networking_network" "network_1" { +resource "openstack_networking_network_v2" "network_1" { name = "tf_test_network" admin_state_up = "true" } -resource "openstack_networking_subnet" "subnet_1" { - network_id = "${openstack_networking_network.network_1.id}" +resource "openstack_networking_subnet_v2" "subnet_1" { + network_id = "${openstack_networking_network_v2.network_1.id}" cidr = "192.168.199.0/24" ip_version = 4 } @@ -29,6 +29,11 @@ resource "openstack_networking_subnet" "subnet_1" { The following arguments are supported: +* `region` - (Required) The region in which to obtain the V2 Networking client. + A Networking client is needed to create a Neutron subnet. If omitted, the + `OS_REGION_NAME` environment variable is used. Changing this creates a new + subnet. + * `network_id` - (Required) The UUID of the parent network. Changing this creates a new subnet. @@ -80,6 +85,7 @@ The `host_routes` block supports: The following attributes are exported: +* `region` - See Argument Reference above. * `network_id` - See Argument Reference above. * `cidr` - See Argument Reference above. * `ip_version` - See Argument Reference above. diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb index b7878887c..37b0c2a7c 100644 --- a/website/source/layouts/openstack.erb +++ b/website/source/layouts/openstack.erb @@ -13,35 +13,35 @@ > Resources From e6f3a192842806b509b343cbe7f5ae541fdd4935 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 22:33:34 -0700 Subject: [PATCH 059/167] add defaultFunc for 'tenant_name' --- builtin/providers/openstack/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 48029afc7..37a6e9b4c 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -34,7 +34,7 @@ func Provider() terraform.ResourceProvider { "tenant_name": &schema.Schema{ Type: schema.TypeString, Optional: true, - Default: "", + DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), }, "password": &schema.Schema{ Type: schema.TypeString, From 59b5efc25aee9d7af40307789543dd40d29d9f1f Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 22:35:18 -0700 Subject: [PATCH 060/167] add defaultFuncs; fix bug with server createOpts --- .../openstack/resource_openstack_compute_instance_v2.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index f6a922f43..bacf3707e 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -39,11 +39,13 @@ func resourceComputeInstanceV2() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: false, + DefaultFunc: envDefaultFunc("OS_IMAGE_ID"), }, "flavor_ref": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: false, + DefaultFunc: envDefaultFunc("OS_FLAVOR_ID"), }, "security_groups": &schema.Schema{ Type: schema.TypeSet, @@ -127,7 +129,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e var createOpts servers.CreateOptsBuilder - serverCreateOpts := &servers.CreateOpts{ + createOpts = &servers.CreateOpts{ Name: d.Get("name").(string), ImageRef: d.Get("image_ref").(string), FlavorRef: d.Get("flavor_ref").(string), @@ -141,7 +143,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e if keyName, ok := d.Get("key_pair").(string); ok && keyName != "" { createOpts = &keypairs.CreateOptsExt{ - serverCreateOpts, + createOpts, keyName, } } From e278f852b34122de1d1e874026047c0823c00943 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 22:36:08 -0700 Subject: [PATCH 061/167] add image_ref and flavor_ref checks --- builtin/providers/openstack/provider_test.go | 32 ++++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go index 2819a4575..57257f5d0 100644 --- a/builtin/providers/openstack/provider_test.go +++ b/builtin/providers/openstack/provider_test.go @@ -8,6 +8,11 @@ import ( "github.com/hashicorp/terraform/terraform" ) + +var ( + OS_REGION_NAME = "" +) + var testAccProviders map[string]terraform.ResourceProvider var testAccProvider *schema.Provider @@ -29,23 +34,24 @@ func TestProvider_impl(t *testing.T) { } func testAccPreCheck(t *testing.T) { - if v := os.Getenv("OS_REGION_NAME"); v == "" { - t.Fatal("OS_REGION_NAME must be set for acceptance tests") - } - - if v := os.Getenv("OS_AUTH_URL"); v == "" { + v := os.Getenv("OS_AUTH_URL") + if v == "" { t.Fatal("OS_AUTH_URL must be set for acceptance tests") } - if v := os.Getenv("OS_USERNAME"); v == "" { - t.Fatal("OS_USERNAME must be set for acceptance tests") + v = os.Getenv("OS_REGION_NAME") + if v == "" { + t.Fatal("OS_REGION_NAME must be set for acceptance tests") + } + OS_REGION_NAME = v + + v = os.Getenv("OS_IMAGE_ID") + if v == "" { + t.Fatal("OS_IMAGE_ID must be set for acceptance tests") } - if v := os.Getenv("OS_TENANT_NAME"); v != "us-central1" { - t.Fatal("OS_TENANT_NAME must be set to us-central1 for acceptance tests") - } - - if v := os.Getenv("OS_PASSWORD"); v != "us-central1" { - t.Fatal("OS_PASSWORD must be set to us-central1 for acceptance tests") + v = os.Getenv("OS_FLAVOR_ID") + if v == "" { + t.Fatal("OS_FLAVOR_ID must be set for acceptance tests") } } From a707f8414cec65c19ec0867999bae6a08f83b6d5 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 26 Jan 2015 22:36:39 -0700 Subject: [PATCH 062/167] compute instance v2 acceptance tests --- ...urce_openstack_compute_instance_v2_test.go | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go new file mode 100644 index 000000000..4b73957d2 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -0,0 +1,123 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" + "github.com/rackspace/gophercloud/openstack/compute/v2/servers" +) + +func TestAccComputeV2Instance_basic(t *testing.T) { + var instance servers.Server + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2InstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2Instance_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance), + testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"), + ), + }, + }, + }) +} + +func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: OS_REGION_NAME, + }) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2InstanceDestroy) Error creating OpenStack compute client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_compute_instance_v2" { + continue + } + + _, err := servers.Get(computeClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Instance still exists") + } + } + + return nil +} + +func testAccCheckComputeV2InstanceExists(t *testing.T, n string, instance *servers.Server) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + osClient := testAccProvider.Meta().(*Config).osClient + computeClient, err := openstack.NewComputeV2(osClient, gophercloud.EndpointOpts{ + Region: OS_REGION_NAME, + }) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2InstanceExists) Error creating OpenStack compute client: %s", err) + } + + + found, err := servers.Get(computeClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Instance not found") + } + + *instance = *found + + return nil + } +} + +func testAccCheckComputeV2InstanceMetadata( + instance *servers.Server, k string, v string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Metadata == nil { + return fmt.Errorf("No metadata") + } + + for key, value := range instance.Metadata { + if k != key { + continue + } + + if v == value.(string) { + return nil + } + + return fmt.Errorf("Bad value for %s: %s", k, value) + } + + return fmt.Errorf("Metadata not found: %s", k) + } +} + +var testAccComputeV2Instance_basic = fmt.Sprintf(` + resource "openstack_compute_instance_v2" "foo" { + region = "%s" + name = "terraform-test" + metadata { + foo = "bar" + } + }`, + OS_REGION_NAME) From 17b137c972ad647581531d23ea061de391957c91 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Tue, 27 Jan 2015 10:19:11 -0700 Subject: [PATCH 063/167] go fmt --- builtin/providers/openstack/provider.go | 6 +++--- builtin/providers/openstack/provider_test.go | 1 - .../resource_openstack_compute_instance_v2.go | 12 ++++++------ .../resource_openstack_compute_instance_v2_test.go | 3 +-- .../openstack/resource_openstack_lb_vip_v1.go | 2 +- .../resource_openstack_networking_network_v2.go | 8 ++++---- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 37a6e9b4c..b28c330bc 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -32,9 +32,9 @@ func Provider() terraform.ResourceProvider { Default: "", }, "tenant_name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), + Type: schema.TypeString, + Optional: true, + DefaultFunc: envDefaultFunc("OS_TENANT_NAME"), }, "password": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go index 57257f5d0..9e90bb4ea 100644 --- a/builtin/providers/openstack/provider_test.go +++ b/builtin/providers/openstack/provider_test.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/terraform" ) - var ( OS_REGION_NAME = "" ) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index bacf3707e..8b23c5a87 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -36,15 +36,15 @@ func resourceComputeInstanceV2() *schema.Resource { ForceNew: false, }, "image_ref": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: false, + Type: schema.TypeString, + Required: true, + ForceNew: false, DefaultFunc: envDefaultFunc("OS_IMAGE_ID"), }, "flavor_ref": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: false, + Type: schema.TypeString, + Required: true, + ForceNew: false, DefaultFunc: envDefaultFunc("OS_FLAVOR_ID"), }, "security_groups": &schema.Schema{ diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index 4b73957d2..ff2457d09 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -73,7 +73,6 @@ func testAccCheckComputeV2InstanceExists(t *testing.T, n string, instance *serve return fmt.Errorf("(testAccCheckComputeV2InstanceExists) Error creating OpenStack compute client: %s", err) } - found, err := servers.Get(computeClient, rs.Primary.ID).Extract() if err != nil { return err @@ -120,4 +119,4 @@ var testAccComputeV2Instance_basic = fmt.Sprintf(` foo = "bar" } }`, - OS_REGION_NAME) + OS_REGION_NAME) diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index 748ed8d6a..eefe239be 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -169,7 +169,7 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { } if t, exists := d.GetOk("persistence"); exists && t != "" { - d.Set("persistence", p.Description) + d.Set("persistence", p.Description) } if t, exists := d.GetOk("conn_limit"); exists && t != "" { diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index 92e02e031..e32db9697 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -20,9 +20,9 @@ func resourceNetworkingNetworkV2() *schema.Resource { Schema: map[string]*schema.Schema{ "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, DefaultFunc: envDefaultFunc("OS_REGION_NAME"), }, "name": &schema.Schema{ @@ -111,7 +111,7 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e d.Set("region", d.Get("region").(string)) - if t, exists := d.GetOk("name"); exists && t != ""{ + if t, exists := d.GetOk("name"); exists && t != "" { d.Set("name", n.Name) } else { d.Set("name", "") From 3a6107d0ab5a877a69070d241ba1241f3864ace5 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Tue, 27 Jan 2015 10:19:45 -0700 Subject: [PATCH 064/167] keypairs v2 acceptance tests --- ...ource_openstack_compute_keypair_v2_test.go | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go new file mode 100644 index 000000000..19e5cf655 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go @@ -0,0 +1,96 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" +) + +func TestAccComputeV2Keypair_basic(t *testing.T) { + var keypair keypairs.KeyPair + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2KeypairDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2Keypair_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2KeypairExists(t, "openstack_compute_keypair_v2.foo", &keypair), + ), + }, + }, + }) +} + +func testAccCheckComputeV2KeypairDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ + Region: OS_REGION_NAME, + }) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2InstanceDestroy) Error creating OpenStack compute client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_compute_keypair_v2" { + continue + } + + _, err := keypairs.Get(computeClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Keypair still exists") + } + } + + return nil +} + +func testAccCheckComputeV2KeypairExists(t *testing.T, n string, kp *keypairs.KeyPair) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + osClient := testAccProvider.Meta().(*Config).osClient + computeClient, err := openstack.NewComputeV2(osClient, gophercloud.EndpointOpts{ + Region: OS_REGION_NAME, + }) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2KeypairExists) Error creating OpenStack compute client: %s", err) + } + + found, err := keypairs.Get(computeClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.Name != rs.Primary.ID { + return fmt.Errorf("Keypair not found") + } + + *kp = *found + + return nil + } +} + +var testAccComputeV2Keypair_basic = fmt.Sprintf(` + resource "openstack_compute_keypair_v2" "foo" { + region = "%s" + name = "test-keypair-tf" + public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAjpC1hwiOCCmKEWxJ4qzTTsJbKzndLo1BCz5PcwtUnflmU+gHJtWMZKpuEGVi29h0A/+ydKek1O18k10Ff+4tyFjiHDQAT9+OfgWf7+b1yK+qDip3X1C0UPMbwHlTfSGWLGZquwhvEFx9k3h/M+VtMvwR1lJ9LUyTAImnNjWG7TAIPmui30HvM2UiFEmqkr4ijq45MyX2+fLIePLRIFuu1p4whjHAQYufqyno3BS48icQb4p6iVEZPo4AE2o9oIyQvj2mx4dk5Y8CgSETOZTYDOR3rU2fZTRDRgPJDH9FWvQjF5tA0p3d9CoWWd2s6GKKbfoUIi8R/Db1BSPJwkqB jrp-hp-pc" + }`, + OS_REGION_NAME) From ea7c075273ef8a3237c67208f506115149245a04 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 29 Jan 2015 21:54:07 -0700 Subject: [PATCH 065/167] add security group rules ops to security groups file --- .../resource_openstack_compute_secgroup_v2.go | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index a79a6a88b..dd5aafd09 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -1,9 +1,11 @@ package openstack import ( + "bytes" "fmt" "log" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack" @@ -34,6 +36,40 @@ func resourceComputeSecGroupV2() *schema.Resource { Required: true, ForceNew: false, }, + "rules": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from_port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "to_port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "ip_protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "cidr": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "from_group_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + Set: resourceSecGroupRuleHash, + }, }, } } @@ -59,6 +95,14 @@ func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) e d.SetId(sg.ID) + createRuleOptsList := resourceSecGroupRulesV2(d) + for _, createRuleOpts := range createRuleOptsList { + _, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack security group rule: %s", err) + } + } + return resourceComputeSecGroupV2Read(d, meta) } @@ -79,6 +123,7 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("region", d.Get("region").(string)) d.Set("name", sg.Name) d.Set("description", sg.Description) + d.Set("rules", sg.Rules) return nil } @@ -104,6 +149,35 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err) } + if d.HasChange("rules") { + oldSGRaw, newSGRaw := d.GetChange("rules") + oldSGRSet, newSGRSet := oldSGRaw.(*schema.Set), newSGRaw.(*schema.Set) + secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet) + secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet) + + log.Printf("[DEBUG] Security group rules to add: %v", secgrouprulesToAdd) + + log.Printf("[DEBUG] Security groups to remove: %v", secgrouprulesToRemove) + + for _, rawRule := range secgrouprulesToAdd.List() { + createRuleOpts := resourceSecGroupRuleV2(d, rawRule) + rule, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract() + if err != nil { + return fmt.Errorf("Error adding rule to OpenStack security group (%s): %s", d.Id(), err) + } + log.Printf("[DEBUG] Added rule (%s) to OpenStack security group (%s) ", rule.ID, d.Id()) + } + + for _, r := range secgrouprulesToRemove.List() { + rule := r.(secgroups.Rule) + err := secgroups.DeleteRule(computeClient, "").ExtractErr() + if err != nil { + return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err) + } + log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s)", rule.ID, d.Id()) + } + } + return resourceComputeSecGroupV2Read(d, meta) } @@ -123,3 +197,46 @@ func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) e d.SetId("") return nil } + +func resourceSecGroupRuleHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["from_group_id"].(string))) + + return hashcode.String(buf.String()) +} + +func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts { + rawRules := (d.Get("rules")).(*schema.Set) + createRuleOptsList := make([]secgroups.CreateRuleOpts, rawRules.Len()) + for i, raw := range rawRules.List() { + rawMap := raw.(map[string]interface{}) + createRuleOptsList[i] = secgroups.CreateRuleOpts{ + ParentGroupID: d.Id(), + FromPort: rawMap["from_port"].(int), + ToPort: rawMap["to_port"].(int), + IPProtocol: rawMap["ip_protocol"].(string), + CIDR: rawMap["cidr"].(string), + FromGroupID: rawMap["from_group_id"].(string), + } + } + return createRuleOptsList +} + +func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts { + rawMap := raw.(map[string]interface{}) + createRuleOpts := secgroups.CreateRuleOpts{ + ParentGroupID: d.Id(), + FromPort: rawMap["from_port"].(int), + ToPort: rawMap["to_port"].(int), + IPProtocol: rawMap["ip_protocol"].(string), + CIDR: rawMap["cidr"].(string), + FromGroupID: rawMap["from_group_id"].(string), + } + + return createRuleOpts +} From c233c7f7f02f4664c3ec11f6ab42ca62612cec00 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 29 Jan 2015 21:56:40 -0700 Subject: [PATCH 066/167] fix typo in comment --- .../openstack/resource_openstack_compute_keypair_v2_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go index 19e5cf655..0456341df 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go @@ -36,7 +36,7 @@ func testAccCheckComputeV2KeypairDestroy(s *terraform.State) error { Region: OS_REGION_NAME, }) if err != nil { - return fmt.Errorf("(testAccCheckComputeV2InstanceDestroy) Error creating OpenStack compute client: %s", err) + return fmt.Errorf("(testAccCheckComputeV2KeypairDestroy) Error creating OpenStack compute client: %s", err) } for _, rs := range s.RootModule().Resources { From a2d2f927417bb1bb711a676bdaaacc397b0ba163 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 29 Jan 2015 21:57:14 -0700 Subject: [PATCH 067/167] remove security group rule file --- ...ource_openstack_compute_secgrouprule_v2.go | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go diff --git a/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go deleted file mode 100644 index fce40e621..000000000 --- a/builtin/providers/openstack/resource_openstack_compute_secgrouprule_v2.go +++ /dev/null @@ -1,113 +0,0 @@ -package openstack - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" -) - -func resourceComputeSecGroupRuleV2() *schema.Resource { - return &schema.Resource{ - Create: resourceComputeSecGroupRuleV2Create, - Read: resourceComputeSecGroupRuleV2Read, - Delete: resourceComputeSecGroupRuleV2Delete, - - Schema: map[string]*schema.Schema{ - "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), - }, - "group_id": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "from_port": &schema.Schema{ - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "to_port": &schema.Schema{ - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "ip_protocol": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "cidr": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "from_group_id": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - }, - } -} - -func resourceComputeSecGroupRuleV2Create(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) - if err != nil { - return fmt.Errorf("Error creating OpenStack compute client: %s", err) - } - - createOpts := secgroups.CreateRuleOpts{ - ParentGroupID: d.Get("group_id").(string), - FromPort: d.Get("from_port").(int), - ToPort: d.Get("to_port").(int), - IPProtocol: d.Get("ip_protocol").(string), - CIDR: d.Get("cidr").(string), - FromGroupID: d.Get("from_group_id").(string), - } - - sgr, err := secgroups.CreateRule(computeClient, createOpts).Extract() - if err != nil { - return fmt.Errorf("Error creating OpenStack security group rule: %s", err) - } - - d.SetId(sgr.ID) - d.Set("region", d.Get("region").(string)) - d.Set("group_id", sgr.ParentGroupID) - d.Set("from_port", sgr.FromPort) - d.Set("to_port", sgr.ToPort) - d.Set("ip_protocol", sgr.IPProtocol) - d.Set("cidr", sgr.IPRange.CIDR) - d.Set("from_group_id", d.Get("from_group_id").(string)) - - return resourceComputeSecGroupRuleV2Read(d, meta) -} - -func resourceComputeSecGroupRuleV2Read(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func resourceComputeSecGroupRuleV2Delete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) - if err != nil { - return fmt.Errorf("Error creating OpenStack compute client: %s", err) - } - - err = secgroups.DeleteRule(computeClient, d.Id()).ExtractErr() - if err != nil { - return fmt.Errorf("Error deleting OpenStack security group rule: %s", err) - } - d.SetId("") - return nil -} From 3627368fc040bfc39e7b38557931025071665e00 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 29 Jan 2015 22:09:59 -0700 Subject: [PATCH 068/167] remove security group rule option from provider --- builtin/providers/openstack/provider.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index b28c330bc..65496d534 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -59,16 +59,15 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "openstack_compute_instance_v2": resourceComputeInstanceV2(), - "openstack_compute_keypair_v2": resourceComputeKeypairV2(), - "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), - "openstack_compute_secgrouprule_v2": resourceComputeSecGroupRuleV2(), - "openstack_lb_member_v1": resourceLBMemberV1(), - "openstack_lb_monitor_v1": resourceLBMonitorV1(), - "openstack_lb_pool_v1": resourceLBPoolV1(), - "openstack_lb_vip_v1": resourceLBVipV1(), - "openstack_networking_network_v2": resourceNetworkingNetworkV2(), - "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), + "openstack_compute_instance_v2": resourceComputeInstanceV2(), + "openstack_compute_keypair_v2": resourceComputeKeypairV2(), + "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), + "openstack_lb_member_v1": resourceLBMemberV1(), + "openstack_lb_monitor_v1": resourceLBMonitorV1(), + "openstack_lb_pool_v1": resourceLBPoolV1(), + "openstack_lb_vip_v1": resourceLBVipV1(), + "openstack_networking_network_v2": resourceNetworkingNetworkV2(), + "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), }, ConfigureFunc: configureProvider, From 1aba665ad79dc426af72aa31799a09daa5d70c56 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 14:33:54 -0700 Subject: [PATCH 069/167] refactor service clients to *Config --- builtin/providers/openstack/config.go | 12 ++++++++++++ .../resource_openstack_compute_instance_v2.go | 17 ++++------------- .../resource_openstack_compute_keypair_v2.go | 14 +++----------- .../resource_openstack_compute_secgroup_v2.go | 18 ++++-------------- .../resource_openstack_lb_member_v1.go | 18 ++++-------------- .../resource_openstack_lb_monitor_v1.go | 18 ++++-------------- .../openstack/resource_openstack_lb_pool_v1.go | 18 ++++-------------- .../openstack/resource_openstack_lb_vip_v1.go | 17 ++++------------- ...resource_openstack_networking_network_v2.go | 18 ++++-------------- .../resource_openstack_networking_subnet_v2.go | 18 ++++-------------- 10 files changed, 47 insertions(+), 121 deletions(-) diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index 215817bdd..2e37c4fd0 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -41,3 +41,15 @@ func (c *Config) loadAndValidate() error { return nil } + +func (c *Config) computeV2Client(region string) (*gophercloud.ServiceClient, error) { + return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{ + Region: region, + }) +} + +func (c *Config) networkingV2Client(region string) (*gophercloud.ServiceClient, error) { + return openstack.NewNetworkV2(c.osClient, gophercloud.EndpointOpts{ + Region: region, + }) +} diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 8b23c5a87..a2e66ca2e 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" @@ -120,9 +119,7 @@ func resourceComputeInstanceV2() *schema.Resource { func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -185,9 +182,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -254,9 +249,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -382,9 +375,7 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index e2d189a07..8cdc84913 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -4,8 +4,6 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" ) @@ -38,9 +36,7 @@ func resourceComputeKeypairV2() *schema.Resource { func resourceComputeKeypairV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -62,9 +58,7 @@ func resourceComputeKeypairV2Create(d *schema.ResourceData, meta interface{}) er func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -83,9 +77,7 @@ func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) erro func resourceComputeKeypairV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index dd5aafd09..f83ce84c7 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -7,8 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) @@ -76,9 +74,7 @@ func resourceComputeSecGroupV2() *schema.Resource { func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -108,9 +104,7 @@ func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) e func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -130,9 +124,7 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } @@ -183,9 +175,7 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + computeClient, err := config.computeV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_member_v1.go b/builtin/providers/openstack/resource_openstack_lb_member_v1.go index 2d615ed76..9cadfef90 100644 --- a/builtin/providers/openstack/resource_openstack_lb_member_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_member_v1.go @@ -5,8 +5,6 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" ) @@ -55,9 +53,7 @@ func resourceLBMemberV1() *schema.Resource { func resourceLBMemberV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -83,9 +79,7 @@ func resourceLBMemberV1Create(d *schema.ResourceData, meta interface{}) error { func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -113,9 +107,7 @@ func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { func resourceLBMemberV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -137,9 +129,7 @@ func resourceLBMemberV1Update(d *schema.ResourceData, meta interface{}) error { func resourceLBMemberV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index f6c097d23..ca5ad7659 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -6,8 +6,6 @@ import ( "strconv" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" ) @@ -76,9 +74,7 @@ func resourceLBMonitorV1() *schema.Resource { func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -117,9 +113,7 @@ func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error { func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -172,9 +166,7 @@ func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -221,9 +213,7 @@ func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error { func resourceLBMonitorV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 6ca79a802..3cadc2858 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -6,8 +6,6 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" ) @@ -66,9 +64,7 @@ func resourceLBPoolV1() *schema.Resource { func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -104,9 +100,7 @@ func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -137,9 +131,7 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -191,9 +183,7 @@ func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index eefe239be..cef3977c8 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" ) @@ -86,9 +85,7 @@ func resourceLBVipV1() *schema.Resource { func resourceLBVipV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -129,9 +126,7 @@ func resourceLBVipV1Create(d *schema.ResourceData, meta interface{}) error { func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -189,9 +184,7 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -235,9 +228,7 @@ func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error { func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index e32db9697..fc420b2e9 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -6,8 +6,6 @@ import ( "strconv" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/networks" ) @@ -51,9 +49,7 @@ func resourceNetworkingNetworkV2() *schema.Resource { func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -95,9 +91,7 @@ func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -140,9 +134,7 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -184,9 +176,7 @@ func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) func resourceNetworkingNetworkV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index 0e72ce98d..74a43e27c 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -7,8 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" ) @@ -110,9 +108,7 @@ func resourceNetworkingSubnetV2() *schema.Resource { func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -152,9 +148,7 @@ func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -212,9 +206,7 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } @@ -260,9 +252,7 @@ func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{}) func resourceNetworkingSubnetV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - networkingClient, err := openstack.NewNetworkV2(config.osClient, gophercloud.EndpointOpts{ - Region: d.Get("region").(string), - }) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } From 777c34cf7c4f6d791e5bd65ae013058d66c93246 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:19:42 -0700 Subject: [PATCH 070/167] move lb member ops into lb pool file --- builtin/providers/openstack/provider.go | 1 - .../resource_openstack_lb_member_v1.go | 144 ------------------ .../resource_openstack_lb_pool_v1.go | 127 +++++++++++++++ 3 files changed, 127 insertions(+), 145 deletions(-) delete mode 100644 builtin/providers/openstack/resource_openstack_lb_member_v1.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 65496d534..a53882f84 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -62,7 +62,6 @@ func Provider() terraform.ResourceProvider { "openstack_compute_instance_v2": resourceComputeInstanceV2(), "openstack_compute_keypair_v2": resourceComputeKeypairV2(), "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), - "openstack_lb_member_v1": resourceLBMemberV1(), "openstack_lb_monitor_v1": resourceLBMonitorV1(), "openstack_lb_pool_v1": resourceLBPoolV1(), "openstack_lb_vip_v1": resourceLBVipV1(), diff --git a/builtin/providers/openstack/resource_openstack_lb_member_v1.go b/builtin/providers/openstack/resource_openstack_lb_member_v1.go deleted file mode 100644 index 9cadfef90..000000000 --- a/builtin/providers/openstack/resource_openstack_lb_member_v1.go +++ /dev/null @@ -1,144 +0,0 @@ -package openstack - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" -) - -func resourceLBMemberV1() *schema.Resource { - return &schema.Resource{ - Create: resourceLBMemberV1Create, - Read: resourceLBMemberV1Read, - Update: resourceLBMemberV1Update, - Delete: resourceLBMemberV1Delete, - - Schema: map[string]*schema.Schema{ - "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - DefaultFunc: envDefaultFunc("OS_REGION_NAME"), - }, - "tenant_id": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "address": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "port": &schema.Schema{ - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "pool_id": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "admin_state_up": &schema.Schema{ - Type: schema.TypeBool, - Required: true, - ForceNew: false, - }, - }, - } -} - -func resourceLBMemberV1Create(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - networkingClient, err := config.networkingV2Client(d.Get("region").(string)) - if err != nil { - return fmt.Errorf("Error creating OpenStack networking client: %s", err) - } - - createOpts := members.CreateOpts{ - //TenantID: d.Get("tenant_id").(string), - Address: d.Get("address").(string), - ProtocolPort: d.Get("port").(int), - PoolID: d.Get("pool_id").(string), - } - - log.Printf("[INFO] Requesting lb member creation") - p, err := members.Create(networkingClient, createOpts).Extract() - if err != nil { - return fmt.Errorf("Error creating OpenStack LB member: %s", err) - } - log.Printf("[INFO] LB Member ID: %s", p.ID) - - d.SetId(p.ID) - - return resourceLBMemberV1Read(d, meta) -} - -func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - networkingClient, err := config.networkingV2Client(d.Get("region").(string)) - if err != nil { - return fmt.Errorf("Error creating OpenStack networking client: %s", err) - } - - p, err := members.Get(networkingClient, d.Id()).Extract() - if err != nil { - return fmt.Errorf("Error retrieving OpenStack LB Member: %s", err) - } - - log.Printf("[DEBUG] Retreived OpenStack LB Member %s: %+v", d.Id(), p) - - d.Set("region", d.Get("region").(string)) - d.Set("address", p.Address) - d.Set("port", p.ProtocolPort) - d.Set("pool_id", p.PoolID) - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", p.TenantID) - } else { - d.Set("tenant_id", "") - } - - return nil -} - -func resourceLBMemberV1Update(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - networkingClient, err := config.networkingV2Client(d.Get("region").(string)) - if err != nil { - return fmt.Errorf("Error creating OpenStack networking client: %s", err) - } - - var updateOpts members.UpdateOpts - if d.HasChange("admin_state_up") { - updateOpts.AdminStateUp = d.Get("admin_state_up").(bool) - } - - log.Printf("[DEBUG] Updating OpenStack LB Member %s with options: %+v", d.Id(), updateOpts) - - _, err = members.Update(networkingClient, d.Id(), updateOpts).Extract() - if err != nil { - return fmt.Errorf("Error updating OpenStack LB Member: %s", err) - } - - return resourceLBMemberV1Read(d, meta) -} - -func resourceLBMemberV1Delete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - networkingClient, err := config.networkingV2Client(d.Get("region").(string)) - if err != nil { - return fmt.Errorf("Error creating OpenStack networking client: %s", err) - } - - err = members.Delete(networkingClient, d.Id()).ExtractErr() - if err != nil { - return fmt.Errorf("Error deleting OpenStack LB Member: %s", err) - } - - d.SetId("") - return nil -} diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 3cadc2858..a7b5abbaf 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -1,12 +1,15 @@ package openstack import ( + "bytes" "fmt" "log" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" + "github.com/rackspace/gophercloud/pagination" ) func resourceLBPoolV1() *schema.Resource { @@ -49,6 +52,41 @@ func resourceLBPoolV1() *schema.Resource { Optional: true, ForceNew: true, }, + "member": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "address": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "port": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "admin_state_up": &schema.Schema{ + Type: schema.TypeBool, + Required: true, + ForceNew: false, + }, + }, + }, + Set: resourceLBMemberV1Hash, + }, "monitor_ids": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -95,6 +133,15 @@ func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { } } + if memberOpts := resourcePoolMembersV1(d); memberOpts != nil { + for _, memberOpt := range memberOpts { + _, err := members.Create(networkingClient, memberOpt).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack LB member: %s", err) + } + } + } + return resourceLBPoolV1Read(d, meta) } @@ -125,6 +172,7 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { } d.Set("monitor_ids", p.MonitorIDs) + d.Set("member_ids", p.MemberIDs) return nil } @@ -178,6 +226,49 @@ func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { } } + if d.HasChange("member") { + oldMembersRaw, newMembersRaw := d.GetChange("member") + oldMembersSet, newMembersSet := oldMembersRaw.(*schema.Set), newMembersRaw.(*schema.Set) + membersToAdd := newMembersSet.Difference(oldMembersSet) + membersToRemove := oldMembersSet.Difference(newMembersSet) + + log.Printf("[DEBUG] Members to add: %v", membersToAdd) + + log.Printf("[DEBUG] Members to remove: %v", membersToRemove) + + for _, m := range membersToRemove.List() { + oldMember := resourcePoolMemberV1(d, m) + listOpts := members.ListOpts{ + PoolID: d.Id(), + Address: oldMember.Address, + ProtocolPort: oldMember.ProtocolPort, + } + err = members.List(networkingClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { + extractedMembers, err := members.ExtractMembers(page) + if err != nil { + return false, err + } + for _, member := range extractedMembers { + err := members.Delete(networkingClient, member.ID).ExtractErr() + if err != nil { + return false, fmt.Errorf("Error deleting member (%s) from OpenStack LB pool (%s): %s", member.ID, d.Id(), err) + } + log.Printf("[DEBUG] Deleted member (%s) from pool (%s)", member.ID, d.Id()) + } + return true, nil + }) + } + + for _, m := range membersToAdd.List() { + createOpts := resourcePoolMemberV1(d, m) + newMember, err := members.Create(networkingClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating LB member: %s", err) + } + log.Printf("[DEBUG] Created member (%s) in OpenStack LB pool (%s)", newMember.ID, d.Id()) + } + } + return resourceLBPoolV1Read(d, meta) } @@ -205,3 +296,39 @@ func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string { } return mIDs } + +func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts { + memberOptsRaw := (d.Get("member")).(*schema.Set) + memberOpts := make([]members.CreateOpts, memberOptsRaw.Len()) + for i, raw := range memberOptsRaw.List() { + rawMap := raw.(map[string]interface{}) + memberOpts[i] = members.CreateOpts{ + TenantID: rawMap["tenant_id"].(string), + Address: rawMap["address"].(string), + ProtocolPort: rawMap["port"].(int), + PoolID: d.Id(), + } + } + return memberOpts +} + +func resourcePoolMemberV1(d *schema.ResourceData, raw interface{}) members.CreateOpts { + rawMap := raw.(map[string]interface{}) + return members.CreateOpts{ + TenantID: rawMap["tenant_id"].(string), + Address: rawMap["address"].(string), + ProtocolPort: rawMap["port"].(int), + PoolID: d.Id(), + } +} + +func resourceLBMemberV1Hash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["region"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["tenant_id"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["address"].(string))) + buf.WriteString(fmt.Sprintf("%d-", m["port"].(int))) + + return hashcode.String(buf.String()) +} From 33d62bbdbffd41db611288e7bdf2ee2ff89bd746 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:21:57 -0700 Subject: [PATCH 071/167] 'networks' -> 'network' --- .../openstack/resource_openstack_compute_instance_v2.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index a2e66ca2e..8059dec1b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -60,7 +60,7 @@ func resourceComputeInstanceV2() *schema.Resource { Optional: true, ForceNew: true, }, - "networks": &schema.Schema{ + "network": &schema.Schema{ Type: schema.TypeList, Optional: true, ForceNew: true, @@ -430,7 +430,7 @@ func resourceInstanceSecGroupsV2(d *schema.ResourceData) []string { } func resourceInstanceNetworksV2(d *schema.ResourceData) []servers.Network { - rawNetworks := d.Get("networks").([]interface{}) + rawNetworks := d.Get("network").([]interface{}) networks := make([]servers.Network, len(rawNetworks)) for i, raw := range rawNetworks { rawMap := raw.(map[string]interface{}) From 6b2f2df04289b8ff87ae022c54e87b58bc9dccf2 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:22:19 -0700 Subject: [PATCH 072/167] 'rules' -> 'rule' --- .../resource_openstack_compute_secgroup_v2.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index f83ce84c7..f1d9ae714 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -34,7 +34,7 @@ func resourceComputeSecGroupV2() *schema.Resource { Required: true, ForceNew: false, }, - "rules": &schema.Schema{ + "rule": &schema.Schema{ Type: schema.TypeSet, Optional: true, Elem: &schema.Resource{ @@ -66,7 +66,7 @@ func resourceComputeSecGroupV2() *schema.Resource { }, }, }, - Set: resourceSecGroupRuleHash, + Set: resourceSecGroupRuleV2Hash, }, }, } @@ -117,7 +117,7 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("region", d.Get("region").(string)) d.Set("name", sg.Name) d.Set("description", sg.Description) - d.Set("rules", sg.Rules) + d.Set("rule", sg.Rules) return nil } @@ -141,8 +141,8 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err) } - if d.HasChange("rules") { - oldSGRaw, newSGRaw := d.GetChange("rules") + if d.HasChange("rule") { + oldSGRaw, newSGRaw := d.GetChange("rule") oldSGRSet, newSGRSet := oldSGRaw.(*schema.Set), newSGRaw.(*schema.Set) secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet) secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet) @@ -188,7 +188,7 @@ func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) e return nil } -func resourceSecGroupRuleHash(v interface{}) int { +func resourceSecGroupRuleV2Hash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int))) @@ -201,7 +201,7 @@ func resourceSecGroupRuleHash(v interface{}) int { } func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts { - rawRules := (d.Get("rules")).(*schema.Set) + rawRules := (d.Get("rule")).(*schema.Set) createRuleOptsList := make([]secgroups.CreateRuleOpts, rawRules.Len()) for i, raw := range rawRules.List() { rawMap := raw.(map[string]interface{}) @@ -219,7 +219,7 @@ func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts { rawMap := raw.(map[string]interface{}) - createRuleOpts := secgroups.CreateRuleOpts{ + return secgroups.CreateRuleOpts{ ParentGroupID: d.Id(), FromPort: rawMap["from_port"].(int), ToPort: rawMap["to_port"].(int), @@ -227,6 +227,4 @@ func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.C CIDR: rawMap["cidr"].(string), FromGroupID: rawMap["from_group_id"].(string), } - - return createRuleOpts } From b9395b36d28b7267d3968f43495f48ca75a4757f Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:22:37 -0700 Subject: [PATCH 073/167] update client methods --- .../resource_openstack_compute_instance_v2_test.go | 12 +++--------- .../resource_openstack_compute_keypair_v2_test.go | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index ff2457d09..77559fa3a 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -7,8 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) @@ -33,9 +31,7 @@ func TestAccComputeV2Instance_basic(t *testing.T) { func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: OS_REGION_NAME, - }) + computeClient, err := config.computeV2Client(OS_REGION_NAME) if err != nil { return fmt.Errorf("(testAccCheckComputeV2InstanceDestroy) Error creating OpenStack compute client: %s", err) } @@ -65,10 +61,8 @@ func testAccCheckComputeV2InstanceExists(t *testing.T, n string, instance *serve return fmt.Errorf("No ID is set") } - osClient := testAccProvider.Meta().(*Config).osClient - computeClient, err := openstack.NewComputeV2(osClient, gophercloud.EndpointOpts{ - Region: OS_REGION_NAME, - }) + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) if err != nil { return fmt.Errorf("(testAccCheckComputeV2InstanceExists) Error creating OpenStack compute client: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go index 0456341df..da090bcd8 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2_test.go @@ -7,8 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" ) @@ -32,9 +30,7 @@ func TestAccComputeV2Keypair_basic(t *testing.T) { func testAccCheckComputeV2KeypairDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) - computeClient, err := openstack.NewComputeV2(config.osClient, gophercloud.EndpointOpts{ - Region: OS_REGION_NAME, - }) + computeClient, err := config.computeV2Client(OS_REGION_NAME) if err != nil { return fmt.Errorf("(testAccCheckComputeV2KeypairDestroy) Error creating OpenStack compute client: %s", err) } @@ -64,10 +60,8 @@ func testAccCheckComputeV2KeypairExists(t *testing.T, n string, kp *keypairs.Key return fmt.Errorf("No ID is set") } - osClient := testAccProvider.Meta().(*Config).osClient - computeClient, err := openstack.NewComputeV2(osClient, gophercloud.EndpointOpts{ - Region: OS_REGION_NAME, - }) + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) if err != nil { return fmt.Errorf("(testAccCheckComputeV2KeypairExists) Error creating OpenStack compute client: %s", err) } From 7cdb790ece6009594beccec787f26b94108b1bb2 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:35:42 -0700 Subject: [PATCH 074/167] update compute intance docs: 'networks -> 'network' --- .../providers/openstack/r/compute_instance_v2.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 4429174fb..6a9a5ac95 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -48,7 +48,7 @@ The following arguments are supported: * `availability_zone` - (Optional) The availability zone in which to create the server. Changing this creates a new server. -* `networks` - (Optional) An array of one or more networks to attach to the +* `network` - (Optional) An array of one or more networks to attach to the instance. The network object structure is documented below. Changing this creates a new server. From 3427597bd0d9204201a4d7cb25ba8b79a3ae9384 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:37:11 -0700 Subject: [PATCH 075/167] update docs to remove security group rule and lb member files --- .../r/compute_secgroup_v2.html.markdown | 30 ++++++++ .../r/compute_secgrouprule_v2.html.markdown | 69 ------------------- .../openstack/r/lb_member_v1.html.markdown | 58 ---------------- .../openstack/r/lb_pool_v1.html.markdown | 28 +++++++- website/source/layouts/openstack.erb | 6 -- 5 files changed, 57 insertions(+), 134 deletions(-) delete mode 100644 website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown delete mode 100644 website/source/docs/providers/openstack/r/lb_member_v1.html.markdown diff --git a/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown index 86f546ed2..50f328502 100644 --- a/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown @@ -16,6 +16,12 @@ Manages a V2 security group resource within OpenStack. resource "openstack_compute_secgroup_v2" "secgroup_1" { name = "my_secgroup" description = "my security group" + rule { + from_port = 22 + to_port = 22 + ip_protocol = "tcp" + cidr = "0.0.0.0/0" + } } ``` @@ -34,6 +40,29 @@ The following arguments are supported: * `description` - (Required) A description for the security group. Changing this updates the `description` of an existing security group. +* `rule` - (Optional) A rule describing how the security group operates. The + rule object structure is documented below. Changing this updates the + security group rules. + +The `rule` block supports: + +* `from_port` - (Required) An integer representing the lower bound of the port +range to open. Changing this creates a new security group rule. + +* `to_port` - (Required) An integer representing the upper bound of the port +range to open. Changing this creates a new security group rule. + +* `ip_protocol` - (Required) The protocol type that will be allowed. Changing +this creates a new security group rule. + +* `cidr` - (Optional) Required if `from_group_id` is empty. The IP range that +will be the source of network traffic to the security group. Use 0.0.0.0./0 +to allow all IP addresses. Changing this creates a new security group rule. + +* `from_group_id - (Optional) Required if `cidr` is empty. The ID of a group +from which to forward traffic to the parent group. Changing +this creates a new security group rule. + ## Attributes Reference The following attributes are exported: @@ -41,3 +70,4 @@ The following attributes are exported: * `region` - See Argument Reference above. * `name` - See Argument Reference above. * `description` - See Argument Reference above. +* `rule` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown deleted file mode 100644 index 06385a6c8..000000000 --- a/website/source/docs/providers/openstack/r/compute_secgrouprule_v2.html.markdown +++ /dev/null @@ -1,69 +0,0 @@ ---- -layout: "openstack" -page_title: "OpenStack: openstack_compute_secgrouprule_v2" -sidebar_current: "docs-openstack-resource-compute-secgrouprule-v2" -description: |- - Manages a V2 security group rule resource within OpenStack. ---- - -# openstack\_compute\_secgrouprule_v2 - -Manages a V2 security group rule resource within OpenStack. - -## Example Usage - -``` -resource "openstack_compute_secgroup_v2" "secgroup_1" { - name = "my_secgroup" - description = "my security group" -} - -resource "openstack_compute_secgrouprule_v2" "secgrouprule_1" { - group_id = "${openstack_compute_secgroup_v2.secgroup_1.id}" - from_port = 22 - to_port = 22 - ip_protocol = "TCP" - cidr = "0.0.0.0/0" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `region` - (Required) The region in which to obtain the V2 Compute client. - A Compute client is needed to create a security group rule. If omitted, the - `OS_REGION_NAME` environment variable is used. Changing this creates a new - security group rule. - -* `group_id` - (Required) The ID of the group to which this rule will be added. - Changing this creates a new security group rule. - -* `from_port` - (Required) An integer representing the lower bound of the port - range to open. Changing this creates a new security group rule. - -* `to_port` - (Required) An integer representing the upper bound of the port - range to open. Changing this creates a new security group rule. - -* `ip_protocol` - (Required) The protocol type that will be allowed. Changing - this creates a new security group rule. - -* `cidr` - (Optional) Required if `from_group_id` is empty. The IP range that - will be the source of network traffic to the security group. Use 0.0.0.0./0 - to allow all IP addresses. Changing this creates a new security group rule. - -* `from_group_id - (Optional) Required if `cidr` is empty. The ID of a group - from which to forward traffic to the parent group. Changing - this creates a new security group rule. - -## Attributes Reference - -The following attributes are exported: - -* `region` - See Argument Reference above. -* `group_id` - See Argument Reference above. -* `from_port` - See Argument Reference above. -* `to_port` - See Argument Reference above. -* `ip_protocol` - See Argument Reference above. -* `cidr` - See Argument Reference above. -* `from_group_id` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/lb_member_v1.html.markdown b/website/source/docs/providers/openstack/r/lb_member_v1.html.markdown deleted file mode 100644 index 2d1be74dc..000000000 --- a/website/source/docs/providers/openstack/r/lb_member_v1.html.markdown +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: "openstack" -page_title: "OpenStack: openstack_lb_member_v1" -sidebar_current: "docs-openstack-resource-lb-member-v1" -description: |- - Manages a V1 load balancer member resource within OpenStack. ---- - -# openstack\_lb\_member_v1 - -Manages a V1 load balancer member resource within OpenStack. - -## Example Usage - -``` -resource "openstack_lb_member_v1" "node_1" { - address = "196.172.0.1" - port = 80 - pool_id = "12345" - admin_state_up = true -} -``` - -## Argument Reference - -The following arguments are supported: - -* `region` - (Required) The region in which to obtain the V2 Networking client. - A Networking client is needed to create an LB member. If omitted, the - `OS_REGION_NAME` environment variable is used. Changing this creates a new - LB member. - -* `address` - (Required) The IP address of the member. Changing this creates a - new member. - -* `port` - (Required) An integer representing the port on which the member is - hosted. Changing this creates a new member. - -* `pool_id` - (Required) The pool to which this member will belong. Changing - this creates a new member. - -* `admin_state_up` - (Optional) The administrative state of the member. - Acceptable values are 'true' and 'false'. Changing this value updates the - state of the existing member. - -* `tenant_id` - (Optional) The owner of the member. Required if admin wants to - create a pool member for another tenant. Changing this creates a new member. - -## Attributes Reference - -The following attributes are exported: - -* `region` - See Argument Reference above. -* `address` - See Argument Reference above. -* `port` - See Argument Reference above. -* `pool_id` - See Argument Reference above. -* `admin_state_up` - See Argument Reference above. -* `tenant_id` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown b/website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown index f0de6917a..5ddbdf1af 100644 --- a/website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown +++ b/website/source/docs/providers/openstack/r/lb_pool_v1.html.markdown @@ -18,7 +18,12 @@ resource "openstack_lb_pool_v1" "pool_1" { protocol = "HTTP" subnet_id = "12345" lb_method = "ROUND_ROBIN" - monitor_id = "67890" + monitor_ids = ["67890"] + member { + address = "192.168.0.1" + port = 80 + admin_state_up = "true" + } } ``` @@ -51,6 +56,26 @@ The following arguments are supported: * `monitor_ids` - (Optional) A list of IDs of monitors to associate with the pool. +* `member` - (Optional) An existing node to add to the pool. Changing this + updates the members of the pool. The member object structure is documented + below. + +The `member` block supports: + +* `address` - (Required) The IP address of the member. Changing this creates a +new member. + +* `port` - (Required) An integer representing the port on which the member is +hosted. Changing this creates a new member. + +* `admin_state_up` - (Optional) The administrative state of the member. +Acceptable values are 'true' and 'false'. Changing this value updates the +state of the existing member. + +* `tenant_id` - (Optional) The owner of the member. Required if admin wants to +create a pool member for another tenant. Changing this creates a new member. + + ## Attributes Reference The following attributes are exported: @@ -62,3 +87,4 @@ The following attributes are exported: * `lb_method` - See Argument Reference above. * `tenant_id` - See Argument Reference above. * `monitor_id` - See Argument Reference above. +* `member` - See Argument Reference above. diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb index 37b0c2a7c..71cc9eb9e 100644 --- a/website/source/layouts/openstack.erb +++ b/website/source/layouts/openstack.erb @@ -22,12 +22,6 @@ > openstack_compute_secgroup_v2 - > - openstack_compute_secgrouprule_v2 - - > - openstack_lb_member_v1 - > openstack_lb_monitor_v1 From fa15d41d45cd9a10615194c498b899a1b36ccf85 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:55:42 -0700 Subject: [PATCH 076/167] UpdateOpts not optional --- .../resource_openstack_lb_monitor_v1.go | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index ca5ad7659..7483b17e4 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -171,25 +171,15 @@ func resourceLBMonitorV1Update(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } - var updateOpts monitors.UpdateOpts - if d.HasChange("delay") { - updateOpts.Delay = d.Get("delay").(int) - } - if d.HasChange("timeout") { - updateOpts.Timeout = d.Get("timeout").(int) - } - if d.HasChange("max_retries") { - updateOpts.MaxRetries = d.Get("max_retries").(int) - } - if d.HasChange("url_path") { - updateOpts.URLPath = d.Get("url_path").(string) - } - if d.HasChange("http_method") { - updateOpts.HTTPMethod = d.Get("http_method").(string) - } - if d.HasChange("expected_codes") { - updateOpts.ExpectedCodes = d.Get("expected_codes").(string) + updateOpts := monitors.UpdateOpts{ + Delay: d.Get("delay").(int), + Timeout: d.Get("timeout").(int), + MaxRetries: d.Get("max_retries").(int), + URLPath: d.Get("url_path").(string), + HTTPMethod: d.Get("http_method").(string), + ExpectedCodes: d.Get("expected_codes").(string), } + if d.HasChange("admin_state_up") { asuRaw := d.Get("admin_state_up").(string) if asuRaw != "" { From 66129632b358a44949443590b89d5aed1a72e061 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:56:11 -0700 Subject: [PATCH 077/167] security groups acceptance tests --- ...urce_openstack_compute_secgroup_v2_test.go | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_compute_secgroup_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2_test.go new file mode 100644 index 000000000..e78865b8a --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2_test.go @@ -0,0 +1,90 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" +) + +func TestAccComputeV2SecGroup_basic(t *testing.T) { + var secgroup secgroups.SecurityGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2SecGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2SecGroup_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.foo", &secgroup), + ), + }, + }, + }) +} + +func testAccCheckComputeV2SecGroupDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2SecGroupDestroy) Error creating OpenStack compute client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_compute_secgroup_v2" { + continue + } + + _, err := secgroups.Get(computeClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Security group still exists") + } + } + + return nil +} + +func testAccCheckComputeV2SecGroupExists(t *testing.T, n string, secgroup *secgroups.SecurityGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2SecGroupExists) Error creating OpenStack compute client: %s", err) + } + + found, err := secgroups.Get(computeClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Security group not found") + } + + *secgroup = *found + + return nil + } +} + +var testAccComputeV2SecGroup_basic = fmt.Sprintf(` + resource "openstack_compute_secgroup_v2" "foo" { + region = "%s" + name = "test_group_1" + description = "first test security group" + }`, + OS_REGION_NAME) From e08e97304f91d9d674e874ac4acb505bb0be6d73 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:56:27 -0700 Subject: [PATCH 078/167] lb monitors acceptance tests --- .../resource_openstack_lb_monitor_v1_test.go | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_monitor_v1_test.go diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1_test.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1_test.go new file mode 100644 index 000000000..5aaf61d2c --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1_test.go @@ -0,0 +1,110 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors" +) + +func TestAccLBV1Monitor_basic(t *testing.T) { + var monitor monitors.Monitor + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLBV1MonitorDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccLBV1Monitor_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckLBV1MonitorExists(t, "openstack_lb_monitor_v1.monitor_1", &monitor), + ), + }, + resource.TestStep{ + Config: testAccLBV1Monitor_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_lb_monitor_v1.monitor_1", "delay", "20"), + ), + }, + }, + }) +} + +func testAccCheckLBV1MonitorDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckLBV1MonitorDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_lb_monitor_v1" { + continue + } + + _, err := monitors.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("LB monitor still exists") + } + } + + return nil +} + +func testAccCheckLBV1MonitorExists(t *testing.T, n string, monitor *monitors.Monitor) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckLBV1MonitorExists) Error creating OpenStack networking client: %s", err) + } + + found, err := monitors.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Monitor not found") + } + + *monitor = *found + + return nil + } +} + +var testAccLBV1Monitor_basic = fmt.Sprintf(` + resource "openstack_lb_monitor_v1" "monitor_1" { + region = "%s" + type = "PING" + delay = 30 + timeout = 5 + max_retries = 3 + admin_state_up = "true" + }`, + OS_REGION_NAME) + +var testAccLBV1Monitor_update = fmt.Sprintf(` + resource "openstack_lb_monitor_v1" "monitor_1" { + region = "%s" + type = "PING" + delay = 20 + timeout = 5 + max_retries = 3 + admin_state_up = "true" + }`, + OS_REGION_NAME) From e7a69d0a6c59450459bfe9aedc1065591ad24771 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:56:40 -0700 Subject: [PATCH 079/167] lb pools acceptance tests --- .../resource_openstack_lb_pool_v1_test.go | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go new file mode 100644 index 000000000..1889c2384 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go @@ -0,0 +1,134 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" +) + +func TestAccLBV1Pool_basic(t *testing.T) { + var pool pools.Pool + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLBV1PoolDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccLBV1Pool_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckLBV1PoolExists(t, "openstack_lb_pool_v1.pool_1", &pool), + ), + }, + resource.TestStep{ + Config: testAccLBV1Pool_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_lb_pool_v1.pool_1", "name", "tf_test_lb_pool_updated"), + ), + }, + }, + }) +} + +func testAccCheckLBV1PoolDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckLBV1PoolDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_lb_pool_v1" { + continue + } + + _, err := pools.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("LB Pool still exists") + } + } + + return nil +} + +func testAccCheckLBV1PoolExists(t *testing.T, n string, pool *pools.Pool) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckLBV1PoolExists) Error creating OpenStack networking client: %s", err) + } + + found, err := pools.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Pool not found") + } + + *pool = *found + + return nil + } +} + +var testAccLBV1Pool_basic = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } + + resource "openstack_lb_pool_v1" "pool_1" { + region = "%s" + name = "tf_test_lb_pool" + protocol = "HTTP" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + lb_method = "ROUND_ROBIN" + }`, + OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME) + +var testAccLBV1Pool_update = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } + + resource "openstack_lb_pool_v1" "pool_1" { + region = "%s" + name = "tf_test_lb_pool_updated" + protocol = "HTTP" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + lb_method = "ROUND_ROBIN" + }`, + OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME) From d46d9a6540fd2375db3c494d5f70ac7eaa8f0d84 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:56:54 -0700 Subject: [PATCH 080/167] lb vips acceptance tests --- .../resource_openstack_lb_vip_v1_test.go | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_lb_vip_v1_test.go diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1_test.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1_test.go new file mode 100644 index 000000000..f30cd9d56 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1_test.go @@ -0,0 +1,152 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" +) + +func TestAccLBV1VIP_basic(t *testing.T) { + var vip vips.VirtualIP + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLBV1VIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccLBV1VIP_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckLBV1VIPExists(t, "openstack_lb_vip_v1.vip_1", &vip), + ), + }, + resource.TestStep{ + Config: testAccLBV1VIP_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_lb_vip_v1.vip_1", "name", "tf_test_lb_vip_updated"), + ), + }, + }, + }) +} + +func testAccCheckLBV1VIPDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckLBV1VIPDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_lb_vip_v1" { + continue + } + + _, err := vips.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("LB VIP still exists") + } + } + + return nil +} + +func testAccCheckLBV1VIPExists(t *testing.T, n string, vip *vips.VirtualIP) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckLBV1VIPExists) Error creating OpenStack networking client: %s", err) + } + + found, err := vips.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("VIP not found") + } + + *vip = *found + + return nil + } +} + +var testAccLBV1VIP_basic = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } + + resource "openstack_lb_pool_v1" "pool_1" { + region = "%s" + name = "tf_test_lb_pool" + protocol = "HTTP" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + lb_method = "ROUND_ROBIN" + } + + resource "openstack_lb_vip_v1" "vip_1" { + region = "RegionOne" + name = "tf_test_lb_vip" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + protocol = "HTTP" + port = 80 + pool_id = "${openstack_lb_pool_v1.pool_1.id}" + }`, + OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME) + +var testAccLBV1VIP_update = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + } + + resource "openstack_lb_pool_v1" "pool_1" { + region = "%s" + name = "tf_test_lb_pool" + protocol = "HTTP" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + lb_method = "ROUND_ROBIN" + } + + resource "openstack_lb_vip_v1" "vip_1" { + region = "RegionOne" + name = "tf_test_lb_vip_updated" + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + protocol = "HTTP" + port = 80 + pool_id = "${openstack_lb_pool_v1.pool_1.id}" + }`, + OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME) From e0409340776a65abc7ada949f1c92bb34e8cabb1 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:57:10 -0700 Subject: [PATCH 081/167] networking networks acceptance tests --- ...ce_openstack_networking_network_v2_test.go | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_networking_network_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go new file mode 100644 index 000000000..5bff60532 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2_test.go @@ -0,0 +1,104 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/networks" +) + +func TestAccNetworkingV2Network_basic(t *testing.T) { + var network networks.Network + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2NetworkDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Network_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2NetworkExists(t, "openstack_networking_network_v2.foo", &network), + ), + }, + resource.TestStep{ + Config: testAccNetworkingV2Network_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_networking_network_v2.foo", "name", "network_2"), + ), + }, + }, + }) +} + +func testAccCheckNetworkingV2NetworkDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2NetworkDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_networking_network_v2" { + continue + } + + _, err := networks.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Network still exists") + } + } + + return nil +} + +func testAccCheckNetworkingV2NetworkExists(t *testing.T, n string, network *networks.Network) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2NetworkExists) Error creating OpenStack networking client: %s", err) + } + + found, err := networks.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Network not found") + } + + *network = *found + + return nil + } +} + +var testAccNetworkingV2Network_basic = fmt.Sprintf(` + resource "openstack_networking_network_v2" "foo" { + region = "%s" + name = "network_1" + admin_state_up = "true" + }`, + OS_REGION_NAME) + +var testAccNetworkingV2Network_update = fmt.Sprintf(` + resource "openstack_networking_network_v2" "foo" { + region = "%s" + name = "network_2" + admin_state_up = "true" + }`, + OS_REGION_NAME) From 08672e697ebe92849f387241c6d36f202da8d63c Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 17:57:25 -0700 Subject: [PATCH 082/167] networking subnets acceptance tests --- ...rce_openstack_networking_subnet_v2_test.go | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_networking_subnet_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2_test.go new file mode 100644 index 000000000..d7f6116e9 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2_test.go @@ -0,0 +1,119 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/subnets" +) + +func TestAccNetworkingV2Subnet_basic(t *testing.T) { + var subnet subnets.Subnet + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2SubnetDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Subnet_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2SubnetExists(t, "openstack_networking_subnet_v2.subnet_1", &subnet), + ), + }, + resource.TestStep{ + Config: testAccNetworkingV2Subnet_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "name", "tf-test-subnet"), + resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "gateway_ip", "192.68.0.1"), + ), + }, + }, + }) +} + +func testAccCheckNetworkingV2SubnetDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2SubnetDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_networking_subnet_v2" { + continue + } + + _, err := subnets.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Subnet still exists") + } + } + + return nil +} + +func testAccCheckNetworkingV2SubnetExists(t *testing.T, n string, subnet *subnets.Subnet) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2SubnetExists) Error creating OpenStack networking client: %s", err) + } + + found, err := subnets.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Subnet not found") + } + + *subnet = *found + + return nil + } +} + +var testAccNetworkingV2Subnet_basic = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + }`, OS_REGION_NAME, OS_REGION_NAME) + +var testAccNetworkingV2Subnet_update = fmt.Sprintf(` + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + name = "tf-test-subnet" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + gateway_ip = "192.68.0.1" + }`, OS_REGION_NAME, OS_REGION_NAME) From 43564d1c5cf3f7533615699574bbceec9f1390c0 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sat, 31 Jan 2015 20:51:50 -0700 Subject: [PATCH 083/167] object storage container v1 ops --- builtin/providers/openstack/config.go | 6 + builtin/providers/openstack/provider.go | 17 +- ...ce_openstack_objectstorage_container_v1.go | 148 ++++++++++++++++++ 3 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index 2e37c4fd0..864dc9ab7 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -53,3 +53,9 @@ func (c *Config) networkingV2Client(region string) (*gophercloud.ServiceClient, Region: region, }) } + +func (c *Config) objectStorageV1Client(region string) (*gophercloud.ServiceClient, error) { + return openstack.NewObjectStorageV1(c.osClient, gophercloud.EndpointOpts{ + Region: region, + }) +} diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index a53882f84..019c7b386 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -59,14 +59,15 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "openstack_compute_instance_v2": resourceComputeInstanceV2(), - "openstack_compute_keypair_v2": resourceComputeKeypairV2(), - "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), - "openstack_lb_monitor_v1": resourceLBMonitorV1(), - "openstack_lb_pool_v1": resourceLBPoolV1(), - "openstack_lb_vip_v1": resourceLBVipV1(), - "openstack_networking_network_v2": resourceNetworkingNetworkV2(), - "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), + "openstack_compute_instance_v2": resourceComputeInstanceV2(), + "openstack_compute_keypair_v2": resourceComputeKeypairV2(), + "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), + "openstack_lb_monitor_v1": resourceLBMonitorV1(), + "openstack_lb_pool_v1": resourceLBPoolV1(), + "openstack_lb_vip_v1": resourceLBVipV1(), + "openstack_networking_network_v2": resourceNetworkingNetworkV2(), + "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), + "openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(), }, ConfigureFunc: configureProvider, diff --git a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go new file mode 100644 index 000000000..f9b5d5250 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go @@ -0,0 +1,148 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers" +) + +func resourceObjectStorageContainerV1() *schema.Resource { + return &schema.Resource{ + Create: resourceObjectStorageContainerV1Create, + Read: resourceObjectStorageContainerV1Read, + Update: resourceObjectStorageContainerV1Update, + Delete: resourceObjectStorageContainerV1Delete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "container_read": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "container_sync_to": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "container_sync_key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "container_write": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "content_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "metadata": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: false, + }, + }, + } +} + +func resourceObjectStorageContainerV1Create(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + objectStorgeClient, err := config.objectStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack object storage client: %s", err) + } + + cn := d.Get("name").(string) + + createOpts := &containers.CreateOpts{ + ContainerRead: d.Get("container_read").(string), + ContainerSyncTo: d.Get("container_sync_to").(string), + ContainerSyncKey: d.Get("container_sync_key").(string), + ContainerWrite: d.Get("container_write").(string), + ContentType: d.Get("content_type").(string), + Metadata: resourceContainerMetadataV2(d), + } + + log.Printf("[INFO] Requesting container creation") + _, err = containers.Create(objectStorgeClient, cn, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack container: %s", err) + } + log.Printf("[INFO] Container ID: %s", cn) + + // Store the ID now + d.SetId(cn) + + return resourceObjectStorageContainerV1Read(d, meta) +} + +func resourceObjectStorageContainerV1Read(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceObjectStorageContainerV1Update(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + objectStorgeClient, err := config.objectStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack object storage client: %s", err) + } + + updateOpts := containers.UpdateOpts{ + ContainerRead: d.Get("container_read").(string), + ContainerSyncTo: d.Get("container_sync_to").(string), + ContainerSyncKey: d.Get("container_sync_key").(string), + ContainerWrite: d.Get("container_write").(string), + ContentType: d.Get("content_type").(string), + } + + _, err = containers.Update(objectStorgeClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack container: %s", err) + } + + if d.HasChange("metadata") { + updateOpts.Metadata = resourceContainerMetadataV2(d) + } + + return resourceObjectStorageContainerV1Read(d, meta) +} + +func resourceObjectStorageContainerV1Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + objectStorgeClient, err := config.objectStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack object storage client: %s", err) + } + + _, err = containers.Delete(objectStorgeClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error deleting OpenStack container: %s", err) + } + + d.SetId("") + return nil +} + +func resourceContainerMetadataV2(d *schema.ResourceData) map[string]string { + m := make(map[string]string) + for key, val := range d.Get("metadata").(map[string]interface{}) { + m[key] = val.(string) + } + return m +} From a5147f472bfcd3b5c02785ebd69e4ed5a788899b Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 00:14:43 -0700 Subject: [PATCH 084/167] update metadata before actual Update op --- .../resource_openstack_objectstorage_container_v1.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go index f9b5d5250..687e65b57 100644 --- a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go +++ b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go @@ -111,15 +111,15 @@ func resourceObjectStorageContainerV1Update(d *schema.ResourceData, meta interfa ContentType: d.Get("content_type").(string), } + if d.HasChange("metadata") { + updateOpts.Metadata = resourceContainerMetadataV2(d) + } + _, err = containers.Update(objectStorgeClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack container: %s", err) } - if d.HasChange("metadata") { - updateOpts.Metadata = resourceContainerMetadataV2(d) - } - return resourceObjectStorageContainerV1Read(d, meta) } From f1ac6dbfec477132673a683c4cdd0ea9ede244ce Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 00:15:19 -0700 Subject: [PATCH 085/167] block storage volume v1 ops --- ...source_openstack_blockstorage_volume_v1.go | 226 ++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go new file mode 100644 index 000000000..ee86b51d4 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -0,0 +1,226 @@ +package openstack + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" +) + +func resourceBlockStorageVolumeV1() *schema.Resource { + return &schema.Resource{ + Create: resourceBlockStorageVolumeV1Create, + Read: resourceBlockStorageVolumeV1Read, + Update: resourceBlockStorageVolumeV1Update, + Delete: resourceBlockStorageVolumeV1Delete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "size": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "metadata": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: false, + }, + "snapshot_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "source_vol_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "image_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "volume_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceBlockStorageVolumeV1Create(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } + + createOpts := &volumes.CreateOpts{ + Description: d.Get("description").(string), + Name: d.Get("name").(string), + Size: d.Get("size").(int), + SnapshotID: d.Get("snapshot_id").(string), + SourceVolID: d.Get("source_vol_id").(string), + ImageID: d.Get("image_id").(string), + VolumeType: d.Get("volume_type").(string), + Metadata: resourceContainerMetadataV2(d), + } + + log.Printf("[INFO] Requesting volume creation") + v, err := volumes.Create(blockStorageClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack volume: %s", err) + } + log.Printf("[INFO] Volume ID: %s", v.ID) + + // Store the ID now + d.SetId(v.ID) + + // Wait for the volume to become running. + log.Printf( + "[DEBUG] Waiting for volume (%s) to become running", + v.ID) + + stateConf := &resource.StateChangeConf{ + Target: "available", + Refresh: VolumeV1StateRefreshFunc(blockStorageClient, v.ID), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for volume (%s) to become ready: %s", + v.ID, err) + } + + return resourceBlockStorageVolumeV1Read(d, meta) +} + +func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } + + v, err := volumes.Get(blockStorageClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving OpenStack volume: %s", err) + } + + log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v) + + d.Set("region", d.Get("region").(string)) + d.Set("description", v.Description) + d.Set("name", v.Name) + d.Set("size", v.Size) + d.Set("snapshot_id", v.SnapshotID) + d.Set("source_vol_id", v.SourceVolID) + d.Set("volume_type", v.VolumeType) + d.Set("metadata", v.Metadata) + + return nil +} + +func resourceBlockStorageVolumeV1Update(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } + + updateOpts := volumes.UpdateOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + } + + if d.HasChange("metadata") { + updateOpts.Metadata = resourceVolumeMetadataV1(d) + } + + _, err = volumes.Update(blockStorageClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack volume: %s", err) + } + + return resourceBlockStorageVolumeV1Read(d, meta) +} + +func resourceBlockStorageVolumeV1Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + blockStorageClient, err := config.blockStorageV1Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } + + err = volumes.Delete(blockStorageClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack volume: %s", err) + } + + // Wait for the volume to delete before moving on. + log.Printf("[DEBUG] Waiting for volume (%s) to delete", d.Id()) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"deleting"}, + Target: "", + Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for volume (%s) to delete: %s", + d.Id(), err) + } + + d.SetId("") + return nil +} + +func resourceVolumeMetadataV1(d *schema.ResourceData) map[string]string { + m := make(map[string]string) + for key, val := range d.Get("metadata").(map[string]interface{}) { + m[key] = val.(string) + } + return m +} + +// VolumeV1StateRefreshFunc returns a resource.StateRefreshFunc that is used to watch +// an OpenStack volume. +func VolumeV1StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + v, _ := volumes.Get(client, volumeID).Extract() + if v != nil { + return v, v.Status, nil + } + return nil, "", nil + } +} From acd5a033f0614cd2fd03b75c0bde32bfde46e8f0 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 00:23:53 -0700 Subject: [PATCH 086/167] fix typo in client variable name --- .../resource_openstack_objectstorage_container_v1.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go index 687e65b57..692ed2e46 100644 --- a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go +++ b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go @@ -63,7 +63,7 @@ func resourceObjectStorageContainerV1() *schema.Resource { func resourceObjectStorageContainerV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - objectStorgeClient, err := config.objectStorageV1Client(d.Get("region").(string)) + objectStorageClient, err := config.objectStorageV1Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack object storage client: %s", err) } @@ -80,7 +80,7 @@ func resourceObjectStorageContainerV1Create(d *schema.ResourceData, meta interfa } log.Printf("[INFO] Requesting container creation") - _, err = containers.Create(objectStorgeClient, cn, createOpts).Extract() + _, err = containers.Create(objectStorageClient, cn, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack container: %s", err) } @@ -98,7 +98,7 @@ func resourceObjectStorageContainerV1Read(d *schema.ResourceData, meta interface func resourceObjectStorageContainerV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - objectStorgeClient, err := config.objectStorageV1Client(d.Get("region").(string)) + objectStorageClient, err := config.objectStorageV1Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack object storage client: %s", err) } @@ -115,7 +115,7 @@ func resourceObjectStorageContainerV1Update(d *schema.ResourceData, meta interfa updateOpts.Metadata = resourceContainerMetadataV2(d) } - _, err = containers.Update(objectStorgeClient, d.Id(), updateOpts).Extract() + _, err = containers.Update(objectStorageClient, d.Id(), updateOpts).Extract() if err != nil { return fmt.Errorf("Error updating OpenStack container: %s", err) } @@ -125,12 +125,12 @@ func resourceObjectStorageContainerV1Update(d *schema.ResourceData, meta interfa func resourceObjectStorageContainerV1Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - objectStorgeClient, err := config.objectStorageV1Client(d.Get("region").(string)) + objectStorageClient, err := config.objectStorageV1Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack object storage client: %s", err) } - _, err = containers.Delete(objectStorgeClient, d.Id()).Extract() + _, err = containers.Delete(objectStorageClient, d.Id()).Extract() if err != nil { return fmt.Errorf("Error deleting OpenStack container: %s", err) } From d2169e0e96c30210d5d2e8753c2927b5d31b2446 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 00:33:53 -0700 Subject: [PATCH 087/167] block storage v1 acceptance tests --- ...e_openstack_blockstorage_volume_v1_test.go | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_blockstorage_volume_v1_test.go diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1_test.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1_test.go new file mode 100644 index 000000000..5404fd391 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1_test.go @@ -0,0 +1,138 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" +) + +func TestAccBlockStorageV1Volume_basic(t *testing.T) { + var volume volumes.Volume + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBlockStorageV1VolumeDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccBlockStorageV1Volume_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckBlockStorageV1VolumeExists(t, "openstack_blockstorage_volume_v1.volume_1", &volume), + resource.TestCheckResourceAttr("openstack_blockstorage_volume_v1.volume_1", "name", "tf-test-volume"), + testAccCheckBlockStorageV1VolumeMetadata(&volume, "foo", "bar"), + ), + }, + resource.TestStep{ + Config: testAccBlockStorageV1Volume_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_blockstorage_volume_v1.volume_1", "name", "tf-test-volume-updated"), + testAccCheckBlockStorageV1VolumeMetadata(&volume, "foo", "bar"), + ), + }, + }, + }) +} + +func testAccCheckBlockStorageV1VolumeDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + blockStorageClient, err := config.blockStorageV1Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_blockstorage_volume_v1" { + continue + } + + _, err := volumes.Get(blockStorageClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Volume still exists") + } + } + + return nil +} + +func testAccCheckBlockStorageV1VolumeExists(t *testing.T, n string, volume *volumes.Volume) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + blockStorageClient, err := config.blockStorageV1Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } + + found, err := volumes.Get(blockStorageClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Volume not found") + } + + *volume = *found + + return nil + } +} + +func testAccCheckBlockStorageV1VolumeMetadata( + volume *volumes.Volume, k string, v string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if volume.Metadata == nil { + return fmt.Errorf("No metadata") + } + + for key, value := range volume.Metadata { + if k != key { + continue + } + + if v == value { + return nil + } + + return fmt.Errorf("Bad value for %s: %s", k, value) + } + + return fmt.Errorf("Metadata not found: %s", k) + } +} + +var testAccBlockStorageV1Volume_basic = fmt.Sprintf(` + resource "openstack_blockstorage_volume_v1" "volume_1" { + region = "%s" + name = "tf-test-volume" + description = "first test volume" + metadata{ + foo = "bar" + } + size = 1 + }`, + OS_REGION_NAME) + +var testAccBlockStorageV1Volume_update = fmt.Sprintf(` + resource "openstack_blockstorage_volume_v1" "volume_1" { + region = "%s" + name = "tf-test-volume-updated" + description = "first test volume" + metadata{ + foo = "bar" + } + size = 1 + }`, + OS_REGION_NAME) From a85067062d911f1370a54741a90515f9f3f3b7b5 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 00:34:08 -0700 Subject: [PATCH 088/167] object storage v1 acceptance tests --- ...enstack_objectstorage_container_v1_test.go | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_objectstorage_container_v1_test.go diff --git a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1_test.go b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1_test.go new file mode 100644 index 000000000..9377ad2fb --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1_test.go @@ -0,0 +1,77 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers" +) + +func TestAccObjectStorageV1Container_basic(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckObjectStorageV1ContainerDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccObjectStorageV1Container_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_objectstorage_container_v1.container_1", "name", "tf-test-container"), + resource.TestCheckResourceAttr("openstack_objectstorage_container_v1.container_1", "content_type", "application/json"), + ), + }, + resource.TestStep{ + Config: testAccObjectStorageV1Container_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_objectstorage_container_v1.container_1", "content_type", "text/plain"), + ), + }, + }, + }) +} + +func testAccCheckObjectStorageV1ContainerDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + objectStorageClient, err := config.objectStorageV1Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("Error creating OpenStack object storage client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_objectstorage_container_v1" { + continue + } + + _, err := containers.Get(objectStorageClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Container still exists") + } + } + + return nil +} + +var testAccObjectStorageV1Container_basic = fmt.Sprintf(` + resource "openstack_objectstorage_container_v1" "container_1" { + region = "%s" + name = "tf-test-container" + metadata { + test = "true" + } + content_type = "application/json" + }`, + OS_REGION_NAME) + +var testAccObjectStorageV1Container_update = fmt.Sprintf(` + resource "openstack_objectstorage_container_v1" "container_1" { + region = "%s" + name = "tf-test-container" + metadata { + test = "true" + } + content_type = "text/plain" + }`, + OS_REGION_NAME) From 761d58df2f3cc4f2507b3950a53ec982d3884ac2 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 00:34:37 -0700 Subject: [PATCH 089/167] add container and volume resources --- builtin/providers/openstack/config.go | 6 ++++++ builtin/providers/openstack/provider.go | 1 + 2 files changed, 7 insertions(+) diff --git a/builtin/providers/openstack/config.go b/builtin/providers/openstack/config.go index 864dc9ab7..d05662017 100644 --- a/builtin/providers/openstack/config.go +++ b/builtin/providers/openstack/config.go @@ -42,6 +42,12 @@ func (c *Config) loadAndValidate() error { return nil } +func (c *Config) blockStorageV1Client(region string) (*gophercloud.ServiceClient, error) { + return openstack.NewBlockStorageV1(c.osClient, gophercloud.EndpointOpts{ + Region: region, + }) +} + func (c *Config) computeV2Client(region string) (*gophercloud.ServiceClient, error) { return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{ Region: region, diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 019c7b386..180b5c3a7 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -59,6 +59,7 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ + "openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(), "openstack_compute_instance_v2": resourceComputeInstanceV2(), "openstack_compute_keypair_v2": resourceComputeKeypairV2(), "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), From 9c128b7c9964727fe9d25aa3601fa930585d8c83 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 01:11:01 -0700 Subject: [PATCH 090/167] docs for volume and container resources --- .../r/blockstorage_volume_v1.html.markdown | 68 +++++++++++++++++++ .../objectstorage_container_v1.html.markdown | 68 +++++++++++++++++++ website/source/layouts/openstack.erb | 6 ++ 3 files changed, 142 insertions(+) create mode 100644 website/source/docs/providers/openstack/r/blockstorage_volume_v1.html.markdown create mode 100644 website/source/docs/providers/openstack/r/objectstorage_container_v1.html.markdown diff --git a/website/source/docs/providers/openstack/r/blockstorage_volume_v1.html.markdown b/website/source/docs/providers/openstack/r/blockstorage_volume_v1.html.markdown new file mode 100644 index 000000000..0b91eb11c --- /dev/null +++ b/website/source/docs/providers/openstack/r/blockstorage_volume_v1.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_blockstorage_volume_v1" +sidebar_current: "docs-openstack-resource-blockstorage-volume-v1" +description: |- +Manages a V1 volume resource within OpenStack. +--- + +# openstack\_blockstorage\_volume_v1 + +Manages a V1 volume resource within OpenStack. + +## Example Usage + +``` +resource "openstack_blockstorage_volume_v1" "volume_1" { + region = "RegionOne" + name = "tf-test-volume" + description = "first test volume" + size = 3 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Required) The region in which to create the volume. If + omitted, the `OS_REGION_NAME` environment variable is used. Changing this + creates a new volume. + +* `size` - (Required) The size of the volume to create (in gigabytes). Changing + this creates a new volume. + +* `name` - (Optional) A unique name for the volume. Changing this updates the + volume's name. + +* `description` - (Optional) A description of the volume. Changing this updates + the volume's description. + +* `image_id` - (Optional) The image ID from which to create the volume. + Changing this creates a new volume. + +* `snapshot_id` - (Optional) The snapshot ID from which to create the volume. + Changing this creates a new volume. + +* `source_vol_id` - (Optional) The volume ID from which to create the volume. + Changing this creates a new volume. + +* `metadata` - (Optional) Metadata key/value pairs to associate with the volume. + Changing this updates the existing volume metadata. + +* `volume_type` - (Optional) The type of volume to create (either SATA or SSD). + Changing this creates a new volume. + +## Attributes Reference + +The following attributes are exported: + +* `region` - See Argument Reference above. +* `size` - See Argument Reference above. +* `name` - See Argument Reference above. +* `description` - See Argument Reference above. +* `image_id` - See Argument Reference above. +* `source_vol_id` - See Argument Reference above. +* `snapshot_id` - See Argument Reference above. +* `metadata` - See Argument Reference above. +* `volume_type` - See Argument Reference above. diff --git a/website/source/docs/providers/openstack/r/objectstorage_container_v1.html.markdown b/website/source/docs/providers/openstack/r/objectstorage_container_v1.html.markdown new file mode 100644 index 000000000..8101d1ca2 --- /dev/null +++ b/website/source/docs/providers/openstack/r/objectstorage_container_v1.html.markdown @@ -0,0 +1,68 @@ +--- +layout: "openstack" +page_title: "OpenStack: openstack_objectstorage_container_v1" +sidebar_current: "docs-openstack-resource-objectstorage-container-v1" +description: |- +Manages a V1 container resource within OpenStack. +--- + +# openstack\_objectstorage\_container_v1 + +Manages a V1 container resource within OpenStack. + +## Example Usage + +``` +resource "openstack_objectstorage_container_v1" "container_1" { + region = "RegionOne" + name = "tf-test-container-1" + metadata { + test = "true" + } + content_type = "application/json" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Required) The region in which to create the container. If + omitted, the `OS_REGION_NAME` environment variable is used. Changing this + creates a new container. + +* `name` - (Required) A unique name for the container. Changing this creates a + new container. + +* `container_read` - (Optional) Sets an access control list (ACL) that grants + read access. This header can contain a comma-delimited list of users that + can read the container (allows the GET method for all objects in the + container). Changing this updates the access control list read access. + +* `container_sync_to` - (Optional) The destination for container synchronization. + Changing this updates container synchronization. + +* `container_sync_key` - (Optional) The secret key for container synchronization. + Changing this updates container synchronization. + +* `container_write` - (Optional) Sets an ACL that grants write access. + Changing this updates the access control list write access. + +* `metadata` - (Optional) Custom key/value pairs to associate with the container. + Changing this updates the existing container metadata. + +* `content_type` - (Optional) The MIME type for the container. Changing this + updates the MIME type. + +## Attributes Reference + +The following attributes are exported: + +* `region` - See Argument Reference above. +* `name` - See Argument Reference above. +* `container_read` - See Argument Reference above. +* `container_sync_to` - See Argument Reference above. +* `container_sync_key` - See Argument Reference above. +* `container_write` - See Argument Reference above. +* `metadata` - See Argument Reference above. +* `content_type` - See Argument Reference above. diff --git a/website/source/layouts/openstack.erb b/website/source/layouts/openstack.erb index 71cc9eb9e..22afb4aeb 100644 --- a/website/source/layouts/openstack.erb +++ b/website/source/layouts/openstack.erb @@ -13,6 +13,9 @@ > Resources From 436ef9e53ba74b975ae8d29106c0efc35f17a3d6 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 02:10:42 -0700 Subject: [PATCH 091/167] boot from volume ops and docs --- .../resource_openstack_compute_instance_v2.go | 41 +++++++++++++++++-- .../r/compute_instance_v2.html.markdown | 17 ++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 8059dec1b..766c5dea8 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" @@ -36,13 +37,13 @@ func resourceComputeInstanceV2() *schema.Resource { }, "image_ref": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: false, DefaultFunc: envDefaultFunc("OS_IMAGE_ID"), }, "flavor_ref": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: false, DefaultFunc: envDefaultFunc("OS_FLAVOR_ID"), }, @@ -113,6 +114,11 @@ func resourceComputeInstanceV2() *schema.Resource { Optional: true, ForceNew: true, }, + "block_device": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, }, } } @@ -145,6 +151,14 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e } } + if blockDeviceRaw, ok := d.Get("block_device").(map[string]interface{}); ok && blockDeviceRaw != nil { + blockDevice := resourceInstanceBlockDeviceV2(d, blockDeviceRaw) + createOpts = &bootfromvolume.CreateOptsExt{ + createOpts, + blockDevice, + } + } + log.Printf("[INFO] Requesting instance creation") server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { @@ -407,7 +421,7 @@ func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) e return nil } -// ServerStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch +// ServerV2StateRefreshFunc returns a resource.StateRefreshFunc that is used to watch // an OpenStack instance. func ServerV2StateRefreshFunc(client *gophercloud.ServiceClient, instanceID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { @@ -450,3 +464,24 @@ func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string { } return m } + +func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interface{}) []bootfromvolume.BlockDevice { + sourceType := bootfromvolume.SourceType(bd["source_type"].(string)) + bfvOpts := []bootfromvolume.BlockDevice{ + bootfromvolume.BlockDevice{ + UUID: bd["uuid"].(string), + SourceType: sourceType, + }, + } + if vs, ok := bd["volume_size"].(int); ok { + bfvOpts[0].VolumeSize = vs + } + if dt, ok := bd["destination_type"].(string); ok { + bfvOpts[0].DestinationType = dt + } + if bi, ok := bd["boot_index"].(int); ok { + bfvOpts[0].BootIndex = bi + } + + return bfvOpts +} diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 6a9a5ac95..9095cb15a 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -62,6 +62,9 @@ The following arguments are supported: pair must already be created and associated with the tenant's account. Changing this creates a new server. +* `block_device` - (Optional) The object for booting by volume. The block_device + object structure is documented below. Changing this creates a new server. + The `network` block supports: * `uuid` - (Required unless `port` is provided) The network UUID to attach to @@ -73,6 +76,20 @@ The `network` block supports: * `fixed_ip` - (Optional) Specifies a fixed IP address to be used on this network. +The `block_device` block supports: + +* `uuid` - (Required) The UUID of the image, volume, or snapshot. + +* `source_type` - (Required) The source type of the device. Must be one of + "image", "volume", or "snapshot". + +* `volume_size` - (Optional) The size of the volume to create (in gigabytes). + +* `boot_index` - (Optional) The boot index of the volume. It defaults to 0. + +* `destination_type` - (Optional) The type that gets created. Possible values + are "volume" and "local". + ## Attributes Reference The following attributes are exported: From e2634562a4fd6288d5e81615d2da851cf0effbeb Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Sun, 1 Feb 2015 02:36:58 -0700 Subject: [PATCH 092/167] define block_device schema --- .../resource_openstack_compute_instance_v2.go | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 766c5dea8..17775ef3a 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -115,9 +115,33 @@ func resourceComputeInstanceV2() *schema.Resource { ForceNew: true, }, "block_device": &schema.Schema{ - Type: schema.TypeMap, + Type: schema.TypeList, Optional: true, ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "uuid": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "source_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "volume_size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + "destination_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "boot_index": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + }, + }, + }, }, }, } @@ -469,19 +493,13 @@ func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interfa sourceType := bootfromvolume.SourceType(bd["source_type"].(string)) bfvOpts := []bootfromvolume.BlockDevice{ bootfromvolume.BlockDevice{ - UUID: bd["uuid"].(string), - SourceType: sourceType, + UUID: bd["uuid"].(string), + SourceType: sourceType, + VolumeSize: bd["volume_size"].(int), + DestinationType: bd["destination_type"].(string), + BootIndex: bd["boot_index"].(int), }, } - if vs, ok := bd["volume_size"].(int); ok { - bfvOpts[0].VolumeSize = vs - } - if dt, ok := bd["destination_type"].(string); ok { - bfvOpts[0].DestinationType = dt - } - if bi, ok := bd["boot_index"].(int); ok { - bfvOpts[0].BootIndex = bi - } return bfvOpts } From ccd51ae3ab796e86558b74b17901c217e0291c8e Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Tue, 3 Feb 2015 20:30:10 -0700 Subject: [PATCH 093/167] added ok codes to gophercloud -> update ServerV2StateRefreshFunc --- ...source_openstack_blockstorage_volume_v1.go | 67 +++++++++++++++---- .../resource_openstack_compute_instance_v2.go | 10 ++- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go index ee86b51d4..ea2d99a65 100644 --- a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" ) @@ -97,9 +98,9 @@ func resourceBlockStorageVolumeV1Create(d *schema.ResourceData, meta interface{} // Store the ID now d.SetId(v.ID) - // Wait for the volume to become running. + // Wait for the volume to become available. log.Printf( - "[DEBUG] Waiting for volume (%s) to become running", + "[DEBUG] Waiting for volume (%s) to become available", v.ID) stateConf := &resource.StateChangeConf{ @@ -132,16 +133,48 @@ func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error retrieving OpenStack volume: %s", err) } + log.Printf("\n\ngot volume: %+v\n\n", v) + log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v) d.Set("region", d.Get("region").(string)) - d.Set("description", v.Description) - d.Set("name", v.Name) d.Set("size", v.Size) - d.Set("snapshot_id", v.SnapshotID) - d.Set("source_vol_id", v.SourceVolID) - d.Set("volume_type", v.VolumeType) - d.Set("metadata", v.Metadata) + + if t, exists := d.GetOk("description"); exists && t != "" { + d.Set("description", v.Description) + } else { + d.Set("description", "") + } + + if t, exists := d.GetOk("name"); exists && t != "" { + d.Set("name", v.Name) + } else { + d.Set("name", "") + } + + if t, exists := d.GetOk("snapshot_id"); exists && t != "" { + d.Set("snapshot_id", v.SnapshotID) + } else { + d.Set("snapshot_id", "") + } + + if t, exists := d.GetOk("source_vol_id"); exists && t != "" { + d.Set("source_vol_id", v.SourceVolID) + } else { + d.Set("source_vol_id", "") + } + + if t, exists := d.GetOk("volume_type"); exists && t != "" { + d.Set("volume_type", v.VolumeType) + } else { + d.Set("volume_type", "") + } + + if t, exists := d.GetOk("metadata"); exists && t != "" { + d.Set("metadata", v.Metadata) + } else { + d.Set("metadata", "") + } return nil } @@ -187,7 +220,7 @@ func resourceBlockStorageVolumeV1Delete(d *schema.ResourceData, meta interface{} stateConf := &resource.StateChangeConf{ Pending: []string{"deleting"}, - Target: "", + Target: "deleted", Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()), Timeout: 10 * time.Minute, Delay: 10 * time.Second, @@ -217,10 +250,18 @@ func resourceVolumeMetadataV1(d *schema.ResourceData) map[string]string { // an OpenStack volume. func VolumeV1StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - v, _ := volumes.Get(client, volumeID).Extract() - if v != nil { - return v, v.Status, nil + v, err := volumes.Get(client, volumeID).Extract() + if err != nil { + errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return nil, "", err + } + if errCode.Actual == 404 { + return v, "deleted", nil + } + return nil, "", err } - return nil, "", nil + + return v, v.Status, nil } } diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 17775ef3a..7b3db0c6c 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" @@ -427,7 +428,7 @@ func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Waiting for instance (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ - Target: "", + Target: "DELETED", Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), Timeout: 10 * time.Minute, Delay: 10 * time.Second, @@ -451,6 +452,13 @@ func ServerV2StateRefreshFunc(client *gophercloud.ServiceClient, instanceID stri return func() (interface{}, string, error) { s, err := servers.Get(client, instanceID).Extract() if err != nil { + errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return nil, "", err + } + if errCode.Actual == 404 { + return s, "DELETED", nil + } return nil, "", err } From 8e9c6787dd048e422541342b9108c5c64ee2c5c8 Mon Sep 17 00:00:00 2001 From: Julien Vey Date: Wed, 4 Feb 2015 22:14:36 +0100 Subject: [PATCH 094/167] Just try the first IP available if none found before Some cloud don't implement correctly IP addresses. Instead of failing during the provisionning, we just take the first IP available and try with this one. --- .../resource_openstack_compute_instance_v2.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 7b3db0c6c..2d9a0cdb8 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -246,11 +246,25 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err pa := paRaw.(map[string]interface{}) if pa["version"].(float64) == 4 { host = pa["addr"].(string) - d.Set("access_ip_v4", host) + break } } } } + + // If no host found, just get the first IP we find + if host == "" { + for _, networkAddresses := range server.Addresses { + for _, element := range networkAddresses.([]interface{}) { + address := element.(map[string]interface{}) + if address["version"].(float64) == 4 { + host = address["addr"].(string) + break + } + } + } + } + d.Set("access_ip_v4", host) d.Set("host", host) log.Printf("host: %s", host) From aae87816f672e43aa41b5e3b77855feb817610e8 Mon Sep 17 00:00:00 2001 From: Julien Vey Date: Wed, 4 Feb 2015 22:15:11 +0100 Subject: [PATCH 095/167] add ACTIVE as pending state when deleting instance --- .../openstack/resource_openstack_compute_instance_v2.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 2d9a0cdb8..65c2b45da 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -442,6 +442,7 @@ func resourceComputeInstanceV2Delete(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Waiting for instance (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ + Pending: []string{"ACTIVE"}, Target: "DELETED", Refresh: ServerV2StateRefreshFunc(computeClient, d.Id()), Timeout: 10 * time.Minute, From 9aa9c902482c3fd3131334737cd07d75bfc1b38b Mon Sep 17 00:00:00 2001 From: Julien Vey Date: Thu, 5 Feb 2015 08:23:46 +0100 Subject: [PATCH 096/167] Add floating IP resource --- builtin/providers/openstack/provider.go | 1 + ...urce_openstack_networking_floatingip_v2.go | 160 ++++++++++++++++++ ...openstack_networking_floatingip_v2_test.go | 89 ++++++++++ 3 files changed, 250 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go create mode 100644 builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 180b5c3a7..955101a44 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -68,6 +68,7 @@ func Provider() terraform.ResourceProvider { "openstack_lb_vip_v1": resourceLBVipV1(), "openstack_networking_network_v2": resourceNetworkingNetworkV2(), "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), + "openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(), "openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(), }, diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go new file mode 100644 index 000000000..410fb0e39 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go @@ -0,0 +1,160 @@ +package openstack + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/rackspace/gophercloud/openstack/networking/v2/networks" + "github.com/rackspace/gophercloud/pagination" +) + +func resourceNetworkingFloatingIPV2() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkFloatingIPV2Create, + Read: resourceNetworkFloatingIPV2Read, + Delete: resourceNetworkFloatingIPV2Delete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "pool": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceNetworkFloatingIPV2Create(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack network client: %s", err) + } + + poolID, err := getNetworkID(d, meta, d.Get("pool").(string)) + if err != nil { + return fmt.Errorf("Error retrieving floating IP pool name: %s", err) + } + if len(poolID) == 0 { + return fmt.Errorf("No network found with name: %s", d.Get("pool").(string)) + } + floatingIP, err := floatingips.Create(networkClient, floatingips.CreateOpts{ + FloatingNetworkID: poolID, + }).Extract() + if err != nil { + return fmt.Errorf("Error allocating floating IP: %s", err) + } + + d.SetId(floatingIP.ID) + + return resourceNetworkFloatingIPV2Read(d, meta) +} + +func resourceNetworkFloatingIPV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack network client: %s", err) + } + + floatingIP, err := floatingips.Get(networkClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error retrieving floatingIP: %s", err) + } + + d.Set("region", d.Get("region").(string)) + d.Set("address", floatingIP.FloatingIP) + poolName, err := getNetworkName(d, meta, floatingIP.FloatingNetworkID) + if err != nil { + return fmt.Errorf("Error retrieving floating IP pool name: %s", err) + } + d.Set("pool", poolName) + + return nil +} + +func resourceNetworkFloatingIPV2Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack network client: %s", err) + } + + err = floatingips.Delete(networkClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting floating IP: %s", err) + } + d.SetId("") + return nil +} + +func getNetworkID(d *schema.ResourceData, meta interface{}, networkName string) (string, error) { + config := meta.(*Config) + networkClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return "", fmt.Errorf("Error creating OpenStack network client: %s", err) + } + + opts := networks.ListOpts{Name: networkName} + pager := networks.List(networkClient, opts) + networkID := "" + + err = pager.EachPage(func(page pagination.Page) (bool, error) { + networkList, err := networks.ExtractNetworks(page) + if err != nil { + return false, err + } + + for _, n := range networkList { + if n.Name == networkName { + networkID = n.ID + return false, nil + } + } + + return true, nil + }) + + return networkID, err +} + +func getNetworkName(d *schema.ResourceData, meta interface{}, networkID string) (string, error) { + config := meta.(*Config) + networkClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return "", fmt.Errorf("Error creating OpenStack network client: %s", err) + } + + opts := networks.ListOpts{ID: networkID} + pager := networks.List(networkClient, opts) + networkName := "" + + err = pager.EachPage(func(page pagination.Page) (bool, error) { + networkList, err := networks.ExtractNetworks(page) + if err != nil { + return false, err + } + + for _, n := range networkList { + if n.ID == networkID { + networkName = n.Name + return false, nil + } + } + + return true, nil + }) + + return networkName, err +} diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go new file mode 100644 index 000000000..390275928 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go @@ -0,0 +1,89 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" +) + +func TestAccNetworkingV2FloatingIP_basic(t *testing.T) { + var floatingIP floatingips.FloatingIP + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2FloatingIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2FloatingIP_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2FloatingIPExists(t, "openstack_networking_floatingip_v2.foo", &floatingIP), + ), + }, + }, + }) +} + +func testAccCheckNetworkingV2FloatingIPDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2FloatingIPDestroy) Error creating OpenStack floating IP: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_networking_floatingip_v2" { + continue + } + + _, err := floatingips.Get(networkClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("FloatingIP still exists") + } + } + + return nil +} + +func testAccCheckNetworkingV2FloatingIPExists(t *testing.T, n string, kp *floatingips.FloatingIP) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2FloatingIPExists) Error creating OpenStack networking client: %s", err) + } + + found, err := floatingips.Get(networkClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("FloatingIP not found") + } + + *kp = *found + + return nil + } +} + +var testAccNetworkingV2FloatingIP_basic = fmt.Sprintf(` + resource "openstack_networking_floatingip_v2" "foo" { + region = "%s" + pool = "PublicNetwork-01" + }`, + OS_REGION_NAME) From 760e03856e536f296ce4449aa1789148d96d8cba Mon Sep 17 00:00:00 2001 From: Julien Vey Date: Fri, 6 Feb 2015 14:34:11 +0100 Subject: [PATCH 097/167] Manage floating IP in compute instances --- .../resource_openstack_compute_instance_v2.go | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 65c2b45da..a5807100f 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -14,6 +14,9 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" + "github.com/rackspace/gophercloud/openstack/networking/v2/networks" + "github.com/rackspace/gophercloud/openstack/networking/v2/ports" "github.com/rackspace/gophercloud/pagination" ) @@ -48,6 +51,11 @@ func resourceComputeInstanceV2() *schema.Resource { ForceNew: false, DefaultFunc: envDefaultFunc("OS_FLAVOR_ID"), }, + "floating_ip": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, "security_groups": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -215,6 +223,22 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e "Error waiting for instance (%s) to become ready: %s", server.ID, err) } + floatingIP := d.Get("floating_ip").(string) + if len(floatingIP) > 0 { + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + + allFloatingIPs, err := getFloatingIPs(networkingClient) + if err != nil { + return fmt.Errorf("Error listing OpenStack floating IPs: %s", err) + } + err = assignFloatingIP(networkingClient, extractFloatingIPFromIP(allFloatingIPs, floatingIP), server.ID) + if err != nil { + fmt.Errorf("Error assigning floating IP to OpenStack compute instance: %s", err) + } + } return resourceComputeInstanceV2Read(d, meta) } @@ -375,6 +399,25 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e } } + if d.HasChange("floating_ip") { + floatingIP := d.Get("floating_ip").(string) + if len(floatingIP) > 0 { + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + + allFloatingIPs, err := getFloatingIPs(networkingClient) + if err != nil { + return fmt.Errorf("Error listing OpenStack floating IPs: %s", err) + } + err = assignFloatingIP(networkingClient, extractFloatingIPFromIP(allFloatingIPs, floatingIP), d.Id()) + if err != nil { + fmt.Errorf("Error assigning floating IP to OpenStack compute instance: %s", err) + } + } + } + if d.HasChange("flavor_ref") { resizeOpts := &servers.ResizeOpts{ FlavorRef: d.Get("flavor_ref").(string), @@ -526,3 +569,93 @@ func resourceInstanceBlockDeviceV2(d *schema.ResourceData, bd map[string]interfa return bfvOpts } + +func extractFloatingIPFromIP(ips []floatingips.FloatingIP, IP string) *floatingips.FloatingIP { + for _, floatingIP := range ips { + if floatingIP.FloatingIP == IP { + return &floatingIP + } + } + return nil +} + +func assignFloatingIP(networkingClient *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, instanceID string) error { + networkID, err := getFirstNetworkID(networkingClient, instanceID) + if err != nil { + return err + } + portID, err := getInstancePortID(networkingClient, instanceID, networkID) + _, err = floatingips.Update(networkingClient, floatingIP.ID, floatingips.UpdateOpts{ + PortID: portID, + }).Extract() + return err +} + +func getFirstNetworkID(networkingClient *gophercloud.ServiceClient, instanceID string) (string, error) { + pager := networks.List(networkingClient, networks.ListOpts{}) + + var networkdID string + err := pager.EachPage(func(page pagination.Page) (bool, error) { + networkList, err := networks.ExtractNetworks(page) + if err != nil { + return false, err + } + + if len(networkList) > 0 { + networkdID = networkList[0].ID + return false, nil + } + return false, fmt.Errorf("No network found for the instance %s", instanceID) + }) + if err != nil { + return "", err + } + return networkdID, nil + +} + +func getInstancePortID(networkingClient *gophercloud.ServiceClient, instanceID, networkID string) (string, error) { + pager := ports.List(networkingClient, ports.ListOpts{ + DeviceID: instanceID, + NetworkID: networkID, + }) + + var portID string + err := pager.EachPage(func(page pagination.Page) (bool, error) { + portList, err := ports.ExtractPorts(page) + if err != nil { + return false, err + } + for _, port := range portList { + portID = port.ID + return false, nil + } + return true, nil + }) + + if err != nil { + return "", err + } + return portID, nil +} + +func getFloatingIPs(networkingClient *gophercloud.ServiceClient) ([]floatingips.FloatingIP, error) { + pager := floatingips.List(networkingClient, floatingips.ListOpts{}) + + ips := []floatingips.FloatingIP{} + err := pager.EachPage(func(page pagination.Page) (bool, error) { + floatingipList, err := floatingips.ExtractFloatingIPs(page) + if err != nil { + return false, err + } + for _, f := range floatingipList { + ips = append(ips, f) + } + return true, nil + }) + + if err != nil { + return nil, err + } + return ips, nil +} From 132d5acb33a1a637d065cd2f35e12d53ba589361 Mon Sep 17 00:00:00 2001 From: Julien Vey Date: Mon, 9 Feb 2015 13:27:30 +0100 Subject: [PATCH 098/167] Make pool name configurable in tests --- builtin/providers/openstack/provider_test.go | 7 +++++++ .../openstack/resource_openstack_compute_instance_v2.go | 4 ++-- .../resource_openstack_networking_floatingip_v2_test.go | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go index 9e90bb4ea..d98ec1820 100644 --- a/builtin/providers/openstack/provider_test.go +++ b/builtin/providers/openstack/provider_test.go @@ -10,6 +10,7 @@ import ( var ( OS_REGION_NAME = "" + OS_POOL_NAME = "" ) var testAccProviders map[string]terraform.ResourceProvider @@ -49,6 +50,12 @@ func testAccPreCheck(t *testing.T) { t.Fatal("OS_IMAGE_ID must be set for acceptance tests") } + v = os.Getenv("OS_POOL_NAME") + if v == "" { + t.Fatal("OS_POOL_NAME must be set for acceptance tests") + } + OS_POOL_NAME = v + v = os.Getenv("OS_FLAVOR_ID") if v == "" { t.Fatal("OS_FLAVOR_ID must be set for acceptance tests") diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index a5807100f..bcf30a6ee 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -224,7 +224,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e server.ID, err) } floatingIP := d.Get("floating_ip").(string) - if len(floatingIP) > 0 { + if floatingIP != "" { networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) @@ -401,7 +401,7 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e if d.HasChange("floating_ip") { floatingIP := d.Get("floating_ip").(string) - if len(floatingIP) > 0 { + if floatingIP != "" { networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { return fmt.Errorf("Error creating OpenStack compute client: %s", err) diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go index 390275928..cd08ea512 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go @@ -84,6 +84,6 @@ func testAccCheckNetworkingV2FloatingIPExists(t *testing.T, n string, kp *floati var testAccNetworkingV2FloatingIP_basic = fmt.Sprintf(` resource "openstack_networking_floatingip_v2" "foo" { region = "%s" - pool = "PublicNetwork-01" + pool = "%s" }`, - OS_REGION_NAME) + OS_REGION_NAME, OS_POOL_NAME) From 32d0e36709b75844f697239c607245017013b385 Mon Sep 17 00:00:00 2001 From: Eric Bellemon Date: Mon, 9 Feb 2015 23:37:39 +0100 Subject: [PATCH 099/167] Add router resource --- builtin/providers/openstack/provider.go | 1 + ...resource_openstack_networking_router_v2.go | 190 ++++++++++++++++++ ...rce_openstack_networking_router_v2_test.go | 104 ++++++++++ 3 files changed, 295 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_networking_router_v2.go create mode 100644 builtin/providers/openstack/resource_openstack_networking_router_v2_test.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 955101a44..64e87cbb4 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -69,6 +69,7 @@ func Provider() terraform.ResourceProvider { "openstack_networking_network_v2": resourceNetworkingNetworkV2(), "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), "openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(), + "openstack_networking_router_v2": resourceNetworkingRouterV2(), "openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(), }, diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_v2.go new file mode 100644 index 000000000..c4fe8e90a --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2.go @@ -0,0 +1,190 @@ +package openstack + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers" +) + +func resourceNetworkingRouterV2() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkingRouterV2Create, + Read: resourceNetworkingRouterV2Read, + Update: resourceNetworkingRouterV2Update, + Delete: resourceNetworkingRouterV2Delete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "admin_state_up": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "external_gateway": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + }, + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + createOpts := routers.CreateOpts{ + Name: d.Get("name").(string), + TenantID: d.Get("tenant_id").(string), + } + + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + createOpts.AdminStateUp = &asu + } + + externalGateway := d.Get("external_gateway").(string) + if externalGateway != "" { + gatewayInfo := routers.GatewayInfo{ + NetworkID: externalGateway, + } + createOpts.GatewayInfo = &gatewayInfo + } + + log.Printf("[INFO] Requesting router creation") + n, err := routers.Create(networkingClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack Neutron router: %s", err) + } + log.Printf("[INFO] Router ID: %s", n.ID) + + d.SetId(n.ID) + + return resourceNetworkingRouterV2Read(d, meta) +} + +func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + n, err := routers.Get(networkingClient, d.Id()).Extract() + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err) + } + + if httpError.Actual == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err) + } + + log.Printf("[DEBUG] Retreived Router %s: %+v", d.Id(), n) + + d.Set("region", d.Get("region").(string)) + + if t, exists := d.GetOk("name"); exists && t != "" { + d.Set("name", n.Name) + } else { + d.Set("name", "") + } + + if t, exists := d.GetOk("admin_state_up"); exists && t != "" { + d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) + } else { + d.Set("admin_state_up", "") + } + + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", n.TenantID) + } else { + d.Set("tenant_id", "") + } + + if t, exists := d.GetOk("external_gateway"); exists && t != "" { + d.Set("external_gateway", n.GatewayInfo.NetworkID) + } else { + d.Set("external_gateway", "") + } + + return nil +} + +func resourceNetworkingRouterV2Update(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + var updateOpts routers.UpdateOpts + if d.HasChange("name") { + updateOpts.Name = d.Get("name").(string) + } + if d.HasChange("admin_state_up") { + asuRaw := d.Get("admin_state_up").(string) + if asuRaw != "" { + asu, err := strconv.ParseBool(asuRaw) + if err != nil { + return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'") + } + updateOpts.AdminStateUp = &asu + } + } + + log.Printf("[DEBUG] Updating Router %s with options: %+v", d.Id(), updateOpts) + + _, err = routers.Update(networkingClient, d.Id(), updateOpts).Extract() + if err != nil { + return fmt.Errorf("Error updating OpenStack Neutron Router: %s", err) + } + + return resourceNetworkingRouterV2Read(d, meta) +} + +func resourceNetworkingRouterV2Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + err = routers.Delete(networkingClient, d.Id()).ExtractErr() + if err != nil { + return fmt.Errorf("Error deleting OpenStack Neutron Router: %s", err) + } + + d.SetId("") + return nil +} diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go new file mode 100644 index 000000000..57356cb74 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go @@ -0,0 +1,104 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers" +) + +func TestAccNetworkingV2Router_basic(t *testing.T) { + var router routers.Router + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2RouterDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Router_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2RouterExists(t, "openstack_networking_router_v2.foo", &router), + ), + }, + resource.TestStep{ + Config: testAccNetworkingV2Router_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("openstack_networking_router_v2.foo", "name", "router_2"), + ), + }, + }, + }) +} + +func testAccCheckNetworkingV2RouterDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2RouterDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_networking_router_v2" { + continue + } + + _, err := routers.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Router still exists") + } + } + + return nil +} + +func testAccCheckNetworkingV2RouterExists(t *testing.T, n string, router *routers.Router) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2RouterExists) Error creating OpenStack networking client: %s", err) + } + + found, err := routers.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Router not found") + } + + *router = *found + + return nil + } +} + +var testAccNetworkingV2Router_basic = fmt.Sprintf(` + resource "openstack_networking_router_v2" "foo" { + name = "router" + region = "%s" + admin_state_up = "true" + }`, + OS_REGION_NAME) + +var testAccNetworkingV2Router_update = fmt.Sprintf(` + resource "openstack_networking_router_v2" "foo" { + region = "%s" + name = "router_2" + admin_state_up = "true" + }`, + OS_REGION_NAME) From e9abf04e4b750a6d078be187e50918bb1435b290 Mon Sep 17 00:00:00 2001 From: Eric Bellemon Date: Mon, 9 Feb 2015 23:37:58 +0100 Subject: [PATCH 100/167] Add router interface resource --- builtin/providers/openstack/provider.go | 25 ++-- ...penstack_networking_router_interface_v2.go | 109 ++++++++++++++++++ ...ack_networking_router_interface_v2_test.go | 104 +++++++++++++++++ 3 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go create mode 100644 builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 64e87cbb4..450e1c705 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -59,18 +59,19 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(), - "openstack_compute_instance_v2": resourceComputeInstanceV2(), - "openstack_compute_keypair_v2": resourceComputeKeypairV2(), - "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), - "openstack_lb_monitor_v1": resourceLBMonitorV1(), - "openstack_lb_pool_v1": resourceLBPoolV1(), - "openstack_lb_vip_v1": resourceLBVipV1(), - "openstack_networking_network_v2": resourceNetworkingNetworkV2(), - "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), - "openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(), - "openstack_networking_router_v2": resourceNetworkingRouterV2(), - "openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(), + "openstack_blockstorage_volume_v1": resourceBlockStorageVolumeV1(), + "openstack_compute_instance_v2": resourceComputeInstanceV2(), + "openstack_compute_keypair_v2": resourceComputeKeypairV2(), + "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), + "openstack_lb_monitor_v1": resourceLBMonitorV1(), + "openstack_lb_pool_v1": resourceLBPoolV1(), + "openstack_lb_vip_v1": resourceLBVipV1(), + "openstack_networking_network_v2": resourceNetworkingNetworkV2(), + "openstack_networking_subnet_v2": resourceNetworkingSubnetV2(), + "openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(), + "openstack_networking_router_v2": resourceNetworkingRouterV2(), + "openstack_networking_router_interface_v2": resourceNetworkingRouterInterfaceV2(), + "openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(), }, ConfigureFunc: configureProvider, diff --git a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go new file mode 100644 index 000000000..2daaf787d --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go @@ -0,0 +1,109 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers" + "github.com/rackspace/gophercloud/openstack/networking/v2/ports" +) + +func resourceNetworkingRouterInterfaceV2() *schema.Resource { + return &schema.Resource{ + Create: resourceNetworkingRouterInterfaceV2Create, + Read: resourceNetworkingRouterInterfaceV2Read, + Delete: resourceNetworkingRouterInterfaceV2Delete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "router_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "subnet_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceNetworkingRouterInterfaceV2Create(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + createOpts := routers.InterfaceOpts{ + SubnetID: d.Get("subnet_id").(string), + } + + log.Printf("[INFO] Requesting router interface creation") + n, err := routers.AddInterface(networkingClient, d.Get("router_id").(string), createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating OpenStack Neutron router interface: %s", err) + } + log.Printf("[INFO] Router interface Port ID: %s", n.PortID) + + d.SetId(n.PortID) + + return resourceNetworkingRouterInterfaceV2Read(d, meta) +} + +func resourceNetworkingRouterInterfaceV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + n, err := ports.Get(networkingClient, d.Id()).Extract() + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err) + } + + if httpError.Actual == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err) + } + + log.Printf("[DEBUG] Retreived Router Interface %s: %+v", d.Id(), n) + + d.Set("region", d.Get("region").(string)) + + return nil +} + +func resourceNetworkingRouterInterfaceV2Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + removeOpts := routers.InterfaceOpts{ + SubnetID: d.Get("subnet_id").(string), + } + + _, err = routers.RemoveInterface(networkingClient, d.Get("router_id").(string), removeOpts).Extract() + if err != nil { + return fmt.Errorf("Error deleting OpenStack Neutron Router Interface: %s", err) + } + + d.SetId("") + return nil +} diff --git a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go new file mode 100644 index 000000000..a74dbc30b --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go @@ -0,0 +1,104 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/networking/v2/ports" +) + +func TestAccNetworkingV2RouterInterface_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2RouterInterfaceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2RouterInterface_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2RouterInterfaceExists(t, "openstack_networking_router_interface_v2.int_1"), + ), + }, + }, + }) +} + +func testAccCheckNetworkingV2RouterInterfaceDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2RouterInterfaceDestroy) Error creating OpenStack networking client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_networking_router_interface_v2" { + continue + } + + _, err := ports.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Router interface still exists") + } + } + + return nil +} + +func testAccCheckNetworkingV2RouterInterfaceExists(t *testing.T, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckNetworkingV2RouterInterfaceExists) Error creating OpenStack networking client: %s", err) + } + + found, err := ports.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("Router interface not found") + } + + return nil + } +} + +var testAccNetworkingV2RouterInterface_basic = fmt.Sprintf(` + resource "openstack_networking_router_v2" "router_1" { + name = "router_1" + region = "%s" + admin_state_up = "true" + } + + resource "openstack_networking_router_interface_v2" "int_1" { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + router_id = "${openstack_networking_router_v2.router_1.id}" + region = "%s" + } + + resource "openstack_networking_network_v2" "network_1" { + region = "%s" + name = "network_1" + admin_state_up = "true" + } + + resource "openstack_networking_subnet_v2" "subnet_1" { + region = "%s" + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 + }`, OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME) From 9b30ef4eb2c8284364b5ed18869cdba572f0b2fd Mon Sep 17 00:00:00 2001 From: Eric Bellemon Date: Mon, 9 Feb 2015 23:55:21 +0100 Subject: [PATCH 101/167] Remove region properties on acceptance tests --- ...ack_networking_router_interface_v2_test.go | 38 +++++++++---------- ...rce_openstack_networking_router_v2_test.go | 20 ++++------ 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go index a74dbc30b..be3b12c0b 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2_test.go @@ -78,27 +78,23 @@ func testAccCheckNetworkingV2RouterInterfaceExists(t *testing.T, n string) resou } var testAccNetworkingV2RouterInterface_basic = fmt.Sprintf(` - resource "openstack_networking_router_v2" "router_1" { - name = "router_1" - region = "%s" - admin_state_up = "true" - } +resource "openstack_networking_router_v2" "router_1" { + name = "router_1" + admin_state_up = "true" +} - resource "openstack_networking_router_interface_v2" "int_1" { - subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" - router_id = "${openstack_networking_router_v2.router_1.id}" - region = "%s" - } +resource "openstack_networking_router_interface_v2" "int_1" { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + router_id = "${openstack_networking_router_v2.router_1.id}" +} - resource "openstack_networking_network_v2" "network_1" { - region = "%s" - name = "network_1" - admin_state_up = "true" - } +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} - resource "openstack_networking_subnet_v2" "subnet_1" { - region = "%s" - network_id = "${openstack_networking_network_v2.network_1.id}" - cidr = "192.168.199.0/24" - ip_version = 4 - }`, OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME) +resource "openstack_networking_subnet_v2" "subnet_1" { + network_id = "${openstack_networking_network_v2.network_1.id}" + cidr = "192.168.199.0/24" + ip_version = 4 +}`) diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go index 57356cb74..248f4e721 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2_test.go @@ -88,17 +88,13 @@ func testAccCheckNetworkingV2RouterExists(t *testing.T, n string, router *router } var testAccNetworkingV2Router_basic = fmt.Sprintf(` - resource "openstack_networking_router_v2" "foo" { - name = "router" - region = "%s" - admin_state_up = "true" - }`, - OS_REGION_NAME) + resource "openstack_networking_router_v2" "foo" { + name = "router" + admin_state_up = "true" + }`) var testAccNetworkingV2Router_update = fmt.Sprintf(` - resource "openstack_networking_router_v2" "foo" { - region = "%s" - name = "router_2" - admin_state_up = "true" - }`, - OS_REGION_NAME) + resource "openstack_networking_router_v2" "foo" { + name = "router_2" + admin_state_up = "true" + }`) From fafa9468711f9ce1c851afdce06a80c9f17fd9bb Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Mon, 9 Feb 2015 20:27:39 -0700 Subject: [PATCH 102/167] handle 404 (Not Found) in Get operations --- ...source_openstack_blockstorage_volume_v1.go | 2 +- .../resource_openstack_compute_instance_v2.go | 2 +- .../resource_openstack_compute_keypair_v2.go | 2 +- .../resource_openstack_compute_secgroup_v2.go | 2 +- .../resource_openstack_lb_monitor_v1.go | 2 +- .../resource_openstack_lb_pool_v1.go | 2 +- .../openstack/resource_openstack_lb_vip_v1.go | 2 +- ...urce_openstack_networking_floatingip_v2.go | 2 +- ...esource_openstack_networking_network_v2.go | 2 +- ...resource_openstack_networking_subnet_v2.go | 2 +- builtin/providers/openstack/util.go | 22 +++++++++++++++++++ 11 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 builtin/providers/openstack/util.go diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go index ea2d99a65..ab85317bb 100644 --- a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -130,7 +130,7 @@ func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{}) v, err := volumes.Get(blockStorageClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack volume: %s", err) + return CheckDeleted(d, err, "volume") } log.Printf("\n\ngot volume: %+v\n\n", v) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index bcf30a6ee..a60279c3d 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -252,7 +252,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err server, err := servers.Get(computeClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack server: %s", err) + return CheckDeleted(d, err, "server") } log.Printf("[DEBUG] Retreived Server %s: %+v", d.Id(), server) diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index 8cdc84913..9c1417412 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -65,7 +65,7 @@ func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) erro kp, err := keypairs.Get(computeClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack keypair: %s", err) + return CheckDeleted(d, err, "keypair") } d.Set("region", d.Get("region").(string)) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index f1d9ae714..cb61e7458 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -111,7 +111,7 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err sg, err := secgroups.Get(computeClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack security group: %s", err) + return CheckDeleted(d, err, "security group") } d.Set("region", d.Get("region").(string)) diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index 7483b17e4..0761ec1f4 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -120,7 +120,7 @@ func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { m, err := monitors.Get(networkingClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack LB Monitor: %s", err) + return CheckDeleted(d, err, "LB monitor") } log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m) diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index a7b5abbaf..7e3abdbf7 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -154,7 +154,7 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { p, err := pools.Get(networkingClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack LB Pool: %s", err) + return CheckDeleted(d, err, "LB pool") } log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p) diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index cef3977c8..6181bc8a5 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -133,7 +133,7 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { p, err := vips.Get(networkingClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack LB VIP: %s", err) + return CheckDeleted(d, err, "LB VIP") } log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p) diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go index 410fb0e39..fb4965768 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go @@ -70,7 +70,7 @@ func resourceNetworkFloatingIPV2Read(d *schema.ResourceData, meta interface{}) e floatingIP, err := floatingips.Get(networkClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving floatingIP: %s", err) + return CheckDeleted(d, err, "floating IP") } d.Set("region", d.Get("region").(string)) diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index fc420b2e9..bf556f418 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -98,7 +98,7 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e n, err := networks.Get(networkingClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack Neutron Network: %s", err) + return CheckDeleted(d, err, "network") } log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n) diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index 74a43e27c..8462611df 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -155,7 +155,7 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er s, err := subnets.Get(networkingClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error retrieving OpenStack Neutron Subnet: %s", err) + return CheckDeleted(d, err, "subnet") } log.Printf("[DEBUG] Retreived Subnet %s: %+v", d.Id(), s) diff --git a/builtin/providers/openstack/util.go b/builtin/providers/openstack/util.go new file mode 100644 index 000000000..7ed9f8f5f --- /dev/null +++ b/builtin/providers/openstack/util.go @@ -0,0 +1,22 @@ +package openstack + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" +) + +// CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so, +// sets the resource ID to the empty string instead of throwing an error. +func CheckDeleted(d *schema.ResourceData, err error, resource string) error { + errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return fmt.Errorf("Error retrieving OpenStack %s: %s", resource, err) + } + if errCode.Actual == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving OpenStack %s: %s", resource, err) +} From f51a53000f54ef180a1f3a3e4cd30628b72c769c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Feb 2015 05:20:23 +0000 Subject: [PATCH 103/167] Support for image_name This commit renames image_ref to image_id and adds the image_name parameter. Users can now specify either an image UUID or image name when launching instances. image_name is preferrable as deployers/sysadmins generally regularly deprecate/remove outdated and insecure images. Using a consistent naming scheme allows end-users to always retrieve a working image. --- builtin/providers/openstack/provider_test.go | 8 +-- .../resource_openstack_compute_instance_v2.go | 51 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go index d98ec1820..cafda7ef8 100644 --- a/builtin/providers/openstack/provider_test.go +++ b/builtin/providers/openstack/provider_test.go @@ -45,9 +45,11 @@ func testAccPreCheck(t *testing.T) { } OS_REGION_NAME = v - v = os.Getenv("OS_IMAGE_ID") - if v == "" { - t.Fatal("OS_IMAGE_ID must be set for acceptance tests") + v1 := os.Getenv("OS_IMAGE_ID") + v2 := os.Getenv("OS_IMAGE_NAME") + + if v1 == "" && v2 == "" { + t.Fatal("OS_IMAGE_ID or OS_IMAGE_NAME must be set for acceptance tests") } v = os.Getenv("OS_POOL_NAME") diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index a60279c3d..c56f86792 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -13,6 +13,7 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/rackspace/gophercloud/openstack/compute/v2/images" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "github.com/rackspace/gophercloud/openstack/networking/v2/networks" @@ -39,12 +40,18 @@ func resourceComputeInstanceV2() *schema.Resource { Required: true, ForceNew: false, }, - "image_ref": &schema.Schema{ + "image_id": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: false, + ForceNew: true, DefaultFunc: envDefaultFunc("OS_IMAGE_ID"), }, + "image_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_IMAGE_NAME"), + }, "flavor_ref": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -165,9 +172,14 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e var createOpts servers.CreateOptsBuilder + imageId, err := getImageID(computeClient, d) + if err != nil { + return err + } + createOpts = &servers.CreateOpts{ Name: d.Get("name").(string), - ImageRef: d.Get("image_ref").(string), + ImageRef: imageId, FlavorRef: d.Get("flavor_ref").(string), SecurityGroups: resourceInstanceSecGroupsV2(d), AvailabilityZone: d.Get("availability_zone").(string), @@ -659,3 +671,36 @@ func getFloatingIPs(networkingClient *gophercloud.ServiceClient) ([]floatingips. } return ips, nil } + +func getImageID(client *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) { + imageID := d.Get("image_id").(string) + imageName := d.Get("image_name").(string) + if imageID == "" { + pager := images.ListDetail(client, nil) + + pager.EachPage(func(page pagination.Page) (bool, error) { + imageList, err := images.ExtractImages(page) + + if err != nil { + return false, err + } + + for _, i := range imageList { + if i.Name == imageName { + imageID = i.ID + } + } + return true, nil + }) + + if imageID == "" { + return "", fmt.Errorf("Unable to find image: %v", imageName) + } + } + + if imageID == "" && imageName == "" { + return "", fmt.Errorf("Neither an image ID nor an image name were able to be determined.") + } + + return imageID, nil +} From bad2c9f18d5038c9b6377c916dec2f46cc500479 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Feb 2015 16:58:27 +0000 Subject: [PATCH 104/167] Accounting for multiple results of an image name If multiple results are found, an error will be returned to the user. --- .../resource_openstack_compute_instance_v2.go | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index c56f86792..b4f51cc1d 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -673,34 +673,43 @@ func getFloatingIPs(networkingClient *gophercloud.ServiceClient) ([]floatingips. } func getImageID(client *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) { - imageID := d.Get("image_id").(string) + imageId := d.Get("image_id").(string) imageName := d.Get("image_name").(string) - if imageID == "" { - pager := images.ListDetail(client, nil) + imageCount := 0 + if imageId == "" && imageName != "" { + pager := images.ListDetail(client, &images.ListOpts{ + Name: imageName, + }) pager.EachPage(func(page pagination.Page) (bool, error) { imageList, err := images.ExtractImages(page) - if err != nil { return false, err } for _, i := range imageList { if i.Name == imageName { - imageID = i.ID + imageCount++ + imageId = i.ID } } return true, nil }) - if imageID == "" { - return "", fmt.Errorf("Unable to find image: %v", imageName) + switch imageCount { + case 0: + return "", fmt.Errorf("Unable to find image: %s", imageName) + case 1: + return imageId, nil + default: + return "", fmt.Errorf("Found %d images matching %s", imageCount, imageName) } } - if imageID == "" && imageName == "" { + if imageId == "" && imageName == "" { return "", fmt.Errorf("Neither an image ID nor an image name were able to be determined.") } - return imageID, nil + return imageId, nil + } From 74482abc5bf300c6d909ffd9ffd17be6ba04ba72 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 04:11:04 +0000 Subject: [PATCH 105/167] Refactoring multiple results --- .../resource_openstack_compute_instance_v2.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index b4f51cc1d..5b03577f4 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -674,10 +674,14 @@ func getFloatingIPs(networkingClient *gophercloud.ServiceClient) ([]floatingips. func getImageID(client *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) { imageId := d.Get("image_id").(string) - imageName := d.Get("image_name").(string) - imageCount := 0 - if imageId == "" && imageName != "" { + if imageId != "" { + return imageId, nil + } + + imageCount := 0 + imageName := d.Get("image_name").(string) + if imageName != "" { pager := images.ListDetail(client, &images.ListOpts{ Name: imageName, }) @@ -705,11 +709,5 @@ func getImageID(client *gophercloud.ServiceClient, d *schema.ResourceData) (stri return "", fmt.Errorf("Found %d images matching %s", imageCount, imageName) } } - - if imageId == "" && imageName == "" { - return "", fmt.Errorf("Neither an image ID nor an image name were able to be determined.") - } - - return imageId, nil - + return "", fmt.Errorf("Neither an image ID nor an image name were able to be determined.") } From 2b5c7c6e2c9c72c12b349552e3e405e9a456a38c Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 04:14:45 +0000 Subject: [PATCH 106/167] Updated documentation to reflect the image_ref / image_id change. --- .../openstack/r/compute_instance_v2.html.markdown | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 9095cb15a..f6cf6401c 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -15,7 +15,7 @@ Manages a V2 VM instance resource within OpenStack. ``` resource "openstack_compute_instance_v2" "test-server" { name = "tf-test" - image_ref = "ad091b52-742f-469e-8f3c-fd81cadf0743" + image_id = "ad091b52-742f-469e-8f3c-fd81cadf0743" flavor_ref = "3" metadata { this = "that" @@ -35,8 +35,13 @@ The following arguments are supported: * `name` - (Required) A unique name for the resource. -* `image_ref` - (Required) The image reference (ID) for the desired image for - the server. Changing this creates a new server. +* `image_id` - (Required) The image reference (ID) for the desired image for + the server. Changing this creates a new server. Note that `image_id` and + `image_name` are mutually exclusive. + +* `image_name` - (Required) The image name for the server. Changing this + creates a new server. Note that `image_id` and `image_name` are mutually + exclusive. * `flavor_ref` - (Required) The flavor reference (ID) for the desired flavor for the server. Changing this resizes the existing server. From 6f8df3d34ef8f1ef3e1a5d0260d58f60d68d5fcc Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 04:22:25 +0000 Subject: [PATCH 107/167] Doc touchup --- .../providers/openstack/r/compute_instance_v2.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index f6cf6401c..542210dc6 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -35,9 +35,9 @@ The following arguments are supported: * `name` - (Required) A unique name for the resource. -* `image_id` - (Required) The image reference (ID) for the desired image for - the server. Changing this creates a new server. Note that `image_id` and - `image_name` are mutually exclusive. +* `image_id` - (Required) The image ID of the desired image for the server. + Changing this creates a new server. Note that `image_id` and `image_name` + are mutually exclusive. * `image_name` - (Required) The image name for the server. Changing this creates a new server. Note that `image_id` and `image_name` are mutually From 52102624c68acc946aeb5c5c559c4c51febb74b3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 04:27:03 +0000 Subject: [PATCH 108/167] More doc touchups --- .../openstack/r/compute_instance_v2.html.markdown | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 542210dc6..03091df0f 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -35,13 +35,11 @@ The following arguments are supported: * `name` - (Required) A unique name for the resource. -* `image_id` - (Required) The image ID of the desired image for the server. - Changing this creates a new server. Note that `image_id` and `image_name` - are mutually exclusive. +* `image_id` - (Optional; Required if `image_name` is empty) The image ID of + the desired image for the server. Changing this creates a new server. -* `image_name` - (Required) The image name for the server. Changing this - creates a new server. Note that `image_id` and `image_name` are mutually - exclusive. +* `image_name` - (Optional; Required if `image_id` is empty) The name of the + desired image for the server. Changing this creates a new server. * `flavor_ref` - (Required) The flavor reference (ID) for the desired flavor for the server. Changing this resizes the existing server. From 768292c069c246a6b87455a576ee9755b597171f Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 05:24:38 +0000 Subject: [PATCH 109/167] Support for flavor_name This commit renames flavor_ref to flavor_id and adds the flavor_name parameter. Users can now specify either a flavor ID or name when launching instances. --- builtin/providers/openstack/provider_test.go | 7 +- .../resource_openstack_compute_instance_v2.go | 66 +++++++++++++++++-- .../r/compute_instance_v2.html.markdown | 9 ++- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/builtin/providers/openstack/provider_test.go b/builtin/providers/openstack/provider_test.go index cafda7ef8..7b3e65dd4 100644 --- a/builtin/providers/openstack/provider_test.go +++ b/builtin/providers/openstack/provider_test.go @@ -58,8 +58,9 @@ func testAccPreCheck(t *testing.T) { } OS_POOL_NAME = v - v = os.Getenv("OS_FLAVOR_ID") - if v == "" { - t.Fatal("OS_FLAVOR_ID must be set for acceptance tests") + v1 = os.Getenv("OS_FLAVOR_ID") + v2 = os.Getenv("OS_FLAVOR_NAME") + if v1 == "" && v2 == "" { + t.Fatal("OS_FLAVOR_ID or OS_FLAVOR_NAME must be set for acceptance tests") } } diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 5b03577f4..0a7b1b624 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -13,6 +13,7 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" "github.com/rackspace/gophercloud/openstack/compute/v2/images" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" @@ -52,12 +53,20 @@ func resourceComputeInstanceV2() *schema.Resource { ForceNew: true, DefaultFunc: envDefaultFunc("OS_IMAGE_NAME"), }, - "flavor_ref": &schema.Schema{ + "flavor_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: false, + Computed: true, DefaultFunc: envDefaultFunc("OS_FLAVOR_ID"), }, + "flavor_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: false, + Computed: true, + DefaultFunc: envDefaultFunc("OS_FLAVOR_NAME"), + }, "floating_ip": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -177,10 +186,15 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e return err } + flavorId, err := getFlavorID(computeClient, d) + if err != nil { + return err + } + createOpts = &servers.CreateOpts{ Name: d.Get("name").(string), ImageRef: imageId, - FlavorRef: d.Get("flavor_ref").(string), + FlavorRef: flavorId, SecurityGroups: resourceInstanceSecGroupsV2(d), AvailabilityZone: d.Get("availability_zone").(string), Networks: resourceInstanceNetworksV2(d), @@ -327,11 +341,17 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err }) d.Set("security_groups.#", secGrpNum) - newFlavor, ok := server.Flavor["id"].(string) + flavorId, ok := server.Flavor["id"].(string) if !ok { return fmt.Errorf("Error setting OpenStack server's flavor: %v", server.Flavor) } - d.Set("flavor_ref", newFlavor) + d.Set("flavor_id", flavorId) + + flavor, err := flavors.Get(computeClient, flavorId).Extract() + if err != nil { + return err + } + d.Set("flavor_name", flavor.Name) return nil } @@ -711,3 +731,41 @@ func getImageID(client *gophercloud.ServiceClient, d *schema.ResourceData) (stri } return "", fmt.Errorf("Neither an image ID nor an image name were able to be determined.") } + +func getFlavorID(client *gophercloud.ServiceClient, d *schema.ResourceData) (string, error) { + flavorId := d.Get("flavor_id").(string) + + if flavorId != "" { + return flavorId, nil + } + + flavorCount := 0 + flavorName := d.Get("flavor_name").(string) + if flavorName != "" { + pager := flavors.ListDetail(client, nil) + pager.EachPage(func(page pagination.Page) (bool, error) { + flavorList, err := flavors.ExtractFlavors(page) + if err != nil { + return false, err + } + + for _, f := range flavorList { + if f.Name == flavorName { + flavorCount++ + flavorId = f.ID + } + } + return true, nil + }) + + switch flavorCount { + case 0: + return "", fmt.Errorf("Unable to find flavor: %s", flavorName) + case 1: + return flavorId, nil + default: + return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, flavorName) + } + } + return "", fmt.Errorf("Neither an flavor ID nor an flavor name were able to be determined.") +} diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 03091df0f..ba03ee01c 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -16,7 +16,7 @@ Manages a V2 VM instance resource within OpenStack. resource "openstack_compute_instance_v2" "test-server" { name = "tf-test" image_id = "ad091b52-742f-469e-8f3c-fd81cadf0743" - flavor_ref = "3" + flavor_id = "3" metadata { this = "that" } @@ -41,8 +41,11 @@ The following arguments are supported: * `image_name` - (Optional; Required if `image_id` is empty) The name of the desired image for the server. Changing this creates a new server. -* `flavor_ref` - (Required) The flavor reference (ID) for the desired flavor - for the server. Changing this resizes the existing server. +* `flavor_id` - (Optional; Required if `flavor_name` is empty) The flavor ID of + the desired flavor for the server. Changing this resizes the existing server. + +* `flavor_name` - (Optional; Required if `flavor_id` is empty) The name of the + desired flavor for the server. Changing this resizes the existing server. * `security_groups` - (Optional) An array of one or more security group names to associate with the server. Changing this results in adding/removing From b3438d07d69fd3d92ad865a07d8e116ae5a0da60 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 05:33:04 +0000 Subject: [PATCH 110/167] This commit enables both the image_id and the image_name to be computed, so that specifying one will populate the other. --- .../resource_openstack_compute_instance_v2.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 0a7b1b624..fbc7a7dd7 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -45,12 +45,14 @@ func resourceComputeInstanceV2() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + Computed: true, DefaultFunc: envDefaultFunc("OS_IMAGE_ID"), }, "image_name": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, + Computed: true, DefaultFunc: envDefaultFunc("OS_IMAGE_NAME"), }, "flavor_id": &schema.Schema{ @@ -353,6 +355,18 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err } d.Set("flavor_name", flavor.Name) + imageId, ok := server.Image["id"].(string) + if !ok { + return fmt.Errorf("Error setting OpenStack server's image: %v", server.Image) + } + d.Set("image_id", imageId) + + image, err := images.Get(computeClient, imageId).Extract() + if err != nil { + return err + } + d.Set("image_name", image.Name) + return nil } From 16ea14e8c914f15340d947f34658f40b023f1896 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 11 Feb 2015 17:08:23 +0000 Subject: [PATCH 111/167] Grammar fix --- .../openstack/resource_openstack_compute_instance_v2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index fbc7a7dd7..0025e7f8a 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -781,5 +781,5 @@ func getFlavorID(client *gophercloud.ServiceClient, d *schema.ResourceData) (str return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, flavorName) } } - return "", fmt.Errorf("Neither an flavor ID nor an flavor name were able to be determined.") + return "", fmt.Errorf("Neither a flavor ID nor a flavor name were able to be determined.") } From e5f2315bfeaab93680e96e824932cd9c346eb558 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Feb 2015 16:01:08 +0000 Subject: [PATCH 112/167] Instance volume attach This commit adds the ability for instances to attach volumes from within their resource. --- .../resource_openstack_compute_instance_v2.go | 186 ++++++++++++++++++ ...urce_openstack_compute_instance_v2_test.go | 69 +++++++ 2 files changed, 255 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 0025e7f8a..1dc0a1616 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -1,6 +1,7 @@ package openstack import ( + "bytes" "fmt" "log" "time" @@ -13,6 +14,7 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" "github.com/rackspace/gophercloud/openstack/compute/v2/images" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" @@ -170,6 +172,30 @@ func resourceComputeInstanceV2() *schema.Resource { }, }, }, + "volume": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "volume_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "device": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + Set: resourceComputeVolumeAttachmentHash, + }, }, } } @@ -268,6 +294,20 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e } } + // were volume attachments specified? + if v := d.Get("volume"); v != nil { + vols := v.(*schema.Set).List() + if len(vols) > 0 { + if blockClient, err := config.blockStorageV1Client(d.Get("region").(string)); err != nil { + return fmt.Errorf("Error creating OpenStack block storage client: %s", err) + } else { + if err := attachVolumesToInstance(computeClient, blockClient, d.Id(), vols); err != nil { + return err + } + } + } + } + return resourceComputeInstanceV2Read(d, meta) } @@ -367,6 +407,23 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err } d.Set("image_name", image.Name) + // volume attachments + vas, err := getVolumeAttachments(computeClient, d.Id()) + if err != nil { + return err + } + if len(vas) > 0 { + attachments := make([]map[string]interface{}, len(vas)) + for i, attachment := range vas { + attachments[i] = make(map[string]interface{}) + attachments[i]["id"] = attachment.ID + attachments[i]["volume_id"] = attachment.VolumeID + attachments[i]["device"] = attachment.Device + } + log.Printf("[INFO] Volume attachments: %v", attachments) + d.Set("volume", attachments) + } + return nil } @@ -464,6 +521,37 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e } } + if d.HasChange("volume") { + // old attachments and new attachments + oldAttachments, newAttachments := d.GetChange("volume") + + // for each old attachment, detach the volume + oldAttachmentSet := oldAttachments.(*schema.Set).List() + if len(oldAttachmentSet) > 0 { + if blockClient, err := config.blockStorageV1Client(d.Get("region").(string)); err != nil { + return err + } else { + if err := detachVolumesFromInstance(computeClient, blockClient, d.Id(), oldAttachmentSet); err != nil { + return err + } + } + } + + // for each new attachment, attach the volume + newAttachmentSet := newAttachments.(*schema.Set).List() + if len(newAttachmentSet) > 0 { + if blockClient, err := config.blockStorageV1Client(d.Get("region").(string)); err != nil { + return err + } else { + if err := attachVolumesToInstance(computeClient, blockClient, d.Id(), newAttachmentSet); err != nil { + return err + } + } + } + + d.SetPartial("volume") + } + if d.HasChange("flavor_ref") { resizeOpts := &servers.ResizeOpts{ FlavorRef: d.Get("flavor_ref").(string), @@ -783,3 +871,101 @@ func getFlavorID(client *gophercloud.ServiceClient, d *schema.ResourceData) (str } return "", fmt.Errorf("Neither a flavor ID nor a flavor name were able to be determined.") } + +func resourceComputeVolumeAttachmentHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["volume_id"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["device"].(string))) + return hashcode.String(buf.String()) +} + +func attachVolumesToInstance(computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverId string, vols []interface{}) error { + if len(vols) > 0 { + for _, v := range vols { + va := v.(map[string]interface{}) + volumeId := va["volume_id"].(string) + device := va["device"].(string) + + s := "" + if serverId != "" { + s = serverId + } else if va["server_id"] != "" { + s = va["server_id"].(string) + } else { + return fmt.Errorf("Unable to determine server ID to attach volume.") + } + + vaOpts := &volumeattach.CreateOpts{ + Device: device, + VolumeID: volumeId, + } + + if _, err := volumeattach.Create(computeClient, s, vaOpts).Extract(); err != nil { + return err + } + + stateConf := &resource.StateChangeConf{ + Target: "in-use", + Refresh: VolumeV1StateRefreshFunc(blockClient, va["volume_id"].(string)), + Timeout: 30 * time.Minute, + Delay: 5 * time.Second, + MinTimeout: 2 * time.Second, + } + + if _, err := stateConf.WaitForState(); err != nil { + return err + } + + log.Printf("[INFO] Attached volume %s to instance %s", volumeId, serverId) + } + } + return nil +} + +func detachVolumesFromInstance(computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverId string, vols []interface{}) error { + if len(vols) > 0 { + for _, v := range vols { + va := v.(map[string]interface{}) + aId := va["id"].(string) + + if err := volumeattach.Delete(computeClient, serverId, aId).ExtractErr(); err != nil { + return err + } + + stateConf := &resource.StateChangeConf{ + Target: "available", + Refresh: VolumeV1StateRefreshFunc(blockClient, va["volume_id"].(string)), + Timeout: 30 * time.Minute, + Delay: 5 * time.Second, + MinTimeout: 2 * time.Second, + } + + if _, err := stateConf.WaitForState(); err != nil { + return err + } + log.Printf("[INFO] Detached volume %s from instance %s", va["volume_id"], serverId) + } + } + + return nil +} + +func getVolumeAttachments(computeClient *gophercloud.ServiceClient, serverId string) ([]volumeattach.VolumeAttachment, error) { + var attachments []volumeattach.VolumeAttachment + err := volumeattach.List(computeClient, serverId).EachPage(func(page pagination.Page) (bool, error) { + actual, err := volumeattach.ExtractVolumeAttachments(page) + if err != nil { + return false, err + } + + attachments = actual + return true, nil + }) + + if err != nil { + return nil, err + } + + return attachments, nil +} diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index 77559fa3a..f4c6c8557 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -7,7 +7,10 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" + "github.com/rackspace/gophercloud/pagination" ) func TestAccComputeV2Instance_basic(t *testing.T) { @@ -29,6 +32,27 @@ func TestAccComputeV2Instance_basic(t *testing.T) { }) } +func TestAccComputeV2Instance_volumeAttach(t *testing.T) { + var instance servers.Server + var volume volumes.Volume + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2InstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2Instance_volumeAttach, + Check: resource.ComposeTestCheckFunc( + testAccCheckBlockStorageV1VolumeExists(t, "openstack_blockstorage_volume_v1.myvol", &volume), + testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance), + testAccCheckComputeV2InstanceVolumeAttachment(&instance, &volume), + ), + }, + }, + }) +} + func testAccCheckComputeV2InstanceDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) computeClient, err := config.computeV2Client(OS_REGION_NAME) @@ -105,6 +129,36 @@ func testAccCheckComputeV2InstanceMetadata( } } +func testAccCheckComputeV2InstanceVolumeAttachment( + instance *servers.Server, volume *volumes.Volume) resource.TestCheckFunc { + return func(s *terraform.State) error { + var attachments []volumeattach.VolumeAttachment + + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) + if err != nil { + return err + } + err = volumeattach.List(computeClient, instance.ID).EachPage(func(page pagination.Page) (bool, error) { + actual, err := volumeattach.ExtractVolumeAttachments(page) + if err != nil { + return false, fmt.Errorf("Unable to lookup attachment: %s", err) + } + + attachments = actual + return true, nil + }) + + for _, attachment := range attachments { + if attachment.VolumeID == volume.ID { + return nil + } + } + + return fmt.Errorf("Volume not found: %s", volume.ID) + } +} + var testAccComputeV2Instance_basic = fmt.Sprintf(` resource "openstack_compute_instance_v2" "foo" { region = "%s" @@ -114,3 +168,18 @@ var testAccComputeV2Instance_basic = fmt.Sprintf(` } }`, OS_REGION_NAME) + +var testAccComputeV2Instance_volumeAttach = fmt.Sprintf(` + resource "openstack_blockstorage_volume_v1" "myvol" { + name = "myvol" + size = 1 + } + + resource "openstack_compute_instance_v2" "foo" { + region = "%s" + name = "terraform-test" + volume { + volume_id = "${openstack_blockstorage_volume_v1.myvol.id}" + } + }`, + OS_REGION_NAME) From 2b152f38b2f72434e499306ce4c659e879f7a950 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 12 Feb 2015 03:26:22 +0000 Subject: [PATCH 113/167] Doc update for instance volume attachment. --- .../openstack/r/compute_instance_v2.html.markdown | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index ba03ee01c..36805ed0d 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -71,6 +71,9 @@ The following arguments are supported: * `block_device` - (Optional) The object for booting by volume. The block_device object structure is documented below. Changing this creates a new server. +* `volume` - (Optional) Attach an existing volume to the instance. The volume + structure is described below. + The `network` block supports: * `uuid` - (Required unless `port` is provided) The network UUID to attach to @@ -96,6 +99,14 @@ The `block_device` block supports: * `destination_type` - (Optional) The type that gets created. Possible values are "volume" and "local". +The `volume` block supports: + +* `volume_id` - (Required) The UUID of the volume to attach. + +* `device` - (Optional) The device that the volume will be attached as. For + example: `/dev/vdc`. Omit this option to allow the volume to be + auto-assigned a device. + ## Attributes Reference The following attributes are exported: From 49b01a4f0a219fab6109e77446cc7a2d29fb4960 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 11 Feb 2015 21:56:26 -0700 Subject: [PATCH 114/167] update compute instance security group Read operation --- .../openstack/resource_openstack_compute_instance_v2.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 1dc0a1616..6406600ae 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -175,7 +175,6 @@ func resourceComputeInstanceV2() *schema.Resource { "volume": &schema.Schema{ Type: schema.TypeSet, Optional: true, - Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": &schema.Schema{ @@ -369,19 +368,18 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err d.Set("metadata", server.Metadata) - secGrpNum := 0 + secGrpNames := []string{} err = secgroups.ListByServer(computeClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { secGrpList, err := secgroups.ExtractSecurityGroups(page) if err != nil { return false, fmt.Errorf("Error getting security groups for OpenStack server: %s", err) } for _, sg := range secGrpList { - d.Set(fmt.Sprintf("security_groups.%d", secGrpNum), sg.Name) - secGrpNum++ + secGrpNames = append(secGrpNames, sg.Name) } return true, nil }) - d.Set("security_groups.#", secGrpNum) + d.Set("security_groups", secGrpNames) flavorId, ok := server.Flavor["id"].(string) if !ok { From bb6969a4c537bd5443ba71b245590d91549c2526 Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 11 Feb 2015 22:05:38 -0700 Subject: [PATCH 115/167] resize server on flavor_id or flavor_name change --- .../resource_openstack_compute_instance_v2.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 6406600ae..6da455ad1 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -550,11 +550,15 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e d.SetPartial("volume") } - if d.HasChange("flavor_ref") { - resizeOpts := &servers.ResizeOpts{ - FlavorRef: d.Get("flavor_ref").(string), + if d.HasChange("flavor_id") || d.HasChange("flavor_name") { + flavorId, err := getFlavorID(computeClient, d) + if err != nil { + return err } - err := servers.Resize(computeClient, d.Id(), resizeOpts).ExtractErr() + resizeOpts := &servers.ResizeOpts{ + FlavorRef: flavorId, + } + err = servers.Resize(computeClient, d.Id(), resizeOpts).ExtractErr() if err != nil { return fmt.Errorf("Error resizing OpenStack server: %s", err) } From 64d53009a05af30a0a62123811e15686b7fbaddf Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Wed, 11 Feb 2015 22:29:31 -0700 Subject: [PATCH 116/167] typeset->typelist --- .../resource_openstack_compute_instance_v2.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 6da455ad1..ce6d7772f 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -77,13 +77,10 @@ func resourceComputeInstanceV2() *schema.Resource { ForceNew: false, }, "security_groups": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, ForceNew: false, Elem: &schema.Schema{Type: schema.TypeString}, - Set: func(v interface{}) int { - return hashcode.String(v.(string)) - }, }, "availability_zone": &schema.Schema{ Type: schema.TypeString, @@ -374,6 +371,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err if err != nil { return false, fmt.Errorf("Error getting security groups for OpenStack server: %s", err) } + log.Printf("[DEBUG] secGrpList: %+v\n\n", secGrpList) for _, sg := range secGrpList { secGrpNames = append(secGrpNames, sg.Name) } @@ -466,7 +464,9 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e if d.HasChange("security_groups") { oldSGRaw, newSGRaw := d.GetChange("security_groups") - oldSGSet, newSGSet := oldSGRaw.(*schema.Set), newSGRaw.(*schema.Set) + oldSGSlice, newSGSlice := oldSGRaw.([]interface{}), newSGRaw.([]interface{}) + oldSGSet := schema.NewSet(func(v interface{}) int { return hashcode.String(v.(string)) }, oldSGSlice) + newSGSet := schema.NewSet(func(v interface{}) int { return hashcode.String(v.(string)) }, newSGSlice) secgroupsToAdd := newSGSet.Difference(oldSGSet) secgroupsToRemove := oldSGSet.Difference(newSGSet) @@ -661,9 +661,9 @@ func ServerV2StateRefreshFunc(client *gophercloud.ServiceClient, instanceID stri } func resourceInstanceSecGroupsV2(d *schema.ResourceData) []string { - rawSecGroups := d.Get("security_groups").(*schema.Set) - secgroups := make([]string, rawSecGroups.Len()) - for i, raw := range rawSecGroups.List() { + rawSecGroups := d.Get("security_groups").([]interface{}) + secgroups := make([]string, len(rawSecGroups)) + for i, raw := range rawSecGroups { secgroups[i] = raw.(string) } return secgroups From 4df32aebed07d3d222d90a5fdf6f9a12efe641ab Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 12 Feb 2015 20:58:12 +0000 Subject: [PATCH 117/167] Changing how security groups are read for compute instances --- .../resource_openstack_compute_instance_v2.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index ce6d7772f..9693fb890 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -366,17 +366,9 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err d.Set("metadata", server.Metadata) secGrpNames := []string{} - err = secgroups.ListByServer(computeClient, d.Id()).EachPage(func(page pagination.Page) (bool, error) { - secGrpList, err := secgroups.ExtractSecurityGroups(page) - if err != nil { - return false, fmt.Errorf("Error getting security groups for OpenStack server: %s", err) - } - log.Printf("[DEBUG] secGrpList: %+v\n\n", secGrpList) - for _, sg := range secGrpList { - secGrpNames = append(secGrpNames, sg.Name) - } - return true, nil - }) + for _, sg := range server.SecurityGroups { + secGrpNames = append(secGrpNames, sg["name"].(string)) + } d.Set("security_groups", secGrpNames) flavorId, ok := server.Flavor["id"].(string) From 633e98dffe249a93e9592f46a1ca0b8a5f8a69ae Mon Sep 17 00:00:00 2001 From: Jon Perritt Date: Thu, 12 Feb 2015 17:25:45 -0700 Subject: [PATCH 118/167] security group rule fix; still not exporting rule ID --- .../resource_openstack_compute_instance_v2.go | 13 ++- .../resource_openstack_compute_secgroup_v2.go | 104 ++++++++++++------ builtin/providers/openstack/util.go | 6 +- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 9693fb890..5462d566b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -477,9 +477,18 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e for _, g := range secgroupsToRemove.List() { err := secgroups.RemoveServerFromGroup(computeClient, d.Id(), g.(string)).ExtractErr() if err != nil { - return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) + errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) + } + if errCode.Actual == 404 { + continue + } else { + return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) + } + } else { + log.Printf("[DEBUG] Removed security group (%s) from instance (%s)", g.(string), d.Id()) } - log.Printf("[DEBUG] Removed security group (%s) from instance (%s)", g.(string), d.Id()) } } diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index cb61e7458..948bfa80b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) @@ -35,38 +36,41 @@ func resourceComputeSecGroupV2() *schema.Resource { ForceNew: false, }, "rule": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, "from_port": &schema.Schema{ Type: schema.TypeInt, Required: true, - ForceNew: true, + ForceNew: false, }, "to_port": &schema.Schema{ Type: schema.TypeInt, Required: true, - ForceNew: true, + ForceNew: false, }, "ip_protocol": &schema.Schema{ Type: schema.TypeString, Required: true, - ForceNew: true, + ForceNew: false, }, "cidr": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: true, + ForceNew: false, }, "from_group_id": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: true, + ForceNew: false, }, }, }, - Set: resourceSecGroupRuleV2Hash, }, }, } @@ -117,7 +121,8 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("region", d.Get("region").(string)) d.Set("name", sg.Name) d.Set("description", sg.Description) - d.Set("rule", sg.Rules) + log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rulesToMap(sg.Rules)) + d.Set("rules", rulesToMap(sg.Rules)) return nil } @@ -143,16 +148,18 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e if d.HasChange("rule") { oldSGRaw, newSGRaw := d.GetChange("rule") - oldSGRSet, newSGRSet := oldSGRaw.(*schema.Set), newSGRaw.(*schema.Set) + oldSGRSlice, newSGRSlice := oldSGRaw.([]interface{}), newSGRaw.([]interface{}) + oldSGRSet := schema.NewSet(secgroupRuleV2Hash, oldSGRSlice) + newSGRSet := schema.NewSet(secgroupRuleV2Hash, newSGRSlice) secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet) secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet) log.Printf("[DEBUG] Security group rules to add: %v", secgrouprulesToAdd) - log.Printf("[DEBUG] Security groups to remove: %v", secgrouprulesToRemove) + log.Printf("[DEBUG] Security groups rules to remove: %v", secgrouprulesToRemove) for _, rawRule := range secgrouprulesToAdd.List() { - createRuleOpts := resourceSecGroupRuleV2(d, rawRule) + createRuleOpts := resourceSecGroupRuleCreateOptsV2(d, rawRule) rule, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract() if err != nil { return fmt.Errorf("Error adding rule to OpenStack security group (%s): %s", d.Id(), err) @@ -161,12 +168,21 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e } for _, r := range secgrouprulesToRemove.List() { - rule := r.(secgroups.Rule) - err := secgroups.DeleteRule(computeClient, "").ExtractErr() + rule := resourceSecGroupRuleV2(d, r) + err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr() if err != nil { - return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err) + errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err) + } + if errCode.Actual == 404 { + continue + } else { + return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id()) + } + } else { + log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err) } - log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s)", rule.ID, d.Id()) } } @@ -188,22 +204,10 @@ func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) e return nil } -func resourceSecGroupRuleV2Hash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int))) - buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["from_group_id"].(string))) - - return hashcode.String(buf.String()) -} - func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts { - rawRules := (d.Get("rule")).(*schema.Set) - createRuleOptsList := make([]secgroups.CreateRuleOpts, rawRules.Len()) - for i, raw := range rawRules.List() { + rawRules := (d.Get("rule")).([]interface{}) + createRuleOptsList := make([]secgroups.CreateRuleOpts, len(rawRules)) + for i, raw := range rawRules { rawMap := raw.(map[string]interface{}) createRuleOptsList[i] = secgroups.CreateRuleOpts{ ParentGroupID: d.Id(), @@ -217,7 +221,7 @@ func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts return createRuleOptsList } -func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts { +func resourceSecGroupRuleCreateOptsV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts { rawMap := raw.(map[string]interface{}) return secgroups.CreateRuleOpts{ ParentGroupID: d.Id(), @@ -228,3 +232,41 @@ func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.C FromGroupID: rawMap["from_group_id"].(string), } } + +func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.Rule { + rawMap := raw.(map[string]interface{}) + return secgroups.Rule{ + ID: rawMap["id"].(string), + ParentGroupID: d.Id(), + FromPort: rawMap["from_port"].(int), + ToPort: rawMap["to_port"].(int), + IPProtocol: rawMap["ip_protocol"].(string), + IPRange: secgroups.IPRange{CIDR: rawMap["cidr"].(string)}, + } +} + +func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} { + sgrMap := make([]map[string]interface{}, len(sgrs)) + for i, sgr := range sgrs { + sgrMap[i] = map[string]interface{}{ + "to_port": sgr.ToPort, + "from_port": sgr.FromPort, + "id": sgr.ID, + "ruleID": sgr.ID, + "cidr": sgr.IPRange.CIDR, + "ip_protocol": sgr.IPProtocol, + } + } + return sgrMap +} + +func secgroupRuleV2Hash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int))) + buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int))) + buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string))) + + return hashcode.String(buf.String()) +} diff --git a/builtin/providers/openstack/util.go b/builtin/providers/openstack/util.go index 7ed9f8f5f..c2a385156 100644 --- a/builtin/providers/openstack/util.go +++ b/builtin/providers/openstack/util.go @@ -9,14 +9,14 @@ import ( // CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so, // sets the resource ID to the empty string instead of throwing an error. -func CheckDeleted(d *schema.ResourceData, err error, resource string) error { +func CheckDeleted(d *schema.ResourceData, err error, msg string) error { errCode, ok := err.(*perigee.UnexpectedResponseCodeError) if !ok { - return fmt.Errorf("Error retrieving OpenStack %s: %s", resource, err) + return fmt.Errorf("%s: %s", msg, err) } if errCode.Actual == 404 { d.SetId("") return nil } - return fmt.Errorf("Error retrieving OpenStack %s: %s", resource, err) + return fmt.Errorf("%s: %s", msg, err) } From 4c9a44b69f292381a0b231828358d5c4ea7364a7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Tue, 10 Feb 2015 05:47:35 +0000 Subject: [PATCH 119/167] Added access_ip_v6 support This commit populates access_ip_v6 by either the AccessIPv6 attribute or by finding the first available IPv6 address. This commit retains the original feature of setting the default ssh connection to the IPv4 address unless one is not found. IPv6 access can still be enabled by explicitly setting it in the resource paramters. This commit also removes d.Set("host") in favor of SetConnInfo --- .../resource_openstack_compute_instance_v2.go | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 5462d566b..f7d92b14f 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -326,42 +326,78 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err d.Set("access_ip_v4", server.AccessIPv4) d.Set("access_ip_v6", server.AccessIPv6) - host := server.AccessIPv4 - if host == "" { + hostv4 := server.AccessIPv4 + if hostv4 == "" { if publicAddressesRaw, ok := server.Addresses["public"]; ok { publicAddresses := publicAddressesRaw.([]interface{}) for _, paRaw := range publicAddresses { pa := paRaw.(map[string]interface{}) if pa["version"].(float64) == 4 { - host = pa["addr"].(string) + hostv4 = pa["addr"].(string) break } } } } - // If no host found, just get the first IP we find - if host == "" { + // If no host found, just get the first IPv4 we find + if hostv4 == "" { for _, networkAddresses := range server.Addresses { for _, element := range networkAddresses.([]interface{}) { address := element.(map[string]interface{}) if address["version"].(float64) == 4 { - host = address["addr"].(string) + hostv4 = address["addr"].(string) break } } } } - d.Set("access_ip_v4", host) - d.Set("host", host) + d.Set("access_ip_v4", hostv4) + log.Printf("hostv4: %s", hostv4) - log.Printf("host: %s", host) + hostv6 := server.AccessIPv6 + if hostv6 == "" { + if publicAddressesRaw, ok := server.Addresses["public"]; ok { + publicAddresses := publicAddressesRaw.([]interface{}) + for _, paRaw := range publicAddresses { + pa := paRaw.(map[string]interface{}) + if pa["version"].(float64) == 4 { + hostv6 = fmt.Sprintf("[%s]", pa["addr"].(string)) + break + } + } + } + } - // Initialize the connection info - d.SetConnInfo(map[string]string{ - "type": "ssh", - "host": host, - }) + // If no hostv6 found, just get the first IPv6 we find + if hostv6 == "" { + for _, networkAddresses := range server.Addresses { + for _, element := range networkAddresses.([]interface{}) { + address := element.(map[string]interface{}) + if address["version"].(float64) == 6 { + hostv6 = fmt.Sprintf("[%s]", address["addr"].(string)) + break + } + } + } + } + d.Set("access_ip_v6", hostv6) + log.Printf("hostv6: %s", hostv6) + + preferredv := "" + if hostv4 != "" { + preferredv = hostv4 + } else if hostv6 != "" { + preferredv = hostv6 + } + + if preferredv != "" { + // Initialize the connection info + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": preferredv, + }) + } d.Set("metadata", server.Metadata) From 79e5c419c3b9ffac4b328966741afb9ce5af4195 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 13 Feb 2015 05:26:35 +0000 Subject: [PATCH 120/167] Fixing rule/rules and re-arranged order for schema consistency --- .../openstack/resource_openstack_compute_secgroup_v2.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 948bfa80b..f1ee715a2 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -122,7 +122,7 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("name", sg.Name) d.Set("description", sg.Description) log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rulesToMap(sg.Rules)) - d.Set("rules", rulesToMap(sg.Rules)) + d.Set("rule", rulesToMap(sg.Rules)) return nil } @@ -249,12 +249,11 @@ func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} { sgrMap := make([]map[string]interface{}, len(sgrs)) for i, sgr := range sgrs { sgrMap[i] = map[string]interface{}{ - "to_port": sgr.ToPort, - "from_port": sgr.FromPort, "id": sgr.ID, - "ruleID": sgr.ID, - "cidr": sgr.IPRange.CIDR, + "from_port": sgr.FromPort, + "to_port": sgr.ToPort, "ip_protocol": sgr.IPProtocol, + "cidr": sgr.IPRange.CIDR, } } return sgrMap From 42fb14f19a85cc0a6b340498ccb889d52672306f Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Mon, 16 Feb 2015 16:04:08 -0500 Subject: [PATCH 121/167] Added self option to security groups --- .../resource_openstack_compute_secgroup_v2.go | 17 +++++++++++++++-- .../r/compute_secgroup_v2.html.markdown | 5 ++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index f1ee715a2..013040db8 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -69,6 +69,11 @@ func resourceComputeSecGroupV2() *schema.Resource { Optional: true, ForceNew: false, }, + "self": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: false, + }, }, }, }, @@ -209,13 +214,17 @@ func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts createRuleOptsList := make([]secgroups.CreateRuleOpts, len(rawRules)) for i, raw := range rawRules { rawMap := raw.(map[string]interface{}) + groupId := rawMap["from_group_id"].(string) + if rawMap["self"].(bool) { + groupId = d.Id() + } createRuleOptsList[i] = secgroups.CreateRuleOpts{ ParentGroupID: d.Id(), FromPort: rawMap["from_port"].(int), ToPort: rawMap["to_port"].(int), IPProtocol: rawMap["ip_protocol"].(string), CIDR: rawMap["cidr"].(string), - FromGroupID: rawMap["from_group_id"].(string), + FromGroupID: groupId, } } return createRuleOptsList @@ -223,13 +232,17 @@ func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts func resourceSecGroupRuleCreateOptsV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts { rawMap := raw.(map[string]interface{}) + groupId := rawMap["from_group_id"].(string) + if rawMap["self"].(bool) { + groupId = d.Id() + } return secgroups.CreateRuleOpts{ ParentGroupID: d.Id(), FromPort: rawMap["from_port"].(int), ToPort: rawMap["to_port"].(int), IPProtocol: rawMap["ip_protocol"].(string), CIDR: rawMap["cidr"].(string), - FromGroupID: rawMap["from_group_id"].(string), + FromGroupID: groupId, } } diff --git a/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown index 50f328502..5b9538793 100644 --- a/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_secgroup_v2.html.markdown @@ -59,10 +59,13 @@ this creates a new security group rule. will be the source of network traffic to the security group. Use 0.0.0.0./0 to allow all IP addresses. Changing this creates a new security group rule. -* `from_group_id - (Optional) Required if `cidr` is empty. The ID of a group +* `from_group_id` - (Optional) Required if `cidr` is empty. The ID of a group from which to forward traffic to the parent group. Changing this creates a new security group rule. +* `self` - (Optional) Required if `cidr` and `from_group_id` is empty. If true, +the security group itself will be added as a source to this ingress rule. + ## Attributes Reference The following attributes are exported: From d03b420e623c4a9c4e8992c3f3d3ba5d573d2e9f Mon Sep 17 00:00:00 2001 From: Eric Bellemon Date: Tue, 17 Feb 2015 18:48:23 +0100 Subject: [PATCH 122/167] Replace perigee.UnexpectedResponseCodeError with gophercloud.UnexpectedResponseCodeError --- .../openstack/resource_openstack_blockstorage_volume_v1.go | 3 +-- .../openstack/resource_openstack_compute_instance_v2.go | 5 ++--- .../openstack/resource_openstack_compute_secgroup_v2.go | 4 ++-- .../resource_openstack_networking_router_interface_v2.go | 4 ++-- .../openstack/resource_openstack_networking_router_v2.go | 4 ++-- builtin/providers/openstack/util.go | 4 ++-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go index ab85317bb..f38fea5fb 100644 --- a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" ) @@ -252,7 +251,7 @@ func VolumeV1StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string return func() (interface{}, string, error) { v, err := volumes.Get(client, volumeID).Extract() if err != nil { - errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return nil, "", err } diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index f7d92b14f..348806931 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" @@ -513,7 +512,7 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e for _, g := range secgroupsToRemove.List() { err := secgroups.RemoveServerFromGroup(computeClient, d.Id(), g.(string)).ExtractErr() if err != nil { - errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) } @@ -683,7 +682,7 @@ func ServerV2StateRefreshFunc(client *gophercloud.ServiceClient, instanceID stri return func() (interface{}, string, error) { s, err := servers.Get(client, instanceID).Extract() if err != nil { - errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return nil, "", err } diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 013040db8..cf00f7f1b 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups" ) @@ -176,7 +176,7 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e rule := resourceSecGroupRuleV2(d, r) err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr() if err != nil { - errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err) } diff --git a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go index 2daaf787d..492c14d30 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go @@ -5,7 +5,7 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/rackspace/gophercloud/openstack/networking/v2/ports" ) @@ -69,7 +69,7 @@ func resourceNetworkingRouterInterfaceV2Read(d *schema.ResourceData, meta interf n, err := ports.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err) } diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_v2.go index c4fe8e90a..cb235a88b 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2.go @@ -6,7 +6,7 @@ import ( "strconv" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers" ) @@ -98,7 +98,7 @@ func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) er n, err := routers.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err) } diff --git a/builtin/providers/openstack/util.go b/builtin/providers/openstack/util.go index c2a385156..93a8bfbc5 100644 --- a/builtin/providers/openstack/util.go +++ b/builtin/providers/openstack/util.go @@ -4,13 +4,13 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" ) // CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so, // sets the resource ID to the empty string instead of throwing an error. func CheckDeleted(d *schema.ResourceData, err error, msg string) error { - errCode, ok := err.(*perigee.UnexpectedResponseCodeError) + errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return fmt.Errorf("%s: %s", msg, err) } From c3c4840bafc735a75d343ffaa87f5bb5d96eba40 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 13 Feb 2015 20:59:41 +0000 Subject: [PATCH 123/167] openstack_compute_floatingip_v2 This commit adds a resource that allows the user to allocate, deallocate, associate, and disassociate floating IPs through the nova api. --- builtin/providers/openstack/provider.go | 1 + ...esource_openstack_compute_floatingip_v2.go | 121 ++++++++++++++++++ ...ce_openstack_compute_floatingip_v2_test.go | 86 +++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go create mode 100644 builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 450e1c705..6a708a28f 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -63,6 +63,7 @@ func Provider() terraform.ResourceProvider { "openstack_compute_instance_v2": resourceComputeInstanceV2(), "openstack_compute_keypair_v2": resourceComputeKeypairV2(), "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), + "openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(), "openstack_lb_monitor_v1": resourceLBMonitorV1(), "openstack_lb_pool_v1": resourceLBPoolV1(), "openstack_lb_vip_v1": resourceLBVipV1(), diff --git a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go new file mode 100644 index 000000000..5a78483de --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go @@ -0,0 +1,121 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip" +) + +func resourceComputeFloatingIPV2() *schema.Resource { + return &schema.Resource{ + Create: resourceComputeFloatingIPV2Create, + Read: resourceComputeFloatingIPV2Read, + Update: nil, + Delete: resourceComputeFloatingIPV2Delete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + + "pool": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_POOL_NAME"), + }, + + // exported + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "address": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "fixed_ip": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceComputeFloatingIPV2Create(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + computeClient, err := config.computeV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + + createOpts := &floatingip.CreateOpts{ + Pool: d.Get("pool").(string), + } + newFip, err := floatingip.Create(computeClient, createOpts).Extract() + if err != nil { + return fmt.Errorf("Error creating Floating IP: %s", err) + } + + d.SetId(newFip.ID) + + return resourceComputeFloatingIPV2Read(d, meta) +} + +func resourceComputeFloatingIPV2Read(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + computeClient, err := config.computeV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + + fip, err := floatingip.Get(computeClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error getting Floating IP: %s", err) + } + + log.Printf("[DEBUG] Retrieved Floating IP %s: %+v", d.Id(), fip) + + d.Set("id", d.Id()) + d.Set("region", d.Get("region").(string)) + d.Set("pool", fip.Pool) + d.Set("instance_id", fip.InstanceID) + d.Set("address", fip.IP) + d.Set("fixed_ip", fip.FixedIP) + + return nil +} + +func resourceComputeFloatingIPV2Delete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + computeClient, err := config.computeV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack compute client: %s", err) + } + + fip, err := floatingip.Get(computeClient, d.Id()).Extract() + if err != nil { + return fmt.Errorf("Error getting Floating IP for update: %s", err) + } + + log.Printf("[DEBUG] Deleting Floating IP %s", fip.IP) + + // Now do the actual deletion + if err := floatingip.Delete(computeClient, fip.ID).ExtractErr(); err != nil { + return fmt.Errorf("Error deleting Floating IP: %s", err) + } + + return nil +} diff --git a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go new file mode 100644 index 000000000..c246d1a51 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go @@ -0,0 +1,86 @@ +package openstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip" +) + +func TestAccComputeV2FloatingIP_basic(t *testing.T) { + var floatingIP floatingip.FloatingIP + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeV2FloatingIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeV2FloatingIP_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeV2FloatingIPExists(t, "openstack_compute_floatingip_v2.foo", &floatingIP), + ), + }, + }, + }) +} + +func testAccCheckComputeV2FloatingIPDestroy(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2FloatingIPDestroy) Error creating OpenStack compute client: %s", err) + } + + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_compute_floatingip_v2" { + continue + } + + _, err := floatingip.Get(computeClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("FloatingIP still exists") + } + } + + return nil +} + +func testAccCheckComputeV2FloatingIPExists(t *testing.T, n string, kp *floatingip.FloatingIP) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + computeClient, err := config.computeV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckComputeV2FloatingIPExists) Error creating OpenStack compute client: %s", err) + } + + found, err := floatingip.Get(computeClient, rs.Primary.ID).Extract() + if err != nil { + return err + } + + if found.ID != rs.Primary.ID { + return fmt.Errorf("FloatingIP not found") + } + + *kp = *found + + return nil + } +} + +var testAccComputeV2FloatingIP_basic = fmt.Sprintf(` + resource "openstack_compute_floatingip_v2" "foo" { + }`) From b7091414fe7edf4cbfb725c14474bc5b96e4161b Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Thu, 19 Feb 2015 22:22:37 +0000 Subject: [PATCH 124/167] Volume Safe Delete This commit ensures that a volume is detached from all instances before it is deleted. It also adds in an `attachment` exported parameter that shows details of the volume's attachment(s). --- ...source_openstack_blockstorage_volume_v1.go | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go index f38fea5fb..eefd75f7e 100644 --- a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -1,14 +1,17 @@ package openstack import ( + "bytes" "fmt" "log" "time" + "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes" + "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" ) func resourceBlockStorageVolumeV1() *schema.Resource { @@ -65,6 +68,27 @@ func resourceBlockStorageVolumeV1() *schema.Resource { Optional: true, ForceNew: true, }, + "attachment": &schema.Schema{ + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "instance_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "device": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + }, + Set: resourceVolumeAttachmentHash, + }, }, } } @@ -175,6 +199,18 @@ func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{}) d.Set("metadata", "") } + if len(v.Attachments) > 0 { + attachments := make([]map[string]interface{}, len(v.Attachments)) + for i, attachment := range v.Attachments { + attachments[i] = make(map[string]interface{}) + attachments[i]["id"] = attachment["id"] + attachments[i]["instance_id"] = attachment["server_id"] + attachments[i]["device"] = attachment["device"] + log.Printf("[DEBUG] attachment: %v", attachment) + } + d.Set("attachment", attachments) + } + return nil } @@ -209,6 +245,42 @@ func resourceBlockStorageVolumeV1Delete(d *schema.ResourceData, meta interface{} return fmt.Errorf("Error creating OpenStack block storage client: %s", err) } + v, err := volumes.Get(blockStorageClient, d.Id()).Extract() + if err != nil { + return CheckDeleted(d, err, "volume") + } + + // make sure this volume is detached from all instances before deleting + if len(v.Attachments) > 0 { + log.Printf("[DEBUG] detaching volumes") + if computeClient, err := config.computeV2Client(d.Get("region").(string)); err != nil { + return err + } else { + for _, volumeAttachment := range v.Attachments { + log.Printf("[DEBUG] Attachment: %v", volumeAttachment) + if err := volumeattach.Delete(computeClient, volumeAttachment["server_id"].(string), volumeAttachment["id"].(string)).ExtractErr(); err != nil { + return err + } + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"in-use"}, + Target: "available", + Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()), + Timeout: 10 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf( + "Error waiting for volume (%s) to become available: %s", + d.Id(), err) + } + } + } + err = volumes.Delete(blockStorageClient, d.Id()).ExtractErr() if err != nil { return fmt.Errorf("Error deleting OpenStack volume: %s", err) @@ -264,3 +336,12 @@ func VolumeV1StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string return v, v.Status, nil } } + +func resourceVolumeAttachmentHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + if m["instance_id"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["instance_id"].(string))) + } + return hashcode.String(buf.String()) +} From 102848525f4e2a1b31200994b67c4ce409cc8b28 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 20 Feb 2015 05:08:09 +0000 Subject: [PATCH 125/167] Added CheckDelete to handle bad Gets. Also removed unneeded Get from Delete. --- .../resource_openstack_compute_floatingip_v2.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go index 5a78483de..3501fe282 100644 --- a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go @@ -83,7 +83,7 @@ func resourceComputeFloatingIPV2Read(d *schema.ResourceData, meta interface{}) e fip, err := floatingip.Get(computeClient, d.Id()).Extract() if err != nil { - return fmt.Errorf("Error getting Floating IP: %s", err) + return CheckDeleted(d, err, "floating ip") } log.Printf("[DEBUG] Retrieved Floating IP %s: %+v", d.Id(), fip) @@ -105,15 +105,8 @@ func resourceComputeFloatingIPV2Delete(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Error creating OpenStack compute client: %s", err) } - fip, err := floatingip.Get(computeClient, d.Id()).Extract() - if err != nil { - return fmt.Errorf("Error getting Floating IP for update: %s", err) - } - - log.Printf("[DEBUG] Deleting Floating IP %s", fip.IP) - - // Now do the actual deletion - if err := floatingip.Delete(computeClient, fip.ID).ExtractErr(); err != nil { + log.Printf("[DEBUG] Deleting Floating IP %s", d.Id()) + if err := floatingip.Delete(computeClient, d.Id()).ExtractErr(); err != nil { return fmt.Errorf("Error deleting Floating IP: %s", err) } From 552b0af2016e893919cfa572b20c12496dbea60a Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Mon, 2 Feb 2015 21:36:21 +0100 Subject: [PATCH 126/167] Add FWaaS rule resource --- .../resource_openstack_fw_rule_v2.go | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_fw_rule_v2.go diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go new file mode 100644 index 000000000..97d8da568 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go @@ -0,0 +1,219 @@ +package openstack + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" +) + +func resourceFWRuleV2() *schema.Resource { + return &schema.Resource{ + Create: resourceFirewallRuleCreate, + Read: resourceFirewallRuleRead, + Update: resourceFirewallRuleUpdate, + Delete: resourceFirewallRuleDelete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "protocol": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "action": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "ip_version": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Default: 4, + }, + "source_ip_address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "destination_ip_address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "source_port": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "destination_port": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + }, + } +} + +func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error { + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + enabled := d.Get("enabled").(bool) + + ruleConfiguration := rules.CreateOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + Protocol: d.Get("protocol").(string), + Action: d.Get("action").(string), + IPVersion: d.Get("ip_version").(int), + SourceIPAddress: d.Get("source_ip_address").(string), + DestinationIPAddress: d.Get("destination_ip_address").(string), + SourcePort: d.Get("source_port").(string), + DestinationPort: d.Get("destination_port").(string), + Enabled: &enabled, + } + + log.Printf("[DEBUG] Create firewall rule: %#v", ruleConfiguration) + + rule, err := rules.Create(networkingClient, ruleConfiguration).Extract() + + if err != nil { + return err + } + + log.Printf("[DEBUG] Firewall rule with id %s : %#v", rule.ID, rule) + + d.SetId(rule.ID) + + d.Set("name", rule.Name) + d.Set("description", rule.Description) + d.Set("protocol", rule.Protocol) + d.Set("action", rule.Action) + d.Set("ip_version", rule.IPVersion) + d.Set("source_ip_address", rule.SourceIPAddress) + d.Set("destination_ip_address", rule.DestinationIPAddress) + d.Set("source_port", rule.SourcePort) + d.Set("destination_port", rule.DestinationPort) + d.Set("enabled", rule.Enabled) + + return nil +} + +func resourceFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Retrieve information about firewall rule: %s", d.Id()) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + rule, err := rules.Get(networkingClient, d.Id()).Extract() + + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return err + } + if httpError.Actual == 404 { + d.SetId("") + return nil + } + return err + } + + d.Set("name", rule.Name) + d.Set("description", rule.Description) + d.Set("protocol", rule.Protocol) + d.Set("action", rule.Action) + d.Set("ip_version", rule.IPVersion) + d.Set("source_ip_address", rule.SourceIPAddress) + d.Set("destination_ip_address", rule.DestinationIPAddress) + d.Set("source_port", rule.SourcePort) + d.Set("destination_port", rule.DestinationPort) + d.Set("enabled", rule.Enabled) + + return nil +} + +func resourceFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + opts := rules.UpdateOpts{} + + if d.HasChange("name") { + name := d.Get("name").(string) + opts.Name = &name + } + if d.HasChange("description") { + description := d.Get("description").(string) + opts.Description = &description + } + if d.HasChange("protocol") { + opts.Protocol = d.Get("protocol").(string) + } + if d.HasChange("action") { + opts.Action = d.Get("action").(string) + } + if d.HasChange("ip_version") { + opts.IPVersion = d.Get("ip_version").(int) + } + if d.HasChange("source_ip_address") { + sourceIPAddress := d.Get("source_ip_address").(string) + opts.SourceIPAddress = &sourceIPAddress + } + if d.HasChange("destination_ip_address") { + destinationIPAddress := d.Get("destination_ip_address").(string) + opts.DestinationIPAddress = &destinationIPAddress + } + if d.HasChange("source_port") { + sourcePort := d.Get("source_port").(string) + opts.SourcePort = &sourcePort + } + if d.HasChange("destination_port") { + destinationPort := d.Get("destination_port").(string) + opts.DestinationPort = &destinationPort + } + if d.HasChange("enabled") { + enabled := d.Get("enabled").(bool) + opts.Enabled = &enabled + } + + log.Printf("[DEBUG] Updating firewall rules: %#v", opts) + + return rules.Update(networkingClient, d.Id(), opts).Err +} + +func resourceFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Destroy firewall rule: %s", d.Id()) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + return rules.Delete(networkingClient, d.Id()).Err +} From f829427151c432acc5aebf66a03264d0b169044b Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Tue, 3 Feb 2015 20:56:13 +0100 Subject: [PATCH 127/167] Add FWaaS policy resource --- .../resource_openstack_fw_policy_v2.go | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_fw_policy_v2.go diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go new file mode 100644 index 000000000..8f7b593d8 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go @@ -0,0 +1,196 @@ +package openstack + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" +) + +func resourceFWPolicyV2() *schema.Resource { + return &schema.Resource{ + Create: resourceFirewallPolicyCreate, + Read: resourceFirewallPolicyRead, + Update: resourceFirewallPolicyUpdate, + Delete: resourceFirewallPolicyDelete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "audited": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "shared": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "rules": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: func(v interface{}) int { + return hashcode.String(v.(string)) + }, + }, + }, + } +} + +func resourceFirewallPolicyCreate(d *schema.ResourceData, meta interface{}) error { + + // TODO To remove + time.Sleep(time.Second * 5) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + v := d.Get("rules").(*schema.Set) + + log.Printf("[DEBUG] Rules found : %#v", v) + log.Printf("[DEBUG] Rules count : %d", v.Len()) + + rules := make([]string, v.Len()) + for i, v := range v.List() { + rules[i] = v.(string) + } + + opts := policies.CreateOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + // Audited: d.Get("audited").(bool), + // Shared: d.Get("shared").(bool), + Rules: rules, + } + + log.Printf("[DEBUG] Create firewall policy: %#v", opts) + + policy, err := policies.Create(networkingClient, opts).Extract() + if err != nil { + return err + } + + log.Printf("[DEBUG] Firewall policy craeted: %#v", policy) + + d.SetId(policy.ID) + + return nil +} + +func resourceFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Retrieve information about firewall policy: %s", d.Id()) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + policy, err := policies.Get(networkingClient, d.Id()).Extract() + + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return err + } + if httpError.Actual == 404 { + d.SetId("") + return nil + } + return err + } + + d.Set("name", policy.Name) + + return nil +} + +func resourceFirewallPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + opts := policies.UpdateOpts{} + + if d.HasChange("name") { + name := d.Get("name").(string) + opts.Name = &name + } + + if d.HasChange("description") { + description := d.Get("description").(string) + opts.Description = &description + } + + if d.HasChange("rules") { + v := d.Get("rules").(*schema.Set) + + log.Printf("[DEBUG] Rules found : %#v", v) + log.Printf("[DEBUG] Rules count : %d", v.Len()) + + rules := make([]string, v.Len()) + for i, v := range v.List() { + rules[i] = v.(string) + } + opts.Rules = rules + } + + log.Printf("[DEBUG] Updating firewall policy with id %s: %#v", d.Id(), opts) + + return policies.Update(networkingClient, d.Id(), opts).Err +} + +func resourceFirewallPolicyDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Destroy firewall policy: %s", d.Id()) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + for i := 0; i < 15; i++ { + + err = policies.Delete(networkingClient, d.Id()).Err + if err == nil { + break + } + + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 409 { + return err + } + + // This error usualy means that the policy is attached + // to a firewall. At this point, the firewall is probably + // being delete. So, we retry a few times. + + time.Sleep(time.Second * 2) + } + + return err +} From 5d42242d4b541b9abd9b332e5a318abd23ec414c Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Tue, 3 Feb 2015 22:14:56 +0100 Subject: [PATCH 128/167] Add FWaaS firewall resource --- .../resource_openstack_fw_firewall_v2.go | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_fw_firewall_v2.go diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go new file mode 100644 index 000000000..e2125e7fb --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go @@ -0,0 +1,236 @@ +package openstack + +import ( + "errors" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" +) + +func resourceFWFirewallV2() *schema.Resource { + return &schema.Resource{ + Create: resourceFirewallCreate, + Read: resourceFirewallRead, + Update: resourceFirewallUpdate, + Delete: resourceFirewallDelete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_REGION_NAME"), + }, + "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "policy_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "admin_state_up": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + }, + } +} + +func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + adminStateUp := d.Get("admin_state_up").(bool) + + firewallConfiguration := firewalls.CreateOpts{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + PolicyID: d.Get("policy_id").(string), + AdminStateUp: &adminStateUp, + } + + log.Printf("[DEBUG] Create firewall: %#v", firewallConfiguration) + + firewall, err := firewalls.Create(networkingClient, firewallConfiguration).Extract() + if err != nil { + return err + } + + log.Printf("[DEBUG] Firewall created: %#v", firewall) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"PENDING_CREATE"}, + Target: "ACTIVE", + Refresh: WaitForFirewallActive(networkingClient, firewall.ID), + Timeout: 30 * time.Second, + Delay: 0, + MinTimeout: 2 * time.Second, + } + + d.SetId(firewall.ID) + + d.Set("name", firewall.Name) + d.Set("description", firewall.Description) + d.Set("policy_id", firewall.PolicyID) + d.Set("admin_state_up", firewall.AdminStateUp) + + _, err = stateConf.WaitForState() + + return nil +} + +func resourceFirewallRead(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Retrieve information about firewall: %s", d.Id()) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + firewall, err := firewalls.Get(networkingClient, d.Id()).Extract() + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok { + return err + } + + if httpError.Actual == 404 { + d.SetId("") + return nil + } + return err + } + + d.Set("name", firewall.Name) + d.Set("description", firewall.Description) + d.Set("policy_id", firewall.PolicyID) + d.Set("admin_state_up", firewall.AdminStateUp) + + return nil +} + +func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + opts := firewalls.UpdateOpts{} + + if d.HasChange("name") { + name := d.Get("name").(string) + opts.Name = &name + } + + if d.HasChange("description") { + description := d.Get("description").(string) + opts.Description = &description + } + + if d.HasChange("policy_id") { + opts.PolicyID = d.Get("policy_id").(string) + } + + log.Printf("[DEBUG] Updating firewall with id %s: %#v", d.Id(), opts) + + if err := firewalls.Update(networkingClient, d.Id(), opts).Err; err != nil { + return err + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"PENDING_CREATE"}, + Target: "ACTIVE", + Refresh: WaitForFirewallActive(networkingClient, d.Id()), + Timeout: 30 * time.Second, + Delay: 0, + MinTimeout: 2 * time.Second, + } + + _, err = stateConf.WaitForState() + + return err +} + +func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[DEBUG] Destroy firewall: %s", d.Id()) + + config := meta.(*Config) + networkingClient, err := config.networkingV2Client(d.Get("region").(string)) + if err != nil { + return fmt.Errorf("Error creating OpenStack networking client: %s", err) + } + + err = firewalls.Delete(networkingClient, d.Id()).Err + + if err != nil { + return err + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"DELETING"}, + Target: "DELETED", + Refresh: WaitForFirewallDeletion(networkingClient, d.Id()), + Timeout: 2 * time.Minute, + Delay: 0, + MinTimeout: 2 * time.Second, + } + + _, err = stateConf.WaitForState() + + return err +} + +func WaitForFirewallActive(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc { + + return func() (interface{}, string, error) { + fw, err := firewalls.Get(networkingClient, id).Extract() + log.Printf("[DEBUG] Get firewall %s => %#v", id, fw) + + if err != nil { + return nil, "", err + } + return fw, fw.Status, nil + } +} + +func WaitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc { + + return func() (interface{}, string, error) { + fw, err := firewalls.Get(networkingClient, id).Extract() + log.Printf("[DEBUG] Get firewall %s => %#v", id, fw) + + if err != nil { + httpStatus := err.(*perigee.UnexpectedResponseCodeError) + log.Printf("[DEBUG] Get firewall %s status is %d", id, httpStatus.Actual) + + if httpStatus.Actual == 404 { + log.Printf("[DEBUG] Firewall %s is actually deleted", id) + return "", "DELETED", nil + } + return nil, "", errors.New(fmt.Sprintf("Unexpected status code %d", httpStatus.Actual)) + } + + log.Printf("[DEBUG] Firewall %s deletion is pending", id) + return fw, "DELETING", nil + } +} From 3d1001d8fe8864c2b79b83447c2a05fcf4b87e03 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Thu, 5 Feb 2015 13:49:23 +0100 Subject: [PATCH 129/167] Add FWaaS rule acceptance test --- .../resource_openstack_fw_rule_v2_test.go | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go new file mode 100644 index 000000000..86e731b44 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go @@ -0,0 +1,182 @@ +package openstack + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" +) + +func TestAccOpenstackFirewallRule(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOpenstackFirewallRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testFirewallRuleMinimalConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallRuleExists( + "openstack_fw_rule_v2.accept_test_minimal", + &rules.Rule{ + Protocol: "udp", + Action: "deny", + IPVersion: 4, + Enabled: true, + }), + ), + }, + resource.TestStep{ + Config: testFirewallRuleConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallRuleExists( + "openstack_fw_rule_v2.accept_test", + &rules.Rule{ + Name: "accept_test", + Protocol: "udp", + Action: "deny", + Description: "Terraform accept test", + IPVersion: 4, + SourceIPAddress: "1.2.3.4", + DestinationIPAddress: "4.3.2.0/24", + SourcePort: "444", + DestinationPort: "555", + Enabled: true, + }), + ), + }, + resource.TestStep{ + Config: testFirewallRuleUpdateAllFieldsConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallRuleExists( + "openstack_fw_rule_v2.accept_test", + &rules.Rule{ + Name: "accept_test_updated_2", + Protocol: "tcp", + Action: "allow", + Description: "Terraform accept test updated", + IPVersion: 4, + SourceIPAddress: "1.2.3.0/24", + DestinationIPAddress: "4.3.2.8", + SourcePort: "666", + DestinationPort: "777", + Enabled: false, + }), + ), + }, + }, + }) +} + +func testAccCheckOpenstackFirewallRuleDestroy(s *terraform.State) error { + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckOpenstackFirewallRuleDestroy) Error creating OpenStack networking client: %s", err) + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_firewall_rule" { + continue + } + _, err = rules.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Firewall rule (%s) still exists.", rs.Primary.ID) + } + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 404 { + return httpError + } + } + return nil +} + +func testAccCheckFirewallRuleExists(n string, expected *rules.Rule) resource.TestCheckFunc { + + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckFirewallRuleExists) Error creating OpenStack networking client: %s", err) + } + + var found *rules.Rule + for i := 0; i < 5; i++ { + // Firewall rule creation is asynchronous. Retry some times + // if we get a 404 error. Fail on any other error. + found, err = rules.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 404 { + time.Sleep(time.Second) + continue + } + } + break + } + + if err != nil { + return err + } + + expected.ID = found.ID + + if !reflect.DeepEqual(expected, found) { + return fmt.Errorf("Expected:\n%#v\nFound:\n%#v", expected, found) + } + + return nil + } +} + +const testFirewallRuleMinimalConfig = ` +resource "openstack_fw_rule_v2" "accept_test_minimal" { + protocol = "udp" + action = "deny" +} +` + +const testFirewallRuleConfig = ` +resource "openstack_fw_rule_v2" "accept_test" { + name = "accept_test" + description = "Terraform accept test" + protocol = "udp" + action = "deny" + ip_version = 4 + source_ip_address = "1.2.3.4" + destination_ip_address = "4.3.2.0/24" + source_port = "444" + destination_port = "555" + enabled = true +} +` + +const testFirewallRuleUpdateAllFieldsConfig = ` +resource "openstack_fw_rule_v2" "accept_test" { + name = "accept_test_updated_2" + description = "Terraform accept test updated" + protocol = "tcp" + action = "allow" + ip_version = 4 + source_ip_address = "1.2.3.0/24" + destination_ip_address = "4.3.2.8" + source_port = "666" + destination_port = "777" + enabled = false +} +` From 88a55a5d58c2c6317d0e21de7c25ee481afe5102 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Mon, 9 Feb 2015 22:44:27 +0100 Subject: [PATCH 130/167] Enable FWaaS resources --- builtin/providers/openstack/provider.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 6a708a28f..7e0880007 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -64,6 +64,9 @@ func Provider() terraform.ResourceProvider { "openstack_compute_keypair_v2": resourceComputeKeypairV2(), "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), "openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(), + "openstack_fw_firewall_v2": resourceFWFirewallV2(), + "openstack_fw_policy_v2": resourceFWPolicyV2(), + "openstack_fw_rule_v2": resourceFWRuleV2(), "openstack_lb_monitor_v1": resourceLBMonitorV1(), "openstack_lb_pool_v1": resourceLBPoolV1(), "openstack_lb_vip_v1": resourceLBVipV1(), From 06826fb67742f2635cb9bdec15fbb4ec6763fbb5 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Mon, 9 Feb 2015 22:53:09 +0100 Subject: [PATCH 131/167] Add FWaaS policy acceptance test --- .../resource_openstack_fw_policy_v2_test.go | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go new file mode 100644 index 000000000..8a3f19ef5 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go @@ -0,0 +1,175 @@ +package openstack + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" +) + +func TestAccOpenstackFirewallPolicy(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOpenstackFirewallPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testFirewallPolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists( + "openstack_fw_policy_v2.accept_test", + &policies.Policy{ + Rules: []string{}, + }), + ), + }, + resource.TestStep{ + Config: testFirewallPolicyConfigAddRules, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists( + "openstack_fw_policy_v2.accept_test", + &policies.Policy{ + Name: "accept_test", + Description: "terraform acceptance test", + Rules: []string{ + "", + "", + }, + }), + ), + }, + resource.TestStep{ + Config: testFirewallPolicyUpdateDeleteRule, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists( + "openstack_fw_policy_v2.accept_test", + &policies.Policy{}), + ), + }, + }, + }) +} + +func testAccCheckOpenstackFirewallPolicyDestroy(s *terraform.State) error { + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckOpenstackFirewallPolicyDestroy) Error creating OpenStack networking client: %s", err) + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_fw_policy_v2" { + continue + } + _, err = policies.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Firewall policy (%s) still exists.", rs.Primary.ID) + } + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 404 { + return httpError + } + } + return nil +} + +func testAccCheckFirewallPolicyExists(n string, expected *policies.Policy) resource.TestCheckFunc { + + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckFirewallPolicyExists) Error creating OpenStack networking client: %s", err) + } + + var found *policies.Policy + for i := 0; i < 5; i++ { + // Firewall policy creation is asynchronous. Retry some times + // if we get a 404 error. Fail on any other error. + found, err = policies.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 404 { + time.Sleep(time.Second) + continue + } + } + break + } + + if err != nil { + return err + } + + expected.ID = found.ID + + if !reflect.DeepEqual(expected, found) { + return fmt.Errorf("Expected:\n%#v\nFound:\n%#v", expected, found) + } + + return nil + } +} + +const testFirewallPolicyConfig = ` +resource "openstack_fw_policy_v2" "accept_test" { + +} +` + +const testFirewallPolicyConfigAddRules = ` +resource "openstack_fw_policy_v2" "accept_test" { + name = "accept_test" + description = "terraform acceptance test" + rules = [ + "${openstack_fw_rule_v2.accept_test_udp_deny.id}", + "${openstack_fw_rule_v2.accept_test_tcp_allow.id}", + "${openstack_fw_rule_v2.accept_test_icmp_allow.id}" + ] +} + +resource "openstack_fw_rule_v2" "accept_test_tcp_allow" { + protocol = "tcp" + action = "allow" +} + +resource "openstack_fw_rule_v2" "accept_test_udp_deny" { + protocol = "udp" + action = "deny" +} + +resource "openstack_fw_rule_v2" "accept_test_icmp_allow" { + protocol = "icmp" + action = "allow" +} +` + +const testFirewallPolicyUpdateDeleteRule = ` +resource "openstack_fw_policy_v2" "accept_test" { + name = "accept_test" + description = "terraform acceptance test" + rules = [ + "${openstack_fw_rule_v2.accept_test_udp_deny.id}" + ] +} + +resource "openstack_fw_rule_v2" "accept_test_udp_deny" { + protocol = "udp" + action = "deny" +} +` From 0ab06af410f9ebd211d1cbcdb082d84f1031b6e4 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Mon, 9 Feb 2015 22:53:20 +0100 Subject: [PATCH 132/167] Add FWaaS firewall acceptance test --- .../resource_openstack_fw_firewall_v2_test.go | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go new file mode 100644 index 000000000..e32ded4c9 --- /dev/null +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go @@ -0,0 +1,151 @@ +package openstack + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/racker/perigee" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" +) + +func TestAccOpenstackFirewall(t *testing.T) { + + var policyID *string + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOpenstackFirewallDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testFirewallConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallExists("openstack_fw_firewall_v2.accept_test", "", "", policyID), + ), + }, + resource.TestStep{ + Config: testFirewallConfigUpdated, + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallExists("openstack_fw_firewall_v2.accept_test", "accept_test", "terraform acceptance test", policyID), + ), + }, + }, + }) +} + +func testAccCheckOpenstackFirewallDestroy(s *terraform.State) error { + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckOpenstackFirewallDestroy) Error creating OpenStack networking client: %s", err) + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "openstack_firewall" { + continue + } + _, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract() + if err == nil { + return fmt.Errorf("Firewall (%s) still exists.", rs.Primary.ID) + } + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 404 { + return httpError + } + } + return nil +} + +func testAccCheckFirewallExists(n, expectedName, expectedDescription string, policyID *string) resource.TestCheckFunc { + + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + networkingClient, err := config.networkingV2Client(OS_REGION_NAME) + if err != nil { + return fmt.Errorf("(testAccCheckFirewallExists) Error creating OpenStack networking client: %s", err) + } + + var found *firewalls.Firewall + for i := 0; i < 5; i++ { + // Firewall creation is asynchronous. Retry some times + // if we get a 404 error. Fail on any other error. + found, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract() + if err != nil { + httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + if !ok || httpError.Actual != 404 { + time.Sleep(time.Second) + continue + } + } + break + } + + if err != nil { + return err + } + + if found.Name != expectedName { + return fmt.Errorf("Expected Name to be <%s> but found <%s>", expectedName, found.Name) + } + if found.Description != expectedDescription { + return fmt.Errorf("Expected Description to be <%s> but found <%s>", expectedDescription, found.Description) + } + if found.PolicyID == "" { + return fmt.Errorf("Policy should not be empty") + } + if policyID != nil && found.PolicyID == *policyID { + return fmt.Errorf("Policy had not been correctly updated. Went from <%s> to <%s>", expectedName, found.Name) + } + + policyID = &found.PolicyID + + return nil + } +} + +const testFirewallConfig = ` +resource "openstack_fw_firewall_v2" "accept_test" { + policy_id = "${openstack_fw_policy_v2.accept_test_policy_1.id}" +} + +resource "openstack_fw_policy_v2" "accept_test_policy_1" { + name = "policy-1" +} +` + +const testFirewallConfigUpdated = ` +resource "openstack_fw_firewall_v2" "accept_test" { + name = "accept_test" + description = "terraform acceptance test" + policy_id = "${openstack_fw_policy_v2.accept_test_policy_1.id}" +} + +resource "openstack_fw_policy_v2" "accept_test_policy_1" { + name = "policy-1" +} +` + +const testFirewallConfigForceNew = ` +resource "openstack_fw_firewall_v2" "accept_test" { + name = "accept_test" + description = "terraform acceptance test" + policy_id = "${openstack_fw_policy_v2.accept_test_policy_2.id}" +} + +resource "openstack_fw_policy_v2" "accept_test_policy_2" { + name = "policy-2" +} +` From cfd3329e00d8c69e3d27909ef81098dd9f3f1327 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Tue, 10 Feb 2015 00:19:01 +0100 Subject: [PATCH 133/167] Add tenant_id attribute on FWaaS resources --- .../resource_openstack_fw_firewall_v2.go | 7 +++++++ .../resource_openstack_fw_policy_v2.go | 19 ++++++++++++++++--- .../resource_openstack_fw_policy_v2_test.go | 3 +++ .../resource_openstack_fw_rule_v2.go | 7 +++++++ .../resource_openstack_fw_rule_v2_test.go | 3 +++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go index e2125e7fb..598907ed1 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go @@ -45,6 +45,11 @@ func resourceFWFirewallV2() *schema.Resource { Optional: true, Default: true, }, + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, }, } } @@ -64,6 +69,7 @@ func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { Description: d.Get("description").(string), PolicyID: d.Get("policy_id").(string), AdminStateUp: &adminStateUp, + TenantID: d.Get("tenant_id").(string), } log.Printf("[DEBUG] Create firewall: %#v", firewallConfiguration) @@ -90,6 +96,7 @@ func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { d.Set("description", firewall.Description) d.Set("policy_id", firewall.PolicyID) d.Set("admin_state_up", firewall.AdminStateUp) + d.Set("tenant_id", firewall.TenantID) _, err = stateConf.WaitForState() diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go index 8f7b593d8..04fa03c07 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go @@ -43,6 +43,11 @@ func resourceFWPolicyV2() *schema.Resource { Optional: true, Default: false, }, + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, "rules": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -76,12 +81,16 @@ func resourceFirewallPolicyCreate(d *schema.ResourceData, meta interface{}) erro rules[i] = v.(string) } + audited := d.Get("audited").(bool) + shared := d.Get("shared").(bool) + opts := policies.CreateOpts{ Name: d.Get("name").(string), Description: d.Get("description").(string), - // Audited: d.Get("audited").(bool), - // Shared: d.Get("shared").(bool), - Rules: rules, + Audited: &audited, + Shared: &shared, + TenantID: d.Get("tenant_id").(string), + Rules: rules, } log.Printf("[DEBUG] Create firewall policy: %#v", opts) @@ -122,6 +131,10 @@ func resourceFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error } d.Set("name", policy.Name) + d.Set("description", policy.Description) + d.Set("shared", policy.Shared) + d.Set("audited", policy.Audited) + d.Set("tenant_id", policy.TenantID) return nil } diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go index 8a3f19ef5..0f0d98e4d 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go @@ -117,6 +117,9 @@ func testAccCheckFirewallPolicyExists(n string, expected *policies.Policy) resou } expected.ID = found.ID + // Erase the tenant id because we don't want to compare + // it as long it is not present in the expected + found.TenantID = "" if !reflect.DeepEqual(expected, found) { return fmt.Errorf("Expected:\n%#v\nFound:\n%#v", expected, found) diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go index 97d8da568..019ee5476 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go @@ -65,6 +65,11 @@ func resourceFWRuleV2() *schema.Resource { Optional: true, Default: true, }, + "tenant_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, }, } } @@ -90,6 +95,7 @@ func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error SourcePort: d.Get("source_port").(string), DestinationPort: d.Get("destination_port").(string), Enabled: &enabled, + TenantID: d.Get("tenant_id").(string), } log.Printf("[DEBUG] Create firewall rule: %#v", ruleConfiguration) @@ -114,6 +120,7 @@ func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error d.Set("source_port", rule.SourcePort) d.Set("destination_port", rule.DestinationPort) d.Set("enabled", rule.Enabled) + d.Set("tenant_id", rule.TenantID) return nil } diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go index 86e731b44..5d9d07cb8 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go @@ -135,6 +135,9 @@ func testAccCheckFirewallRuleExists(n string, expected *rules.Rule) resource.Tes } expected.ID = found.ID + // Erase the tenant id because we don't want to compare + // it as long it is not present in the expected + found.TenantID = "" if !reflect.DeepEqual(expected, found) { return fmt.Errorf("Expected:\n%#v\nFound:\n%#v", expected, found) From d6733fb37903d98b73df365997ef9530297662c6 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Tue, 17 Feb 2015 22:07:01 +0100 Subject: [PATCH 134/167] Fix code regarding to the latest gophercloud code --- .../resource_openstack_fw_firewall_v2.go | 11 ++++------ .../resource_openstack_fw_firewall_v2_test.go | 6 ++--- .../resource_openstack_fw_policy_v2.go | 12 +++++----- .../resource_openstack_fw_policy_v2_test.go | 6 ++--- .../resource_openstack_fw_rule_v2.go | 10 ++++----- .../resource_openstack_fw_rule_v2_test.go | 22 +++++++++---------- 6 files changed, 30 insertions(+), 37 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go index 598907ed1..cab83ce63 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" ) @@ -114,7 +113,7 @@ func resourceFirewallRead(d *schema.ResourceData, meta interface{}) error { firewall, err := firewalls.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return err } @@ -145,13 +144,11 @@ func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { opts := firewalls.UpdateOpts{} if d.HasChange("name") { - name := d.Get("name").(string) - opts.Name = &name + opts.Name = d.Get("name").(string) } if d.HasChange("description") { - description := d.Get("description").(string) - opts.Description = &description + opts.Description = d.Get("description").(string) } if d.HasChange("policy_id") { @@ -227,7 +224,7 @@ func WaitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id str log.Printf("[DEBUG] Get firewall %s => %#v", id, fw) if err != nil { - httpStatus := err.(*perigee.UnexpectedResponseCodeError) + httpStatus := err.(*gophercloud.UnexpectedResponseCodeError) log.Printf("[DEBUG] Get firewall %s status is %d", id, httpStatus.Actual) if httpStatus.Actual == 404 { diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go index e32ded4c9..a0adecdc4 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" ) @@ -51,7 +51,7 @@ func testAccCheckOpenstackFirewallDestroy(s *terraform.State) error { if err == nil { return fmt.Errorf("Firewall (%s) still exists.", rs.Primary.ID) } - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 404 { return httpError } @@ -84,7 +84,7 @@ func testAccCheckFirewallExists(n, expectedName, expectedDescription string, pol // if we get a 404 error. Fail on any other error. found, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 404 { time.Sleep(time.Second) continue diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go index 04fa03c07..c4d05bb8e 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" ) @@ -119,7 +119,7 @@ func resourceFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error policy, err := policies.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return err } @@ -150,13 +150,11 @@ func resourceFirewallPolicyUpdate(d *schema.ResourceData, meta interface{}) erro opts := policies.UpdateOpts{} if d.HasChange("name") { - name := d.Get("name").(string) - opts.Name = &name + opts.Name = d.Get("name").(string) } if d.HasChange("description") { - description := d.Get("description").(string) - opts.Description = &description + opts.Description = d.Get("description").(string) } if d.HasChange("rules") { @@ -193,7 +191,7 @@ func resourceFirewallPolicyDelete(d *schema.ResourceData, meta interface{}) erro break } - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 409 { return err } diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go index 0f0d98e4d..58c1c1ac1 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" ) @@ -70,7 +70,7 @@ func testAccCheckOpenstackFirewallPolicyDestroy(s *terraform.State) error { if err == nil { return fmt.Errorf("Firewall policy (%s) still exists.", rs.Primary.ID) } - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 404 { return httpError } @@ -103,7 +103,7 @@ func testAccCheckFirewallPolicyExists(n string, expected *policies.Policy) resou // if we get a 404 error. Fail on any other error. found, err = policies.Get(networkingClient, rs.Primary.ID).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 404 { time.Sleep(time.Second) continue diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go index 019ee5476..3d6b4a51b 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go @@ -5,7 +5,7 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) @@ -137,7 +137,7 @@ func resourceFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { rule, err := rules.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok { return err } @@ -172,12 +172,10 @@ func resourceFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) error opts := rules.UpdateOpts{} if d.HasChange("name") { - name := d.Get("name").(string) - opts.Name = &name + opts.Name = d.Get("name").(string) } if d.HasChange("description") { - description := d.Get("description").(string) - opts.Description = &description + opts.Description = d.Get("description").(string) } if d.HasChange("protocol") { opts.Protocol = d.Get("protocol").(string) diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go index 5d9d07cb8..748177375 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/racker/perigee" + "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) @@ -88,7 +88,7 @@ func testAccCheckOpenstackFirewallRuleDestroy(s *terraform.State) error { if err == nil { return fmt.Errorf("Firewall rule (%s) still exists.", rs.Primary.ID) } - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 404 { return httpError } @@ -121,7 +121,7 @@ func testAccCheckFirewallRuleExists(n string, expected *rules.Rule) resource.Tes // if we get a 404 error. Fail on any other error. found, err = rules.Get(networkingClient, rs.Primary.ID).Extract() if err != nil { - httpError, ok := err.(*perigee.UnexpectedResponseCodeError) + httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) if !ok || httpError.Actual != 404 { time.Sleep(time.Second) continue @@ -149,17 +149,17 @@ func testAccCheckFirewallRuleExists(n string, expected *rules.Rule) resource.Tes const testFirewallRuleMinimalConfig = ` resource "openstack_fw_rule_v2" "accept_test_minimal" { - protocol = "udp" - action = "deny" + protocol = "udp" + action = "deny" } ` const testFirewallRuleConfig = ` resource "openstack_fw_rule_v2" "accept_test" { - name = "accept_test" + name = "accept_test" description = "Terraform accept test" - protocol = "udp" - action = "deny" + protocol = "udp" + action = "deny" ip_version = 4 source_ip_address = "1.2.3.4" destination_ip_address = "4.3.2.0/24" @@ -171,10 +171,10 @@ resource "openstack_fw_rule_v2" "accept_test" { const testFirewallRuleUpdateAllFieldsConfig = ` resource "openstack_fw_rule_v2" "accept_test" { - name = "accept_test_updated_2" + name = "accept_test_updated_2" description = "Terraform accept test updated" - protocol = "tcp" - action = "allow" + protocol = "tcp" + action = "allow" ip_version = 4 source_ip_address = "1.2.3.0/24" destination_ip_address = "4.3.2.8" From 1c981d6f30fe1fb742cecbcb7fb6b58e93bb1f8e Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 18 Feb 2015 00:12:04 +0100 Subject: [PATCH 135/167] Fix race conditions on firewall state transition --- .../resource_openstack_fw_firewall_v2.go | 38 +++++++++++-------- .../resource_openstack_fw_firewall_v2_test.go | 12 ------ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go index cab83ce63..a216b506b 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go @@ -37,7 +37,6 @@ func resourceFWFirewallV2() *schema.Resource { "policy_id": &schema.Schema{ Type: schema.TypeString, Required: true, - ForceNew: true, }, "admin_state_up": &schema.Schema{ Type: schema.TypeBool, @@ -89,16 +88,10 @@ func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { MinTimeout: 2 * time.Second, } - d.SetId(firewall.ID) - - d.Set("name", firewall.Name) - d.Set("description", firewall.Description) - d.Set("policy_id", firewall.PolicyID) - d.Set("admin_state_up", firewall.AdminStateUp) - d.Set("tenant_id", firewall.TenantID) - _, err = stateConf.WaitForState() + d.SetId(firewall.ID) + return nil } @@ -129,6 +122,7 @@ func resourceFirewallRead(d *schema.ResourceData, meta interface{}) error { d.Set("description", firewall.Description) d.Set("policy_id", firewall.PolicyID) d.Set("admin_state_up", firewall.AdminStateUp) + d.Set("tenant_id", firewall.TenantID) return nil } @@ -155,14 +149,15 @@ func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { opts.PolicyID = d.Get("policy_id").(string) } - log.Printf("[DEBUG] Updating firewall with id %s: %#v", d.Id(), opts) - - if err := firewalls.Update(networkingClient, d.Id(), opts).Err; err != nil { - return err + if d.HasChange("admin_state_up") { + adminStateUp := d.Get("admin_state_up").(bool) + opts.AdminStateUp = &adminStateUp } + log.Printf("[DEBUG] Updating firewall with id %s: %#v", d.Id(), opts) + stateConf := &resource.StateChangeConf{ - Pending: []string{"PENDING_CREATE"}, + Pending: []string{"PENDING_CREATE", "PENDING_UPDATE"}, Target: "ACTIVE", Refresh: WaitForFirewallActive(networkingClient, d.Id()), Timeout: 30 * time.Second, @@ -172,7 +167,7 @@ func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { _, err = stateConf.WaitForState() - return err + return firewalls.Update(networkingClient, d.Id(), opts).Err } func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { @@ -184,13 +179,24 @@ func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } + stateConf := &resource.StateChangeConf{ + Pending: []string{"PENDING_CREATE", "PENDING_UPDATE"}, + Target: "ACTIVE", + Refresh: WaitForFirewallActive(networkingClient, d.Id()), + Timeout: 30 * time.Second, + Delay: 0, + MinTimeout: 2 * time.Second, + } + + _, err = stateConf.WaitForState() + err = firewalls.Delete(networkingClient, d.Id()).Err if err != nil { return err } - stateConf := &resource.StateChangeConf{ + stateConf = &resource.StateChangeConf{ Pending: []string{"DELETING"}, Target: "DELETED", Refresh: WaitForFirewallDeletion(networkingClient, d.Id()), diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go index a0adecdc4..cc5343660 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go @@ -127,18 +127,6 @@ resource "openstack_fw_policy_v2" "accept_test_policy_1" { ` const testFirewallConfigUpdated = ` -resource "openstack_fw_firewall_v2" "accept_test" { - name = "accept_test" - description = "terraform acceptance test" - policy_id = "${openstack_fw_policy_v2.accept_test_policy_1.id}" -} - -resource "openstack_fw_policy_v2" "accept_test_policy_1" { - name = "policy-1" -} -` - -const testFirewallConfigForceNew = ` resource "openstack_fw_firewall_v2" "accept_test" { name = "accept_test" description = "terraform acceptance test" From c5e861c0491dd03b3e701c0daf11164561a537a4 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 18 Feb 2015 00:52:54 +0100 Subject: [PATCH 136/167] Remove useless code --- .../openstack/resource_openstack_fw_rule_v2.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go index 3d6b4a51b..f3aacf510 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go @@ -110,18 +110,6 @@ func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error d.SetId(rule.ID) - d.Set("name", rule.Name) - d.Set("description", rule.Description) - d.Set("protocol", rule.Protocol) - d.Set("action", rule.Action) - d.Set("ip_version", rule.IPVersion) - d.Set("source_ip_address", rule.SourceIPAddress) - d.Set("destination_ip_address", rule.DestinationIPAddress) - d.Set("source_port", rule.SourcePort) - d.Set("destination_port", rule.DestinationPort) - d.Set("enabled", rule.Enabled) - d.Set("tenant_id", rule.TenantID) - return nil } From 54174dcc05efe3b560e105053eb3b90d0405af5c Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 18 Feb 2015 01:00:48 +0100 Subject: [PATCH 137/167] Fix firewall policies tests --- .../resource_openstack_fw_policy_v2_test.go | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go index 58c1c1ac1..ed268b7c3 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go @@ -2,7 +2,6 @@ package openstack import ( "fmt" - "reflect" "testing" "time" @@ -23,9 +22,7 @@ func TestAccOpenstackFirewallPolicy(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckFirewallPolicyExists( "openstack_fw_policy_v2.accept_test", - &policies.Policy{ - Rules: []string{}, - }), + "", "", 0), ), }, resource.TestStep{ @@ -33,14 +30,7 @@ func TestAccOpenstackFirewallPolicy(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckFirewallPolicyExists( "openstack_fw_policy_v2.accept_test", - &policies.Policy{ - Name: "accept_test", - Description: "terraform acceptance test", - Rules: []string{ - "", - "", - }, - }), + "accept_test", "terraform acceptance test", 2), ), }, resource.TestStep{ @@ -48,7 +38,7 @@ func TestAccOpenstackFirewallPolicy(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckFirewallPolicyExists( "openstack_fw_policy_v2.accept_test", - &policies.Policy{}), + "accept_test", "terraform acceptance test", 1), ), }, }, @@ -78,7 +68,7 @@ func testAccCheckOpenstackFirewallPolicyDestroy(s *terraform.State) error { return nil } -func testAccCheckFirewallPolicyExists(n string, expected *policies.Policy) resource.TestCheckFunc { +func testAccCheckFirewallPolicyExists(n, name, description string, ruleCount int) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -116,13 +106,16 @@ func testAccCheckFirewallPolicyExists(n string, expected *policies.Policy) resou return err } - expected.ID = found.ID - // Erase the tenant id because we don't want to compare - // it as long it is not present in the expected - found.TenantID = "" + if name != found.Name { + return fmt.Errorf("Expected name <%s>, but found <%s>", name, found.Name) + } - if !reflect.DeepEqual(expected, found) { - return fmt.Errorf("Expected:\n%#v\nFound:\n%#v", expected, found) + if description != found.Description { + return fmt.Errorf("Expected description <%s>, but found <%s>", description, found.Description) + } + + if ruleCount != len(found.Rules) { + return fmt.Errorf("Expected rule count <%d>, but found <%d>", ruleCount, len(found.Rules)) } return nil @@ -141,8 +134,7 @@ resource "openstack_fw_policy_v2" "accept_test" { description = "terraform acceptance test" rules = [ "${openstack_fw_rule_v2.accept_test_udp_deny.id}", - "${openstack_fw_rule_v2.accept_test_tcp_allow.id}", - "${openstack_fw_rule_v2.accept_test_icmp_allow.id}" + "${openstack_fw_rule_v2.accept_test_tcp_allow.id}" ] } @@ -155,11 +147,6 @@ resource "openstack_fw_rule_v2" "accept_test_udp_deny" { protocol = "udp" action = "deny" } - -resource "openstack_fw_rule_v2" "accept_test_icmp_allow" { - protocol = "icmp" - action = "allow" -} ` const testFirewallPolicyUpdateDeleteRule = ` From ed31588b8428f83f352a88f5d2e0f68d4a7ad20f Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 18 Feb 2015 01:01:46 +0100 Subject: [PATCH 138/167] Unassociate firewall rule from policy before delete --- .../openstack/resource_openstack_fw_rule_v2.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go index f3aacf510..4a50303e1 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) @@ -208,5 +209,18 @@ func resourceFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } + + rule, err := rules.Get(networkingClient, d.Id()).Extract() + if err != nil { + return err + } + + if rule.PolicyID != "" { + err := policies.RemoveRule(networkingClient, rule.PolicyID, rule.ID) + if err != nil { + return err + } + } + return rules.Delete(networkingClient, d.Id()).Err } From 1efaaeeca62738f3c35dd556a08c2db430353500 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 18 Feb 2015 01:27:45 +0100 Subject: [PATCH 139/167] Use d.GetOk to populate data in read operations --- .../resource_openstack_fw_firewall_v2.go | 34 +++++++++-- .../resource_openstack_fw_policy_v2.go | 34 +++++++++-- .../resource_openstack_fw_rule_v2.go | 56 ++++++++++++++++--- 3 files changed, 106 insertions(+), 18 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go index a216b506b..2d10fdc3c 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go @@ -118,11 +118,35 @@ func resourceFirewallRead(d *schema.ResourceData, meta interface{}) error { return err } - d.Set("name", firewall.Name) - d.Set("description", firewall.Description) - d.Set("policy_id", firewall.PolicyID) - d.Set("admin_state_up", firewall.AdminStateUp) - d.Set("tenant_id", firewall.TenantID) + if t, exists := d.GetOk("name"); exists && t != "" { + d.Set("name", firewall.Name) + } else { + d.Set("name", "") + } + + if t, exists := d.GetOk("description"); exists && t != "" { + d.Set("description", firewall.Description) + } else { + d.Set("description", "") + } + + if t, exists := d.GetOk("policy_id"); exists && t != "" { + d.Set("policy_id", firewall.PolicyID) + } else { + d.Set("policy_id", "") + } + + if t, exists := d.GetOk("admin_state_up"); exists && t != "" { + d.Set("admin_state_up", firewall.AdminStateUp) + } else { + d.Set("admin_state_up", "") + } + + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", firewall.TenantID) + } else { + d.Set("tenant_id", "") + } return nil } diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go index c4d05bb8e..ad096397b 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v2.go @@ -130,11 +130,35 @@ func resourceFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error return err } - d.Set("name", policy.Name) - d.Set("description", policy.Description) - d.Set("shared", policy.Shared) - d.Set("audited", policy.Audited) - d.Set("tenant_id", policy.TenantID) + if t, exists := d.GetOk("name"); exists && t != "" { + d.Set("name", policy.Name) + } else { + d.Set("name", "") + } + + if t, exists := d.GetOk("description"); exists && t != "" { + d.Set("description", policy.Description) + } else { + d.Set("description", "") + } + + if t, exists := d.GetOk("shared"); exists && t != "" { + d.Set("shared", policy.Shared) + } else { + d.Set("shared", "") + } + + if t, exists := d.GetOk("audited"); exists && t != "" { + d.Set("audited", policy.Audited) + } else { + d.Set("audited", "") + } + + if t, exists := d.GetOk("tenant_id"); exists && t != "" { + d.Set("tenant_id", policy.TenantID) + } else { + d.Set("tenant_id", "") + } return nil } diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go index 4a50303e1..9aa110757 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v2.go @@ -137,16 +137,56 @@ func resourceFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { return err } - d.Set("name", rule.Name) - d.Set("description", rule.Description) d.Set("protocol", rule.Protocol) d.Set("action", rule.Action) - d.Set("ip_version", rule.IPVersion) - d.Set("source_ip_address", rule.SourceIPAddress) - d.Set("destination_ip_address", rule.DestinationIPAddress) - d.Set("source_port", rule.SourcePort) - d.Set("destination_port", rule.DestinationPort) - d.Set("enabled", rule.Enabled) + + if t, exists := d.GetOk("name"); exists && t != "" { + d.Set("name", rule.Name) + } else { + d.Set("name", "") + } + + if t, exists := d.GetOk("description"); exists && t != "" { + d.Set("description", rule.Description) + } else { + d.Set("description", "") + } + + if t, exists := d.GetOk("ip_version"); exists && t != "" { + d.Set("ip_version", rule.IPVersion) + } else { + d.Set("ip_version", "") + } + + if t, exists := d.GetOk("source_ip_address"); exists && t != "" { + d.Set("source_ip_address", rule.SourceIPAddress) + } else { + d.Set("source_ip_address", "") + } + + if t, exists := d.GetOk("destination_ip_address"); exists && t != "" { + d.Set("destination_ip_address", rule.DestinationIPAddress) + } else { + d.Set("destination_ip_address", "") + } + + if t, exists := d.GetOk("source_port"); exists && t != "" { + d.Set("source_port", rule.SourcePort) + } else { + d.Set("source_port", "") + } + + if t, exists := d.GetOk("destination_port"); exists && t != "" { + d.Set("destination_port", rule.DestinationPort) + } else { + d.Set("destination_port", "") + } + + if t, exists := d.GetOk("enabled"); exists && t != "" { + d.Set("enabled", rule.Enabled) + } else { + d.Set("enabled", "") + } return nil } From 16a963313fe21196660ab83f2f061799668fe66a Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Thu, 19 Feb 2015 22:55:54 +0100 Subject: [PATCH 140/167] FWaaS version is actually v1 not v2 Rename files and methods. Confusion have been made between neutron version and FWaaS extension version. --- builtin/providers/openstack/provider.go | 6 +-- ...o => resource_openstack_fw_firewall_v1.go} | 30 +++++++------- ...resource_openstack_fw_firewall_v1_test.go} | 24 +++++------ ....go => resource_openstack_fw_policy_v1.go} | 18 ++++----- ...> resource_openstack_fw_policy_v1_test.go} | 40 +++++++++---------- ...v2.go => resource_openstack_fw_rule_v1.go} | 18 ++++----- ... => resource_openstack_fw_rule_v1_test.go} | 26 ++++++------ 7 files changed, 81 insertions(+), 81 deletions(-) rename builtin/providers/openstack/{resource_openstack_fw_firewall_v2.go => resource_openstack_fw_firewall_v1.go} (87%) rename builtin/providers/openstack/{resource_openstack_fw_firewall_v2_test.go => resource_openstack_fw_firewall_v1_test.go} (78%) rename builtin/providers/openstack/{resource_openstack_fw_policy_v2.go => resource_openstack_fw_policy_v1.go} (90%) rename builtin/providers/openstack/{resource_openstack_fw_policy_v2_test.go => resource_openstack_fw_policy_v1_test.go} (75%) rename builtin/providers/openstack/{resource_openstack_fw_rule_v2.go => resource_openstack_fw_rule_v1.go} (92%) rename builtin/providers/openstack/{resource_openstack_fw_rule_v2_test.go => resource_openstack_fw_rule_v1_test.go} (86%) diff --git a/builtin/providers/openstack/provider.go b/builtin/providers/openstack/provider.go index 7e0880007..a43242333 100644 --- a/builtin/providers/openstack/provider.go +++ b/builtin/providers/openstack/provider.go @@ -64,9 +64,9 @@ func Provider() terraform.ResourceProvider { "openstack_compute_keypair_v2": resourceComputeKeypairV2(), "openstack_compute_secgroup_v2": resourceComputeSecGroupV2(), "openstack_compute_floatingip_v2": resourceComputeFloatingIPV2(), - "openstack_fw_firewall_v2": resourceFWFirewallV2(), - "openstack_fw_policy_v2": resourceFWPolicyV2(), - "openstack_fw_rule_v2": resourceFWRuleV2(), + "openstack_fw_firewall_v1": resourceFWFirewallV1(), + "openstack_fw_policy_v1": resourceFWPolicyV1(), + "openstack_fw_rule_v1": resourceFWRuleV1(), "openstack_lb_monitor_v1": resourceLBMonitorV1(), "openstack_lb_pool_v1": resourceLBPoolV1(), "openstack_lb_vip_v1": resourceLBVipV1(), diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go similarity index 87% rename from builtin/providers/openstack/resource_openstack_fw_firewall_v2.go rename to builtin/providers/openstack/resource_openstack_fw_firewall_v1.go index 2d10fdc3c..21c93d33d 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go @@ -12,12 +12,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" ) -func resourceFWFirewallV2() *schema.Resource { +func resourceFWFirewallV1() *schema.Resource { return &schema.Resource{ - Create: resourceFirewallCreate, - Read: resourceFirewallRead, - Update: resourceFirewallUpdate, - Delete: resourceFirewallDelete, + Create: resourceFWFirewallV1Create, + Read: resourceFWFirewallV1Read, + Update: resourceFWFirewallV1Update, + Delete: resourceFWFirewallV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -52,7 +52,7 @@ func resourceFWFirewallV2() *schema.Resource { } } -func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { +func resourceFWFirewallV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(d.Get("region").(string)) @@ -82,7 +82,7 @@ func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { stateConf := &resource.StateChangeConf{ Pending: []string{"PENDING_CREATE"}, Target: "ACTIVE", - Refresh: WaitForFirewallActive(networkingClient, firewall.ID), + Refresh: waitForFirewallActive(networkingClient, firewall.ID), Timeout: 30 * time.Second, Delay: 0, MinTimeout: 2 * time.Second, @@ -95,7 +95,7 @@ func resourceFirewallCreate(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceFirewallRead(d *schema.ResourceData, meta interface{}) error { +func resourceFWFirewallV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retrieve information about firewall: %s", d.Id()) config := meta.(*Config) @@ -151,7 +151,7 @@ func resourceFirewallRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceFWFirewallV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(d.Get("region").(string)) @@ -183,7 +183,7 @@ func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { stateConf := &resource.StateChangeConf{ Pending: []string{"PENDING_CREATE", "PENDING_UPDATE"}, Target: "ACTIVE", - Refresh: WaitForFirewallActive(networkingClient, d.Id()), + Refresh: waitForFirewallActive(networkingClient, d.Id()), Timeout: 30 * time.Second, Delay: 0, MinTimeout: 2 * time.Second, @@ -194,7 +194,7 @@ func resourceFirewallUpdate(d *schema.ResourceData, meta interface{}) error { return firewalls.Update(networkingClient, d.Id(), opts).Err } -func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { +func resourceFWFirewallV1Delete(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Destroy firewall: %s", d.Id()) config := meta.(*Config) @@ -206,7 +206,7 @@ func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { stateConf := &resource.StateChangeConf{ Pending: []string{"PENDING_CREATE", "PENDING_UPDATE"}, Target: "ACTIVE", - Refresh: WaitForFirewallActive(networkingClient, d.Id()), + Refresh: waitForFirewallActive(networkingClient, d.Id()), Timeout: 30 * time.Second, Delay: 0, MinTimeout: 2 * time.Second, @@ -223,7 +223,7 @@ func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { stateConf = &resource.StateChangeConf{ Pending: []string{"DELETING"}, Target: "DELETED", - Refresh: WaitForFirewallDeletion(networkingClient, d.Id()), + Refresh: waitForFirewallDeletion(networkingClient, d.Id()), Timeout: 2 * time.Minute, Delay: 0, MinTimeout: 2 * time.Second, @@ -234,7 +234,7 @@ func resourceFirewallDelete(d *schema.ResourceData, meta interface{}) error { return err } -func WaitForFirewallActive(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc { +func waitForFirewallActive(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { fw, err := firewalls.Get(networkingClient, id).Extract() @@ -247,7 +247,7 @@ func WaitForFirewallActive(networkingClient *gophercloud.ServiceClient, id strin } } -func WaitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc { +func waitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { fw, err := firewalls.Get(networkingClient, id).Extract() diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v1_test.go similarity index 78% rename from builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go rename to builtin/providers/openstack/resource_openstack_fw_firewall_v1_test.go index cc5343660..34112f778 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v1_test.go @@ -11,32 +11,32 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls" ) -func TestAccOpenstackFirewall(t *testing.T) { +func TestAccFWFirewallV1(t *testing.T) { var policyID *string resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckOpenstackFirewallDestroy, + CheckDestroy: testAccCheckFWFirewallV1Destroy, Steps: []resource.TestStep{ resource.TestStep{ Config: testFirewallConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallExists("openstack_fw_firewall_v2.accept_test", "", "", policyID), + testAccCheckFWFirewallV1Exists("openstack_fw_firewall_v1.accept_test", "", "", policyID), ), }, resource.TestStep{ Config: testFirewallConfigUpdated, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallExists("openstack_fw_firewall_v2.accept_test", "accept_test", "terraform acceptance test", policyID), + testAccCheckFWFirewallV1Exists("openstack_fw_firewall_v1.accept_test", "accept_test", "terraform acceptance test", policyID), ), }, }, }) } -func testAccCheckOpenstackFirewallDestroy(s *terraform.State) error { +func testAccCheckFWFirewallV1Destroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -59,7 +59,7 @@ func testAccCheckOpenstackFirewallDestroy(s *terraform.State) error { return nil } -func testAccCheckFirewallExists(n, expectedName, expectedDescription string, policyID *string) resource.TestCheckFunc { +func testAccCheckFWFirewallV1Exists(n, expectedName, expectedDescription string, policyID *string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -117,23 +117,23 @@ func testAccCheckFirewallExists(n, expectedName, expectedDescription string, pol } const testFirewallConfig = ` -resource "openstack_fw_firewall_v2" "accept_test" { - policy_id = "${openstack_fw_policy_v2.accept_test_policy_1.id}" +resource "openstack_fw_firewall_v1" "accept_test" { + policy_id = "${openstack_fw_policy_v1.accept_test_policy_1.id}" } -resource "openstack_fw_policy_v2" "accept_test_policy_1" { +resource "openstack_fw_policy_v1" "accept_test_policy_1" { name = "policy-1" } ` const testFirewallConfigUpdated = ` -resource "openstack_fw_firewall_v2" "accept_test" { +resource "openstack_fw_firewall_v1" "accept_test" { name = "accept_test" description = "terraform acceptance test" - policy_id = "${openstack_fw_policy_v2.accept_test_policy_2.id}" + policy_id = "${openstack_fw_policy_v1.accept_test_policy_2.id}" } -resource "openstack_fw_policy_v2" "accept_test_policy_2" { +resource "openstack_fw_policy_v1" "accept_test_policy_2" { name = "policy-2" } ` diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go similarity index 90% rename from builtin/providers/openstack/resource_openstack_fw_policy_v2.go rename to builtin/providers/openstack/resource_openstack_fw_policy_v1.go index ad096397b..e9fc1645b 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go @@ -11,12 +11,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" ) -func resourceFWPolicyV2() *schema.Resource { +func resourceFWPolicyV1() *schema.Resource { return &schema.Resource{ - Create: resourceFirewallPolicyCreate, - Read: resourceFirewallPolicyRead, - Update: resourceFirewallPolicyUpdate, - Delete: resourceFirewallPolicyDelete, + Create: resourceFWPolicyV1Create, + Read: resourceFWPolicyV1Read, + Update: resourceFWPolicyV1Update, + Delete: resourceFWPolicyV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -60,7 +60,7 @@ func resourceFWPolicyV2() *schema.Resource { } } -func resourceFirewallPolicyCreate(d *schema.ResourceData, meta interface{}) error { +func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error { // TODO To remove time.Sleep(time.Second * 5) @@ -107,7 +107,7 @@ func resourceFirewallPolicyCreate(d *schema.ResourceData, meta interface{}) erro return nil } -func resourceFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error { +func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retrieve information about firewall policy: %s", d.Id()) config := meta.(*Config) @@ -163,7 +163,7 @@ func resourceFirewallPolicyRead(d *schema.ResourceData, meta interface{}) error return nil } -func resourceFirewallPolicyUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(d.Get("region").(string)) @@ -199,7 +199,7 @@ func resourceFirewallPolicyUpdate(d *schema.ResourceData, meta interface{}) erro return policies.Update(networkingClient, d.Id(), opts).Err } -func resourceFirewallPolicyDelete(d *schema.ResourceData, meta interface{}) error { +func resourceFWPolicyV1Delete(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Destroy firewall policy: %s", d.Id()) config := meta.(*Config) diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_policy_v1_test.go similarity index 75% rename from builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go rename to builtin/providers/openstack/resource_openstack_fw_policy_v1_test.go index ed268b7c3..1a37a383f 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v1_test.go @@ -11,33 +11,33 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" ) -func TestAccOpenstackFirewallPolicy(t *testing.T) { +func TestAccFWPolicyV1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckOpenstackFirewallPolicyDestroy, + CheckDestroy: testAccCheckFWPolicyV1Destroy, Steps: []resource.TestStep{ resource.TestStep{ Config: testFirewallPolicyConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists( - "openstack_fw_policy_v2.accept_test", + testAccCheckFWPolicyV1Exists( + "openstack_fw_policy_v1.accept_test", "", "", 0), ), }, resource.TestStep{ Config: testFirewallPolicyConfigAddRules, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists( - "openstack_fw_policy_v2.accept_test", + testAccCheckFWPolicyV1Exists( + "openstack_fw_policy_v1.accept_test", "accept_test", "terraform acceptance test", 2), ), }, resource.TestStep{ Config: testFirewallPolicyUpdateDeleteRule, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists( - "openstack_fw_policy_v2.accept_test", + testAccCheckFWPolicyV1Exists( + "openstack_fw_policy_v1.accept_test", "accept_test", "terraform acceptance test", 1), ), }, @@ -45,7 +45,7 @@ func TestAccOpenstackFirewallPolicy(t *testing.T) { }) } -func testAccCheckOpenstackFirewallPolicyDestroy(s *terraform.State) error { +func testAccCheckFWPolicyV1Destroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -53,7 +53,7 @@ func testAccCheckOpenstackFirewallPolicyDestroy(s *terraform.State) error { return fmt.Errorf("(testAccCheckOpenstackFirewallPolicyDestroy) Error creating OpenStack networking client: %s", err) } for _, rs := range s.RootModule().Resources { - if rs.Type != "openstack_fw_policy_v2" { + if rs.Type != "openstack_fw_policy_v1" { continue } _, err = policies.Get(networkingClient, rs.Primary.ID).Extract() @@ -68,7 +68,7 @@ func testAccCheckOpenstackFirewallPolicyDestroy(s *terraform.State) error { return nil } -func testAccCheckFirewallPolicyExists(n, name, description string, ruleCount int) resource.TestCheckFunc { +func testAccCheckFWPolicyV1Exists(n, name, description string, ruleCount int) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -123,42 +123,42 @@ func testAccCheckFirewallPolicyExists(n, name, description string, ruleCount int } const testFirewallPolicyConfig = ` -resource "openstack_fw_policy_v2" "accept_test" { +resource "openstack_fw_policy_v1" "accept_test" { } ` const testFirewallPolicyConfigAddRules = ` -resource "openstack_fw_policy_v2" "accept_test" { +resource "openstack_fw_policy_v1" "accept_test" { name = "accept_test" description = "terraform acceptance test" rules = [ - "${openstack_fw_rule_v2.accept_test_udp_deny.id}", - "${openstack_fw_rule_v2.accept_test_tcp_allow.id}" + "${openstack_fw_rule_v1.accept_test_udp_deny.id}", + "${openstack_fw_rule_v1.accept_test_tcp_allow.id}" ] } -resource "openstack_fw_rule_v2" "accept_test_tcp_allow" { +resource "openstack_fw_rule_v1" "accept_test_tcp_allow" { protocol = "tcp" action = "allow" } -resource "openstack_fw_rule_v2" "accept_test_udp_deny" { +resource "openstack_fw_rule_v1" "accept_test_udp_deny" { protocol = "udp" action = "deny" } ` const testFirewallPolicyUpdateDeleteRule = ` -resource "openstack_fw_policy_v2" "accept_test" { +resource "openstack_fw_policy_v1" "accept_test" { name = "accept_test" description = "terraform acceptance test" rules = [ - "${openstack_fw_rule_v2.accept_test_udp_deny.id}" + "${openstack_fw_rule_v1.accept_test_udp_deny.id}" ] } -resource "openstack_fw_rule_v2" "accept_test_udp_deny" { +resource "openstack_fw_rule_v1" "accept_test_udp_deny" { protocol = "udp" action = "deny" } diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go similarity index 92% rename from builtin/providers/openstack/resource_openstack_fw_rule_v2.go rename to builtin/providers/openstack/resource_openstack_fw_rule_v1.go index 9aa110757..bd418e9b8 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go @@ -10,12 +10,12 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) -func resourceFWRuleV2() *schema.Resource { +func resourceFWRuleV1() *schema.Resource { return &schema.Resource{ - Create: resourceFirewallRuleCreate, - Read: resourceFirewallRuleRead, - Update: resourceFirewallRuleUpdate, - Delete: resourceFirewallRuleDelete, + Create: resourceFWRuleV1Create, + Read: resourceFWRuleV1Read, + Update: resourceFWRuleV1Update, + Delete: resourceFWRuleV1Delete, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ @@ -75,7 +75,7 @@ func resourceFWRuleV2() *schema.Resource { } } -func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error { +func resourceFWRuleV1Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(d.Get("region").(string)) @@ -114,7 +114,7 @@ func resourceFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error return nil } -func resourceFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { +func resourceFWRuleV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retrieve information about firewall rule: %s", d.Id()) config := meta.(*Config) @@ -191,7 +191,7 @@ func resourceFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceFWRuleV1Update(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { @@ -241,7 +241,7 @@ func resourceFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) error return rules.Update(networkingClient, d.Id(), opts).Err } -func resourceFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { +func resourceFWRuleV1Delete(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Destroy firewall rule: %s", d.Id()) config := meta.(*Config) diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go b/builtin/providers/openstack/resource_openstack_fw_rule_v1_test.go similarity index 86% rename from builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go rename to builtin/providers/openstack/resource_openstack_fw_rule_v1_test.go index 748177375..ba96bb8b1 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v1_test.go @@ -12,17 +12,17 @@ import ( "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) -func TestAccOpenstackFirewallRule(t *testing.T) { +func TestAccFWRuleV1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckOpenstackFirewallRuleDestroy, + CheckDestroy: testAccCheckFWRuleV1Destroy, Steps: []resource.TestStep{ resource.TestStep{ Config: testFirewallRuleMinimalConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallRuleExists( - "openstack_fw_rule_v2.accept_test_minimal", + testAccCheckFWRuleV1Exists( + "openstack_fw_rule_v1.accept_test_minimal", &rules.Rule{ Protocol: "udp", Action: "deny", @@ -34,8 +34,8 @@ func TestAccOpenstackFirewallRule(t *testing.T) { resource.TestStep{ Config: testFirewallRuleConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallRuleExists( - "openstack_fw_rule_v2.accept_test", + testAccCheckFWRuleV1Exists( + "openstack_fw_rule_v1.accept_test", &rules.Rule{ Name: "accept_test", Protocol: "udp", @@ -53,8 +53,8 @@ func TestAccOpenstackFirewallRule(t *testing.T) { resource.TestStep{ Config: testFirewallRuleUpdateAllFieldsConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallRuleExists( - "openstack_fw_rule_v2.accept_test", + testAccCheckFWRuleV1Exists( + "openstack_fw_rule_v1.accept_test", &rules.Rule{ Name: "accept_test_updated_2", Protocol: "tcp", @@ -73,7 +73,7 @@ func TestAccOpenstackFirewallRule(t *testing.T) { }) } -func testAccCheckOpenstackFirewallRuleDestroy(s *terraform.State) error { +func testAccCheckFWRuleV1Destroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -96,7 +96,7 @@ func testAccCheckOpenstackFirewallRuleDestroy(s *terraform.State) error { return nil } -func testAccCheckFirewallRuleExists(n string, expected *rules.Rule) resource.TestCheckFunc { +func testAccCheckFWRuleV1Exists(n string, expected *rules.Rule) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -148,14 +148,14 @@ func testAccCheckFirewallRuleExists(n string, expected *rules.Rule) resource.Tes } const testFirewallRuleMinimalConfig = ` -resource "openstack_fw_rule_v2" "accept_test_minimal" { +resource "openstack_fw_rule_v1" "accept_test_minimal" { protocol = "udp" action = "deny" } ` const testFirewallRuleConfig = ` -resource "openstack_fw_rule_v2" "accept_test" { +resource "openstack_fw_rule_v1" "accept_test" { name = "accept_test" description = "Terraform accept test" protocol = "udp" @@ -170,7 +170,7 @@ resource "openstack_fw_rule_v2" "accept_test" { ` const testFirewallRuleUpdateAllFieldsConfig = ` -resource "openstack_fw_rule_v2" "accept_test" { +resource "openstack_fw_rule_v1" "accept_test" { name = "accept_test_updated_2" description = "Terraform accept test updated" protocol = "tcp" From bdeca31731ef0890c5cf162083185ae037d1b4dd Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Thu, 19 Feb 2015 23:31:19 +0100 Subject: [PATCH 141/167] remove boilerplate code using CheckDeleted --- .../openstack/resource_openstack_fw_firewall_v1.go | 12 ++---------- .../openstack/resource_openstack_fw_policy_v1.go | 10 +--------- .../openstack/resource_openstack_fw_rule_v1.go | 11 +---------- 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go index 21c93d33d..732b41622 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go @@ -105,17 +105,9 @@ func resourceFWFirewallV1Read(d *schema.ResourceData, meta interface{}) error { } firewall, err := firewalls.Get(networkingClient, d.Id()).Extract() - if err != nil { - httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) - if !ok { - return err - } - if httpError.Actual == 404 { - d.SetId("") - return nil - } - return err + if err != nil { + return CheckDeleted(d, err, "LB pool") } if t, exists := d.GetOk("name"); exists && t != "" { diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go index e9fc1645b..7837e5a43 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go @@ -119,15 +119,7 @@ func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error { policy, err := policies.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) - if !ok { - return err - } - if httpError.Actual == 404 { - d.SetId("") - return nil - } - return err + return CheckDeleted(d, err, "LB pool") } if t, exists := d.GetOk("name"); exists && t != "" { diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v1.go b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go index bd418e9b8..607d02b21 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go @@ -5,7 +5,6 @@ import ( "log" "github.com/hashicorp/terraform/helper/schema" - "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules" ) @@ -126,15 +125,7 @@ func resourceFWRuleV1Read(d *schema.ResourceData, meta interface{}) error { rule, err := rules.Get(networkingClient, d.Id()).Extract() if err != nil { - httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) - if !ok { - return err - } - if httpError.Actual == 404 { - d.SetId("") - return nil - } - return err + return CheckDeleted(d, err, "LB pool") } d.Set("protocol", rule.Protocol) From 83160acf69c4752d86fd8fd15b34a1035a1fa5f9 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Thu, 19 Feb 2015 23:44:49 +0100 Subject: [PATCH 142/167] Return Read call result in Create & Update --- .../openstack/resource_openstack_fw_firewall_v1.go | 9 +++++++-- .../openstack/resource_openstack_fw_policy_v1.go | 9 +++++++-- .../providers/openstack/resource_openstack_fw_rule_v1.go | 9 +++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go index 732b41622..9d5764357 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go @@ -92,7 +92,7 @@ func resourceFWFirewallV1Create(d *schema.ResourceData, meta interface{}) error d.SetId(firewall.ID) - return nil + return resourceFWFirewallV1Read(d, meta) } func resourceFWFirewallV1Read(d *schema.ResourceData, meta interface{}) error { @@ -183,7 +183,12 @@ func resourceFWFirewallV1Update(d *schema.ResourceData, meta interface{}) error _, err = stateConf.WaitForState() - return firewalls.Update(networkingClient, d.Id(), opts).Err + err = firewalls.Update(networkingClient, d.Id(), opts).Err + if err != nil { + return err + } + + return resourceFWFirewallV1Read(d, meta) } func resourceFWFirewallV1Delete(d *schema.ResourceData, meta interface{}) error { diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go index 7837e5a43..815059752 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go @@ -104,7 +104,7 @@ func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error { d.SetId(policy.ID) - return nil + return resourceFWPolicyV1Read(d, meta) } func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error { @@ -188,7 +188,12 @@ func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Updating firewall policy with id %s: %#v", d.Id(), opts) - return policies.Update(networkingClient, d.Id(), opts).Err + err = policies.Update(networkingClient, d.Id(), opts).Err + if err != nil { + return err + } + + return resourceFWPolicyV1Read(d, meta) } func resourceFWPolicyV1Delete(d *schema.ResourceData, meta interface{}) error { diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v1.go b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go index 607d02b21..f4dbb2fc7 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go @@ -110,7 +110,7 @@ func resourceFWRuleV1Create(d *schema.ResourceData, meta interface{}) error { d.SetId(rule.ID) - return nil + return resourceFWRuleV1Read(d, meta) } func resourceFWRuleV1Read(d *schema.ResourceData, meta interface{}) error { @@ -229,7 +229,12 @@ func resourceFWRuleV1Update(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Updating firewall rules: %#v", opts) - return rules.Update(networkingClient, d.Id(), opts).Err + err = rules.Update(networkingClient, d.Id(), opts).Err + if err != nil { + return err + } + + return resourceFWRuleV1Read(d, meta) } func resourceFWRuleV1Delete(d *schema.ResourceData, meta interface{}) error { From ba880b136bee77159ab98bde438ed61d76ec3949 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Thu, 19 Feb 2015 23:53:30 +0100 Subject: [PATCH 143/167] Code clean-up --- .../providers/openstack/resource_openstack_fw_firewall_v1.go | 3 +-- builtin/providers/openstack/resource_openstack_fw_policy_v1.go | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go index 9d5764357..16b2014a7 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go @@ -1,7 +1,6 @@ package openstack import ( - "errors" "fmt" "log" "time" @@ -258,7 +257,7 @@ func waitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id str log.Printf("[DEBUG] Firewall %s is actually deleted", id) return "", "DELETED", nil } - return nil, "", errors.New(fmt.Sprintf("Unexpected status code %d", httpStatus.Actual)) + return nil, "", fmt.Errorf("Unexpected status code %d", httpStatus.Actual) } log.Printf("[DEBUG] Firewall %s deletion is pending", id) diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go index 815059752..e58b994fc 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go @@ -62,9 +62,6 @@ func resourceFWPolicyV1() *schema.Resource { func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error { - // TODO To remove - time.Sleep(time.Second * 5) - config := meta.(*Config) networkingClient, err := config.networkingV2Client(d.Get("region").(string)) if err != nil { From f5feb7fbbb4f8db0b7ae88e17e8aad27d7c5dc8a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 4 Mar 2015 04:41:15 +0000 Subject: [PATCH 144/167] Allows "self" to be discovered and recorded correctly. --- .../resource_openstack_compute_secgroup_v2.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index cf00f7f1b..3860dc69c 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -72,6 +72,7 @@ func resourceComputeSecGroupV2() *schema.Resource { "self": &schema.Schema{ Type: schema.TypeBool, Optional: true, + Default: false, ForceNew: false, }, }, @@ -126,8 +127,16 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err d.Set("region", d.Get("region").(string)) d.Set("name", sg.Name) d.Set("description", sg.Description) - log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rulesToMap(sg.Rules)) - d.Set("rule", rulesToMap(sg.Rules)) + rtm := rulesToMap(sg.Rules) + for _, v := range rtm { + if v["group"] == d.Get("name") { + v["self"] = "1" + } else { + v["self"] = "0" + } + } + log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm) + d.Set("rule", rtm) return nil } @@ -267,6 +276,7 @@ func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} { "to_port": sgr.ToPort, "ip_protocol": sgr.IPProtocol, "cidr": sgr.IPRange.CIDR, + "group": sgr.Group.Name, } } return sgrMap From f011462e30ff9be274cc54872701fba67e42e3e7 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Fri, 13 Mar 2015 22:24:13 +0000 Subject: [PATCH 145/167] Volume Pending States This commit adds pending states for volume attachment, detachment, and deletion. --- .../openstack/resource_openstack_blockstorage_volume_v1.go | 4 ++-- .../openstack/resource_openstack_compute_instance_v2.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go index eefd75f7e..598640b82 100644 --- a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -264,7 +264,7 @@ func resourceBlockStorageVolumeV1Delete(d *schema.ResourceData, meta interface{} } stateConf := &resource.StateChangeConf{ - Pending: []string{"in-use"}, + Pending: []string{"in-use", "attaching"}, Target: "available", Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()), Timeout: 10 * time.Minute, @@ -290,7 +290,7 @@ func resourceBlockStorageVolumeV1Delete(d *schema.ResourceData, meta interface{} log.Printf("[DEBUG] Waiting for volume (%s) to delete", d.Id()) stateConf := &resource.StateChangeConf{ - Pending: []string{"deleting"}, + Pending: []string{"deleting", "available"}, Target: "deleted", Refresh: VolumeV1StateRefreshFunc(blockStorageClient, d.Id()), Timeout: 10 * time.Minute, diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 348806931..90b64ed1d 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -944,6 +944,7 @@ func attachVolumesToInstance(computeClient *gophercloud.ServiceClient, blockClie } stateConf := &resource.StateChangeConf{ + Pending: []string{"attaching", "available"}, Target: "in-use", Refresh: VolumeV1StateRefreshFunc(blockClient, va["volume_id"].(string)), Timeout: 30 * time.Minute, @@ -972,6 +973,7 @@ func detachVolumesFromInstance(computeClient *gophercloud.ServiceClient, blockCl } stateConf := &resource.StateChangeConf{ + Pending: []string{"detaching", "in-use"}, Target: "available", Refresh: VolumeV1StateRefreshFunc(blockClient, va["volume_id"].(string)), Timeout: 30 * time.Minute, From 2e37784065dac7b024618f887c7966835efe5b22 Mon Sep 17 00:00:00 2001 From: Julien Vey Date: Tue, 24 Mar 2015 13:59:55 +0100 Subject: [PATCH 146/167] Fix general comments by @phinze --- ...source_openstack_blockstorage_volume_v1.go | 47 +++------------- ...esource_openstack_compute_floatingip_v2.go | 9 +-- .../resource_openstack_compute_instance_v2.go | 6 +- .../resource_openstack_compute_keypair_v2.go | 3 +- .../resource_openstack_compute_secgroup_v2.go | 2 +- .../resource_openstack_fw_firewall_v1.go | 34 ++---------- .../resource_openstack_fw_policy_v1.go | 37 ++----------- .../resource_openstack_fw_rule_v1.go | 55 +++---------------- .../resource_openstack_lb_monitor_v1.go | 38 ++----------- .../resource_openstack_lb_pool_v1.go | 11 +--- .../openstack/resource_openstack_lb_vip_v1.go | 3 +- ...urce_openstack_networking_floatingip_v2.go | 8 ++- ...esource_openstack_networking_network_v2.go | 31 ++--------- ...penstack_networking_router_interface_v2.go | 4 +- ...resource_openstack_networking_router_v2.go | 31 ++--------- ...resource_openstack_networking_subnet_v2.go | 46 +++------------- ...ce_openstack_objectstorage_container_v1.go | 2 +- 17 files changed, 67 insertions(+), 300 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go index 598640b82..dc2638590 100644 --- a/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go +++ b/builtin/providers/openstack/resource_openstack_blockstorage_volume_v1.go @@ -111,7 +111,7 @@ func resourceBlockStorageVolumeV1Create(d *schema.ResourceData, meta interface{} Metadata: resourceContainerMetadataV2(d), } - log.Printf("[INFO] Requesting volume creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) v, err := volumes.Create(blockStorageClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack volume: %s", err) @@ -156,48 +156,15 @@ func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{}) return CheckDeleted(d, err, "volume") } - log.Printf("\n\ngot volume: %+v\n\n", v) - log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v) - d.Set("region", d.Get("region").(string)) d.Set("size", v.Size) - - if t, exists := d.GetOk("description"); exists && t != "" { - d.Set("description", v.Description) - } else { - d.Set("description", "") - } - - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", v.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("snapshot_id"); exists && t != "" { - d.Set("snapshot_id", v.SnapshotID) - } else { - d.Set("snapshot_id", "") - } - - if t, exists := d.GetOk("source_vol_id"); exists && t != "" { - d.Set("source_vol_id", v.SourceVolID) - } else { - d.Set("source_vol_id", "") - } - - if t, exists := d.GetOk("volume_type"); exists && t != "" { - d.Set("volume_type", v.VolumeType) - } else { - d.Set("volume_type", "") - } - - if t, exists := d.GetOk("metadata"); exists && t != "" { - d.Set("metadata", v.Metadata) - } else { - d.Set("metadata", "") - } + d.Set("description", v.Description) + d.Set("name", v.Name) + d.Set("snapshot_id", v.SnapshotID) + d.Set("source_vol_id", v.SourceVolID) + d.Set("volume_type", v.VolumeType) + d.Set("metadata", v.Metadata) if len(v.Attachments) > 0 { attachments := make([]map[string]interface{}, len(v.Attachments)) diff --git a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go index 3501fe282..bb2facc4e 100644 --- a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2.go @@ -30,12 +30,6 @@ func resourceComputeFloatingIPV2() *schema.Resource { DefaultFunc: envDefaultFunc("OS_POOL_NAME"), }, - // exported - "id": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "address": &schema.Schema{ Type: schema.TypeString, Computed: true, @@ -64,6 +58,7 @@ func resourceComputeFloatingIPV2Create(d *schema.ResourceData, meta interface{}) createOpts := &floatingip.CreateOpts{ Pool: d.Get("pool").(string), } + log.Printf("[DEBUG] Create Options: %#v", createOpts) newFip, err := floatingip.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating Floating IP: %s", err) @@ -88,8 +83,6 @@ func resourceComputeFloatingIPV2Read(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Retrieved Floating IP %s: %+v", d.Id(), fip) - d.Set("id", d.Id()) - d.Set("region", d.Get("region").(string)) d.Set("pool", fip.Pool) d.Set("instance_id", fip.InstanceID) d.Set("address", fip.IP) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 90b64ed1d..63508ffa7 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -241,7 +241,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e } } - log.Printf("[INFO] Requesting instance creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) server, err := servers.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack server: %s", err) @@ -320,7 +320,6 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err log.Printf("[DEBUG] Retreived Server %s: %+v", d.Id(), server) - d.Set("region", d.Get("region").(string)) d.Set("name", server.Name) d.Set("access_ip_v4", server.AccessIPv4) d.Set("access_ip_v6", server.AccessIPv6) @@ -360,7 +359,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err publicAddresses := publicAddressesRaw.([]interface{}) for _, paRaw := range publicAddresses { pa := paRaw.(map[string]interface{}) - if pa["version"].(float64) == 4 { + if pa["version"].(float64) == 6 { hostv6 = fmt.Sprintf("[%s]", pa["addr"].(string)) break } @@ -594,6 +593,7 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e resizeOpts := &servers.ResizeOpts{ FlavorRef: flavorId, } + log.Printf("[DEBUG] Resize configuration: %#v", resizeOpts) err = servers.Resize(computeClient, d.Id(), resizeOpts).ExtractErr() if err != nil { return fmt.Errorf("Error resizing OpenStack server: %s", err) diff --git a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go index 9c1417412..db6bed5b2 100644 --- a/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_keypair_v2.go @@ -2,6 +2,7 @@ package openstack import ( "fmt" + "log" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" @@ -46,6 +47,7 @@ func resourceComputeKeypairV2Create(d *schema.ResourceData, meta interface{}) er PublicKey: d.Get("public_key").(string), } + log.Printf("[DEBUG] Create Options: %#v", createOpts) kp, err := keypairs.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack keypair: %s", err) @@ -68,7 +70,6 @@ func resourceComputeKeypairV2Read(d *schema.ResourceData, meta interface{}) erro return CheckDeleted(d, err, "keypair") } - d.Set("region", d.Get("region").(string)) d.Set("name", kp.Name) d.Set("public_key", kp.PublicKey) diff --git a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go index 3860dc69c..ca646d77d 100644 --- a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go @@ -94,6 +94,7 @@ func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) e Description: d.Get("description").(string), } + log.Printf("[DEBUG] Create Options: %#v", createOpts) sg, err := secgroups.Create(computeClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack security group: %s", err) @@ -124,7 +125,6 @@ func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) err return CheckDeleted(d, err, "security group") } - d.Set("region", d.Get("region").(string)) d.Set("name", sg.Name) d.Set("description", sg.Description) rtm := rulesToMap(sg.Rules) diff --git a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go index 16b2014a7..8505ac3b3 100644 --- a/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go @@ -109,35 +109,11 @@ func resourceFWFirewallV1Read(d *schema.ResourceData, meta interface{}) error { return CheckDeleted(d, err, "LB pool") } - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", firewall.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("description"); exists && t != "" { - d.Set("description", firewall.Description) - } else { - d.Set("description", "") - } - - if t, exists := d.GetOk("policy_id"); exists && t != "" { - d.Set("policy_id", firewall.PolicyID) - } else { - d.Set("policy_id", "") - } - - if t, exists := d.GetOk("admin_state_up"); exists && t != "" { - d.Set("admin_state_up", firewall.AdminStateUp) - } else { - d.Set("admin_state_up", "") - } - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", firewall.TenantID) - } else { - d.Set("tenant_id", "") - } + d.Set("name", firewall.Name) + d.Set("description", firewall.Description) + d.Set("policy_id", firewall.PolicyID) + d.Set("admin_state_up", firewall.AdminStateUp) + d.Set("tenant_id", firewall.TenantID) return nil } diff --git a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go index e58b994fc..0560bfcef 100644 --- a/builtin/providers/openstack/resource_openstack_fw_policy_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_policy_v1.go @@ -97,7 +97,7 @@ func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error { return err } - log.Printf("[DEBUG] Firewall policy craeted: %#v", policy) + log.Printf("[DEBUG] Firewall policy created: %#v", policy) d.SetId(policy.ID) @@ -119,36 +119,11 @@ func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error { return CheckDeleted(d, err, "LB pool") } - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", policy.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("description"); exists && t != "" { - d.Set("description", policy.Description) - } else { - d.Set("description", "") - } - - if t, exists := d.GetOk("shared"); exists && t != "" { - d.Set("shared", policy.Shared) - } else { - d.Set("shared", "") - } - - if t, exists := d.GetOk("audited"); exists && t != "" { - d.Set("audited", policy.Audited) - } else { - d.Set("audited", "") - } - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", policy.TenantID) - } else { - d.Set("tenant_id", "") - } - + d.Set("name", policy.Name) + d.Set("description", policy.Description) + d.Set("shared", policy.Shared) + d.Set("audited", policy.Audited) + d.Set("tenant_id", policy.TenantID) return nil } diff --git a/builtin/providers/openstack/resource_openstack_fw_rule_v1.go b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go index f4dbb2fc7..f0f5affcc 100644 --- a/builtin/providers/openstack/resource_openstack_fw_rule_v1.go +++ b/builtin/providers/openstack/resource_openstack_fw_rule_v1.go @@ -131,53 +131,14 @@ func resourceFWRuleV1Read(d *schema.ResourceData, meta interface{}) error { d.Set("protocol", rule.Protocol) d.Set("action", rule.Action) - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", rule.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("description"); exists && t != "" { - d.Set("description", rule.Description) - } else { - d.Set("description", "") - } - - if t, exists := d.GetOk("ip_version"); exists && t != "" { - d.Set("ip_version", rule.IPVersion) - } else { - d.Set("ip_version", "") - } - - if t, exists := d.GetOk("source_ip_address"); exists && t != "" { - d.Set("source_ip_address", rule.SourceIPAddress) - } else { - d.Set("source_ip_address", "") - } - - if t, exists := d.GetOk("destination_ip_address"); exists && t != "" { - d.Set("destination_ip_address", rule.DestinationIPAddress) - } else { - d.Set("destination_ip_address", "") - } - - if t, exists := d.GetOk("source_port"); exists && t != "" { - d.Set("source_port", rule.SourcePort) - } else { - d.Set("source_port", "") - } - - if t, exists := d.GetOk("destination_port"); exists && t != "" { - d.Set("destination_port", rule.DestinationPort) - } else { - d.Set("destination_port", "") - } - - if t, exists := d.GetOk("enabled"); exists && t != "" { - d.Set("enabled", rule.Enabled) - } else { - d.Set("enabled", "") - } + d.Set("name", rule.Name) + d.Set("description", rule.Description) + d.Set("ip_version", rule.IPVersion) + d.Set("source_ip_address", rule.SourceIPAddress) + d.Set("destination_ip_address", rule.DestinationIPAddress) + d.Set("source_port", rule.SourcePort) + d.Set("destination_port", rule.DestinationPort) + d.Set("enabled", rule.Enabled) return nil } diff --git a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go index 0761ec1f4..cdfd54ccc 100644 --- a/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_monitor_v1.go @@ -99,7 +99,7 @@ func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error { createOpts.AdminStateUp = &asu } - log.Printf("[INFO] Requesting lb monitor creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) m, err := monitors.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB Monitor: %s", err) @@ -125,41 +125,15 @@ func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m) - d.Set("region", d.Get("region").(string)) d.Set("type", m.Type) d.Set("delay", m.Delay) d.Set("timeout", m.Timeout) d.Set("max_retries", m.MaxRetries) - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", m.TenantID) - } else { - d.Set("tenant_id", "") - } - - if t, exists := d.GetOk("url_path"); exists && t != "" { - d.Set("url_path", m.URLPath) - } else { - d.Set("url_path", "") - } - - if t, exists := d.GetOk("http_method"); exists && t != "" { - d.Set("http_method", m.HTTPMethod) - } else { - d.Set("http_method", "") - } - - if t, exists := d.GetOk("expected_codes"); exists && t != "" { - d.Set("expected_codes", m.ExpectedCodes) - } else { - d.Set("expected_codes", "") - } - - if t, exists := d.GetOk("admin_state_up"); exists && t != "" { - d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp)) - } else { - d.Set("admin_state_up", "") - } + d.Set("tenant_id", m.TenantID) + d.Set("url_path", m.URLPath) + d.Set("http_method", m.HTTPMethod) + d.Set("expected_codes", m.ExpectedCodes) + d.Set("admin_state_up", strconv.FormatBool(m.AdminStateUp)) return nil } diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index 7e3abdbf7..6b69f2fac 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -115,7 +115,7 @@ func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { TenantID: d.Get("tenant_id").(string), } - log.Printf("[INFO] Requesting lb pool creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) p, err := pools.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB pool: %s", err) @@ -159,18 +159,11 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p) - d.Set("region", d.Get("region").(string)) d.Set("name", p.Name) d.Set("protocol", p.Protocol) d.Set("subnet_id", p.SubnetID) d.Set("lb_method", p.LBMethod) - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", p.TenantID) - } else { - d.Set("tenant_id", "") - } - + d.Set("tenant_id", p.TenantID) d.Set("monitor_ids", p.MonitorIDs) d.Set("member_ids", p.MemberIDs) diff --git a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go index 6181bc8a5..bd2ae135e 100644 --- a/builtin/providers/openstack/resource_openstack_lb_vip_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_vip_v1.go @@ -112,7 +112,7 @@ func resourceLBVipV1Create(d *schema.ResourceData, meta interface{}) error { createOpts.AdminStateUp = &asu } - log.Printf("[INFO] Requesting lb vip creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) p, err := vips.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack LB VIP: %s", err) @@ -138,7 +138,6 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p) - d.Set("region", d.Get("region").(string)) d.Set("name", p.Name) d.Set("subnet_id", p.SubnetID) d.Set("protocol", p.Protocol) diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go index fb4965768..cbc430764 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go @@ -2,6 +2,7 @@ package openstack import ( "fmt" + "log" "github.com/hashicorp/terraform/helper/schema" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" @@ -49,9 +50,11 @@ func resourceNetworkFloatingIPV2Create(d *schema.ResourceData, meta interface{}) if len(poolID) == 0 { return fmt.Errorf("No network found with name: %s", d.Get("pool").(string)) } - floatingIP, err := floatingips.Create(networkClient, floatingips.CreateOpts{ + createOpts := floatingips.CreateOpts{ FloatingNetworkID: poolID, - }).Extract() + } + log.Printf("[DEBUG] Create Options: %#v", createOpts) + floatingIP, err := floatingips.Create(networkClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error allocating floating IP: %s", err) } @@ -73,7 +76,6 @@ func resourceNetworkFloatingIPV2Read(d *schema.ResourceData, meta interface{}) e return CheckDeleted(d, err, "floating IP") } - d.Set("region", d.Get("region").(string)) d.Set("address", floatingIP.FloatingIP) poolName, err := getNetworkName(d, meta, floatingIP.FloatingNetworkID) if err != nil { diff --git a/builtin/providers/openstack/resource_openstack_networking_network_v2.go b/builtin/providers/openstack/resource_openstack_networking_network_v2.go index bf556f418..2ac4ab94e 100644 --- a/builtin/providers/openstack/resource_openstack_networking_network_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_network_v2.go @@ -77,7 +77,7 @@ func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) createOpts.Shared = &shared } - log.Printf("[INFO] Requesting network creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) n, err := networks.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron network: %s", err) @@ -103,31 +103,10 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n) - d.Set("region", d.Get("region").(string)) - - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", n.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("admin_state_up"); exists && t != "" { - d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) - } else { - d.Set("admin_state_up", "") - } - - if t, exists := d.GetOk("shared"); exists && t != "" { - d.Set("shared", strconv.FormatBool(n.Shared)) - } else { - d.Set("shared", "") - } - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", n.TenantID) - } else { - d.Set("tenant_id", "") - } + d.Set("name", n.Name) + d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) + d.Set("shared", strconv.FormatBool(n.Shared)) + d.Set("tenant_id", n.TenantID) return nil } diff --git a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go index 492c14d30..e67ff6f58 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_interface_v2.go @@ -48,7 +48,7 @@ func resourceNetworkingRouterInterfaceV2Create(d *schema.ResourceData, meta inte SubnetID: d.Get("subnet_id").(string), } - log.Printf("[INFO] Requesting router interface creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) n, err := routers.AddInterface(networkingClient, d.Get("router_id").(string), createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron router interface: %s", err) @@ -83,8 +83,6 @@ func resourceNetworkingRouterInterfaceV2Read(d *schema.ResourceData, meta interf log.Printf("[DEBUG] Retreived Router Interface %s: %+v", d.Id(), n) - d.Set("region", d.Get("region").(string)) - return nil } diff --git a/builtin/providers/openstack/resource_openstack_networking_router_v2.go b/builtin/providers/openstack/resource_openstack_networking_router_v2.go index cb235a88b..3b6df4816 100644 --- a/builtin/providers/openstack/resource_openstack_networking_router_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_router_v2.go @@ -77,7 +77,7 @@ func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) createOpts.GatewayInfo = &gatewayInfo } - log.Printf("[INFO] Requesting router creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) n, err := routers.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron router: %s", err) @@ -112,31 +112,10 @@ func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Retreived Router %s: %+v", d.Id(), n) - d.Set("region", d.Get("region").(string)) - - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", n.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("admin_state_up"); exists && t != "" { - d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) - } else { - d.Set("admin_state_up", "") - } - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", n.TenantID) - } else { - d.Set("tenant_id", "") - } - - if t, exists := d.GetOk("external_gateway"); exists && t != "" { - d.Set("external_gateway", n.GatewayInfo.NetworkID) - } else { - d.Set("external_gateway", "") - } + d.Set("name", n.Name) + d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp)) + d.Set("tenant_id", n.TenantID) + d.Set("external_gateway", n.GatewayInfo.NetworkID) return nil } diff --git a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go index 8462611df..2f898bb4c 100644 --- a/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go @@ -134,7 +134,7 @@ func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) createOpts.EnableDHCP = &ed } - log.Printf("[INFO] Requesting subnet creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) s, err := subnets.Create(networkingClient, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err) @@ -160,46 +160,16 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Retreived Subnet %s: %+v", d.Id(), s) - d.Set("region", d.Get("region").(string)) d.Set("newtork_id", s.NetworkID) d.Set("cidr", s.CIDR) d.Set("ip_version", s.IPVersion) - - if t, exists := d.GetOk("name"); exists && t != "" { - d.Set("name", s.Name) - } else { - d.Set("name", "") - } - - if t, exists := d.GetOk("tenant_id"); exists && t != "" { - d.Set("tenant_id", s.TenantID) - } else { - d.Set("tenant_id", "") - } - - if _, exists := d.GetOk("allocation_pools"); exists { - d.Set("allocation_pools", s.AllocationPools) - } - - if t, exists := d.GetOk("gateway_ip"); exists && t != "" { - d.Set("gateway_ip", s.GatewayIP) - } else { - d.Set("gateway_ip", "") - } - - if t, exists := d.GetOk("enable_dhcp"); exists && t != "" { - d.Set("enable_dhcp", strconv.FormatBool(s.EnableDHCP)) - } else { - d.Set("enable_dhcp", "") - } - - if _, exists := d.GetOk("dns_nameservers"); exists { - d.Set("dns_nameservers", s.DNSNameservers) - } - - if _, exists := d.GetOk("host_routes"); exists { - d.Set("host_routes", s.HostRoutes) - } + d.Set("name", s.Name) + d.Set("tenant_id", s.TenantID) + d.Set("allocation_pools", s.AllocationPools) + d.Set("gateway_ip", s.GatewayIP) + d.Set("enable_dhcp", strconv.FormatBool(s.EnableDHCP)) + d.Set("dns_nameservers", s.DNSNameservers) + d.Set("host_routes", s.HostRoutes) return nil } diff --git a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go index 692ed2e46..31666a356 100644 --- a/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go +++ b/builtin/providers/openstack/resource_openstack_objectstorage_container_v1.go @@ -79,7 +79,7 @@ func resourceObjectStorageContainerV1Create(d *schema.ResourceData, meta interfa Metadata: resourceContainerMetadataV2(d), } - log.Printf("[INFO] Requesting container creation") + log.Printf("[DEBUG] Create Options: %#v", createOpts) _, err = containers.Create(objectStorageClient, cn, createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack container: %s", err) From 0092946f748f77770e7807d16f6c3e48ae6b14d1 Mon Sep 17 00:00:00 2001 From: Chris Buben Date: Fri, 20 Mar 2015 18:55:42 -0400 Subject: [PATCH 147/167] user_data support Mostly stolen from: https://github.com/jtopjian/terraform-provider-openstack/blob/master/openstack/resource_openstack_instance.go --- .../resource_openstack_compute_instance_v2.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 63508ffa7..5a4c16c7e 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -2,6 +2,8 @@ package openstack import ( "bytes" + "crypto/sha1" + "encoding/hex" "fmt" "log" "time" @@ -75,6 +77,21 @@ func resourceComputeInstanceV2() *schema.Resource { Optional: true, ForceNew: false, }, + "user_data": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + // just stash the hash for state & diff comparisons + StateFunc: func(v interface{}) string { + switch v.(type) { + case string: + hash := sha1.Sum([]byte(v.(string))) + return hex.EncodeToString(hash[:]) + default: + return "" + } + }, + }, "security_groups": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -224,6 +241,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e Metadata: resourceInstanceMetadataV2(d), ConfigDrive: d.Get("config_drive").(bool), AdminPass: d.Get("admin_pass").(string), + UserData: []byte(d.Get("user_data").(string)), } if keyName, ok := d.Get("key_pair").(string); ok && keyName != "" { From 97acccd3eda70f7552cecd71ea8f5916b215b694 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 24 Mar 2015 11:18:15 -0500 Subject: [PATCH 148/167] core: targeted operations Add `-target=resource` flag to core operations, allowing users to target specific resources in their infrastructure. When `-target` is used, the operation will only apply to that resource and its dependencies. The calculated dependencies are different depending on whether we're running a normal operation or a `terraform destroy`. Generally, "dependencies" refers to ancestors: resources falling _before_ the target in the graph, because their changes are required to accurately act on the target. For destroys, "dependencies" are descendents: those resources which fall _after_ the target. These resources depend on our target, which is going to be destroyed, so they should also be destroyed. --- command/apply.go | 16 +- command/apply_destroy_test.go | 90 ++++++ command/flag_kv.go | 14 + command/meta.go | 11 + command/plan.go | 7 +- command/refresh.go | 4 + .../apply-destroy-targeted/main.tf | 7 + dag/dag.go | 93 +++++- dag/dag_test.go | 62 ++++ helper/resource/testing.go | 3 +- terraform/context.go | 12 +- terraform/context_test.go | 266 ++++++++++-------- terraform/graph_builder.go | 11 + terraform/plan.go | 9 - .../transform-targets-basic/main.tf | 16 ++ .../transform-targets-destroy/main.tf | 18 ++ terraform/transform_targets.go | 77 +++++ terraform/transform_targets_test.go | 71 +++++ 18 files changed, 637 insertions(+), 150 deletions(-) create mode 100644 command/test-fixtures/apply-destroy-targeted/main.tf create mode 100644 terraform/test-fixtures/transform-targets-basic/main.tf create mode 100644 terraform/test-fixtures/transform-targets-destroy/main.tf create mode 100644 terraform/transform_targets.go create mode 100644 terraform/transform_targets_test.go diff --git a/command/apply.go b/command/apply.go index d46b71679..529d6e701 100644 --- a/command/apply.go +++ b/command/apply.go @@ -93,6 +93,7 @@ func (c *ApplyCommand) Run(args []string) int { // Build the context based on the arguments given ctx, planned, err := c.Context(contextOpts{ + Destroy: c.Destroy, Path: configPath, StatePath: c.Meta.statePath, }) @@ -140,12 +141,7 @@ func (c *ApplyCommand) Run(args []string) int { } } - var opts terraform.PlanOpts - if c.Destroy { - opts.Destroy = true - } - - if _, err := ctx.Plan(&opts); err != nil { + if _, err := ctx.Plan(); err != nil { c.Ui.Error(fmt.Sprintf( "Error creating plan: %s", err)) return 1 @@ -319,6 +315,10 @@ Options: "-state". This can be used to preserve the old state. + -target=resource Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used + multiple times. + -var 'foo=bar' Set a variable in the Terraform configuration. This flag can be set multiple times. @@ -357,6 +357,10 @@ Options: "-state". This can be used to preserve the old state. + -target=resource Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used + multiple times. + -var 'foo=bar' Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/command/apply_destroy_test.go b/command/apply_destroy_test.go index bdc2440f0..63afb15ed 100644 --- a/command/apply_destroy_test.go +++ b/command/apply_destroy_test.go @@ -116,6 +116,96 @@ func TestApply_destroyPlan(t *testing.T) { } } +func TestApply_destroyTargeted(t *testing.T) { + originalState := &terraform.State{ + Modules: []*terraform.ModuleState{ + &terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_instance.foo": &terraform.ResourceState{ + Type: "test_instance", + Primary: &terraform.InstanceState{ + ID: "i-ab123", + }, + }, + "test_load_balancer.foo": &terraform.ResourceState{ + Type: "test_load_balancer", + Primary: &terraform.InstanceState{ + ID: "lb-abc123", + }, + }, + }, + }, + }, + } + + statePath := testStateFile(t, originalState) + + p := testProvider() + ui := new(cli.MockUi) + c := &ApplyCommand{ + Destroy: true, + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + // Run the apply command pointing to our existing state + args := []string{ + "-force", + "-target", "test_instance.foo", + "-state", statePath, + testFixturePath("apply-destroy-targeted"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + // Verify a new state exists + if _, err := os.Stat(statePath); err != nil { + t.Fatalf("err: %s", err) + } + + f, err := os.Open(statePath) + if err != nil { + t.Fatalf("err: %s", err) + } + defer f.Close() + + state, err := terraform.ReadState(f) + if err != nil { + t.Fatalf("err: %s", err) + } + if state == nil { + t.Fatal("state should not be nil") + } + + actualStr := strings.TrimSpace(state.String()) + expectedStr := strings.TrimSpace(testApplyDestroyStr) + if actualStr != expectedStr { + t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) + } + + // Should have a backup file + f, err = os.Open(statePath + DefaultBackupExtention) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupState, err := terraform.ReadState(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + actualStr = strings.TrimSpace(backupState.String()) + expectedStr = strings.TrimSpace(originalState.String()) + if actualStr != expectedStr { + t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr) + } +} + const testApplyDestroyStr = ` ` diff --git a/command/flag_kv.go b/command/flag_kv.go index fd9b57b3a..6e0198778 100644 --- a/command/flag_kv.go +++ b/command/flag_kv.go @@ -85,3 +85,17 @@ func loadKVFile(rawPath string) (map[string]string, error) { return result, nil } + +// FlagStringSlice is a flag.Value implementation for parsing targets from the +// command line, e.g. -target=aws_instance.foo -target=aws_vpc.bar + +type FlagStringSlice []string + +func (v *FlagStringSlice) String() string { + return "" +} +func (v *FlagStringSlice) Set(raw string) error { + *v = append(*v, raw) + + return nil +} diff --git a/command/meta.go b/command/meta.go index 28a01c542..b542304af 100644 --- a/command/meta.go +++ b/command/meta.go @@ -38,6 +38,9 @@ type Meta struct { input bool variables map[string]string + // Targets for this context (private) + targets []string + color bool oldUi cli.Ui @@ -126,6 +129,9 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) { m.statePath = copts.StatePath } + // Tell the context if we're in a destroy plan / apply + opts.Destroy = copts.Destroy + // Store the loaded state state, err := m.State() if err != nil { @@ -267,6 +273,7 @@ func (m *Meta) contextOpts() *terraform.ContextOpts { vs[k] = v } opts.Variables = vs + opts.Targets = m.targets opts.UIInput = m.UIInput() return &opts @@ -278,6 +285,7 @@ func (m *Meta) flagSet(n string) *flag.FlagSet { f.BoolVar(&m.input, "input", true, "input") f.Var((*FlagKV)(&m.variables), "var", "variables") f.Var((*FlagKVFile)(&m.variables), "var-file", "variable file") + f.Var((*FlagStringSlice)(&m.targets), "target", "resource to target") if m.autoKey != "" { f.Var((*FlagKVFile)(&m.autoVariables), m.autoKey, "variable file") @@ -388,4 +396,7 @@ type contextOpts struct { // GetMode is the module.GetMode to use when loading the module tree. GetMode module.GetMode + + // Set to true when running a destroy plan/apply. + Destroy bool } diff --git a/command/plan.go b/command/plan.go index 24365d185..5c884d632 100644 --- a/command/plan.go +++ b/command/plan.go @@ -53,6 +53,7 @@ func (c *PlanCommand) Run(args []string) int { } ctx, _, err := c.Context(contextOpts{ + Destroy: destroy, Path: path, StatePath: c.Meta.statePath, }) @@ -86,7 +87,7 @@ func (c *PlanCommand) Run(args []string) int { } } - plan, err := ctx.Plan(&terraform.PlanOpts{Destroy: destroy}) + plan, err := ctx.Plan() if err != nil { c.Ui.Error(fmt.Sprintf("Error running plan: %s", err)) return 1 @@ -168,6 +169,10 @@ Options: up Terraform-managed resources. By default it will use the state "terraform.tfstate" if it exists. + -target=resource Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used + multiple times. + -var 'foo=bar' Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/command/refresh.go b/command/refresh.go index 38d630050..32e795047 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -135,6 +135,10 @@ Options: -state-out=path Path to write updated state file. By default, the "-state" path will be used. + -target=resource Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used + multiple times. + -var 'foo=bar' Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/command/test-fixtures/apply-destroy-targeted/main.tf b/command/test-fixtures/apply-destroy-targeted/main.tf new file mode 100644 index 000000000..45ebc5b97 --- /dev/null +++ b/command/test-fixtures/apply-destroy-targeted/main.tf @@ -0,0 +1,7 @@ +resource "test_instance" "foo" { + count = 3 +} + +resource "test_load_balancer" "foo" { + instances = ["${test_instance.foo.*.id}"] +} diff --git a/dag/dag.go b/dag/dag.go index b81cb2874..0f53fb1f0 100644 --- a/dag/dag.go +++ b/dag/dag.go @@ -17,6 +17,40 @@ type AcyclicGraph struct { // WalkFunc is the callback used for walking the graph. type WalkFunc func(Vertex) error +// Returns a Set that includes every Vertex yielded by walking down from the +// provided starting Vertex v. +func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) { + s := new(Set) + start := asVertexList(g.DownEdges(v)) + memoFunc := func(v Vertex) error { + s.Add(v) + return nil + } + + if err := g.depthFirstWalk(start, memoFunc); err != nil { + return nil, err + } + + return s, nil +} + +// Returns a Set that includes every Vertex yielded by walking up from the +// provided starting Vertex v. +func (g *AcyclicGraph) Descendents(v Vertex) (*Set, error) { + s := new(Set) + start := asVertexList(g.UpEdges(v)) + memoFunc := func(v Vertex) error { + s.Add(v) + return nil + } + + if err := g.reverseDepthFirstWalk(start, memoFunc); err != nil { + return nil, err + } + + return s, nil +} + // Root returns the root of the DAG, or an error. // // Complexity: O(V) @@ -61,15 +95,11 @@ func (g *AcyclicGraph) TransitiveReduction() { for _, u := range g.Vertices() { uTargets := g.DownEdges(u) - vs := make([]Vertex, uTargets.Len()) - for i, vRaw := range uTargets.List() { - vs[i] = vRaw.(Vertex) - } + vs := asVertexList(g.DownEdges(u)) g.depthFirstWalk(vs, func(v Vertex) error { shared := uTargets.Intersection(g.DownEdges(v)) - for _, raw := range shared.List() { - vPrime := raw.(Vertex) + for _, vPrime := range asVertexList(shared) { g.RemoveEdge(BasicEdge(u, vPrime)) } @@ -145,12 +175,10 @@ func (g *AcyclicGraph) Walk(cb WalkFunc) error { for _, v := range vertices { // Build our list of dependencies and the list of channels to // wait on until we start executing for this vertex. - depsRaw := g.DownEdges(v).List() - deps := make([]Vertex, len(depsRaw)) + deps := asVertexList(g.DownEdges(v)) depChs := make([]<-chan struct{}, len(deps)) - for i, raw := range depsRaw { - deps[i] = raw.(Vertex) - depChs[i] = vertMap[deps[i]] + for i, dep := range deps { + depChs[i] = vertMap[dep] } // Get our channel so that we can close it when we're done @@ -200,6 +228,16 @@ func (g *AcyclicGraph) Walk(cb WalkFunc) error { return errs } +// simple convenience helper for converting a dag.Set to a []Vertex +func asVertexList(s *Set) []Vertex { + rawList := s.List() + vertexList := make([]Vertex, len(rawList)) + for i, raw := range rawList { + vertexList[i] = raw.(Vertex) + } + return vertexList +} + // depthFirstWalk does a depth-first walk of the graph starting from // the vertices in start. This is not exported now but it would make sense // to export this publicly at some point. @@ -233,3 +271,36 @@ func (g *AcyclicGraph) depthFirstWalk(start []Vertex, cb WalkFunc) error { return nil } + +// reverseDepthFirstWalk does a depth-first walk _up_ the graph starting from +// the vertices in start. +func (g *AcyclicGraph) reverseDepthFirstWalk(start []Vertex, cb WalkFunc) error { + seen := make(map[Vertex]struct{}) + frontier := make([]Vertex, len(start)) + copy(frontier, start) + for len(frontier) > 0 { + // Pop the current vertex + n := len(frontier) + current := frontier[n-1] + frontier = frontier[:n-1] + + // Check if we've seen this already and return... + if _, ok := seen[current]; ok { + continue + } + seen[current] = struct{}{} + + // Visit the current node + if err := cb(current); err != nil { + return err + } + + // Visit targets of this in reverse order. + targets := g.UpEdges(current).List() + for i := len(targets) - 1; i >= 0; i-- { + frontier = append(frontier, targets[i].(Vertex)) + } + } + + return nil +} diff --git a/dag/dag_test.go b/dag/dag_test.go index feead7968..e7b2db8d2 100644 --- a/dag/dag_test.go +++ b/dag/dag_test.go @@ -126,6 +126,68 @@ func TestAcyclicGraphValidate_cycleSelf(t *testing.T) { } } +func TestAcyclicGraphAncestors(t *testing.T) { + var g AcyclicGraph + g.Add(1) + g.Add(2) + g.Add(3) + g.Add(4) + g.Add(5) + g.Connect(BasicEdge(0, 1)) + g.Connect(BasicEdge(1, 2)) + g.Connect(BasicEdge(2, 3)) + g.Connect(BasicEdge(3, 4)) + g.Connect(BasicEdge(4, 5)) + + actual, err := g.Ancestors(2) + if err != nil { + t.Fatalf("err: %#v", err) + } + + expected := []Vertex{3, 4, 5} + + if actual.Len() != len(expected) { + t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected)) + } + + for _, e := range expected { + if !actual.Include(e) { + t.Fatalf("expected: %#v to include: %#v", expected, actual) + } + } +} + +func TestAcyclicGraphDescendents(t *testing.T) { + var g AcyclicGraph + g.Add(1) + g.Add(2) + g.Add(3) + g.Add(4) + g.Add(5) + g.Connect(BasicEdge(0, 1)) + g.Connect(BasicEdge(1, 2)) + g.Connect(BasicEdge(2, 3)) + g.Connect(BasicEdge(3, 4)) + g.Connect(BasicEdge(4, 5)) + + actual, err := g.Descendents(2) + if err != nil { + t.Fatalf("err: %#v", err) + } + + expected := []Vertex{0, 1} + + if actual.Len() != len(expected) { + t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected)) + } + + for _, e := range expected { + if !actual.Include(e) { + t.Fatalf("expected: %#v to include: %#v", expected, actual) + } + } +} + func TestAcyclicGraphWalk(t *testing.T) { var g AcyclicGraph g.Add(1) diff --git a/helper/resource/testing.go b/helper/resource/testing.go index cedadfc72..90cfc175f 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -190,6 +190,7 @@ func testStep( // Build the context opts.Module = mod opts.State = state + opts.Destroy = step.Destroy ctx := terraform.NewContext(&opts) if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { estrs := make([]string, len(es)) @@ -209,7 +210,7 @@ func testStep( } // Plan! - if p, err := ctx.Plan(&terraform.PlanOpts{Destroy: step.Destroy}); err != nil { + if p, err := ctx.Plan(); err != nil { return state, fmt.Errorf( "Error planning: %s", err) } else { diff --git a/terraform/context.go b/terraform/context.go index 86a804548..6beaab636 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -33,6 +33,7 @@ const ( // ContextOpts are the user-configurable options to create a context with // NewContext. type ContextOpts struct { + Destroy bool Diff *Diff Hooks []Hook Module *module.Tree @@ -40,6 +41,7 @@ type ContextOpts struct { State *State Providers map[string]ResourceProviderFactory Provisioners map[string]ResourceProvisionerFactory + Targets []string Variables map[string]string UIInput UIInput @@ -49,6 +51,7 @@ type ContextOpts struct { // perform operations on infrastructure. This structure is built using // NewContext. See the documentation for that. type Context struct { + destroy bool diff *Diff diffLock sync.RWMutex hooks []Hook @@ -58,6 +61,7 @@ type Context struct { sh *stopHook state *State stateLock sync.RWMutex + targets []string uiInput UIInput variables map[string]string @@ -95,12 +99,14 @@ func NewContext(opts *ContextOpts) *Context { } return &Context{ + destroy: opts.Destroy, diff: opts.Diff, hooks: hooks, module: opts.Module, providers: opts.Providers, provisioners: opts.Provisioners, state: state, + targets: opts.Targets, uiInput: opts.UIInput, variables: opts.Variables, @@ -135,6 +141,8 @@ func (c *Context) GraphBuilder() GraphBuilder { Providers: providers, Provisioners: provisioners, State: c.state, + Targets: c.targets, + Destroy: c.destroy, } } @@ -253,7 +261,7 @@ func (c *Context) Apply() (*State, error) { // // Plan also updates the diff of this context to be the diff generated // by the plan, so Apply can be called after. -func (c *Context) Plan(opts *PlanOpts) (*Plan, error) { +func (c *Context) Plan() (*Plan, error) { v := c.acquireRun() defer c.releaseRun(v) @@ -264,7 +272,7 @@ func (c *Context) Plan(opts *PlanOpts) (*Plan, error) { } var operation walkOperation - if opts != nil && opts.Destroy { + if c.destroy { operation = walkPlanDestroy } else { // Set our state to be something temporary. We do this so that diff --git a/terraform/context_test.go b/terraform/context_test.go index abffbf5c3..238f04d04 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -24,7 +24,7 @@ func TestContext2Plan(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -57,7 +57,7 @@ func TestContext2Plan_emptyDiff(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -80,7 +80,7 @@ func TestContext2Plan_minimal(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -103,7 +103,7 @@ func TestContext2Plan_modules(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -126,7 +126,7 @@ func TestContext2Plan_moduleInput(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -149,7 +149,7 @@ func TestContext2Plan_moduleInputComputed(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -175,7 +175,7 @@ func TestContext2Plan_moduleInputFromVar(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -198,7 +198,7 @@ func TestContext2Plan_moduleMultiVar(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -237,7 +237,7 @@ func TestContext2Plan_moduleOrphans(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -282,7 +282,7 @@ func TestContext2Plan_moduleProviderInherit(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -332,7 +332,7 @@ func TestContext2Plan_moduleProviderDefaults(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -385,7 +385,7 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -410,7 +410,7 @@ func TestContext2Plan_moduleVar(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -433,7 +433,7 @@ func TestContext2Plan_moduleVarComputed(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -471,7 +471,7 @@ func TestContext2Plan_nil(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -491,7 +491,7 @@ func TestContext2Plan_computed(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -514,7 +514,7 @@ func TestContext2Plan_computedList(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -537,7 +537,7 @@ func TestContext2Plan_count(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -564,7 +564,7 @@ func TestContext2Plan_countComputed(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err == nil { t.Fatal("should error") } @@ -581,7 +581,7 @@ func TestContext2Plan_countIndex(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -604,7 +604,7 @@ func TestContext2Plan_countIndexZero(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -630,7 +630,7 @@ func TestContext2Plan_countVar(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -653,7 +653,7 @@ func TestContext2Plan_countZero(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -676,7 +676,7 @@ func TestContext2Plan_countOneIndex(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -731,7 +731,7 @@ func TestContext2Plan_countDecreaseToOne(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -774,7 +774,7 @@ func TestContext2Plan_countIncreaseFromNotSet(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -817,7 +817,7 @@ func TestContext2Plan_countIncreaseFromOne(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -875,7 +875,7 @@ func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -917,10 +917,11 @@ func TestContext2Plan_destroy(t *testing.T) { Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, - State: s, + State: s, + Destroy: true, }) - plan, err := ctx.Plan(&PlanOpts{Destroy: true}) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -971,10 +972,11 @@ func TestContext2Plan_moduleDestroy(t *testing.T) { Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, - State: s, + State: s, + Destroy: true, }) - plan, err := ctx.Plan(&PlanOpts{Destroy: true}) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1020,10 +1022,11 @@ func TestContext2Plan_moduleDestroyMultivar(t *testing.T) { Providers: map[string]ResourceProviderFactory{ "aws": testProviderFuncFixed(p), }, - State: s, + State: s, + Destroy: true, }) - plan, err := ctx.Plan(&PlanOpts{Destroy: true}) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1051,7 +1054,7 @@ func TestContext2Plan_pathVar(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1118,7 +1121,7 @@ func TestContext2Plan_diffVar(t *testing.T) { }, nil } - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1143,7 +1146,7 @@ func TestContext2Plan_hook(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1183,7 +1186,7 @@ func TestContext2Plan_orphan(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1221,7 +1224,7 @@ func TestContext2Plan_state(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1273,7 +1276,7 @@ func TestContext2Plan_taint(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1324,7 +1327,7 @@ func TestContext2Plan_multiple_taint(t *testing.T) { State: s, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1357,7 +1360,7 @@ func TestContext2Plan_provider(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -1377,7 +1380,7 @@ func TestContext2Plan_varMultiCountOne(t *testing.T) { }, }) - plan, err := ctx.Plan(nil) + plan, err := ctx.Plan() if err != nil { t.Fatalf("err: %s", err) } @@ -1399,7 +1402,7 @@ func TestContext2Plan_varListErr(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err == nil { t.Fatal("should error") } @@ -2468,7 +2471,7 @@ func TestContext2Input(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2513,7 +2516,7 @@ func TestContext2Input_provider(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2590,7 +2593,7 @@ func TestContext2Input_providerId(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2638,7 +2641,7 @@ func TestContext2Input_providerOnly(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2693,7 +2696,7 @@ func TestContext2Input_providerVars(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2741,7 +2744,7 @@ func TestContext2Input_varOnly(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2787,7 +2790,7 @@ func TestContext2Input_varOnlyUnset(t *testing.T) { t.Fatalf("err: %s", err) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2815,7 +2818,7 @@ func TestContext2Apply(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2848,7 +2851,7 @@ func TestContext2Apply_emptyModule(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -2896,7 +2899,7 @@ func TestContext2Apply_createBeforeDestroy(t *testing.T) { State: state, }) - if p, err := ctx.Plan(nil); err != nil { + if p, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } else { t.Logf(p.String()) @@ -2950,7 +2953,7 @@ func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) { State: state, }) - if p, err := ctx.Plan(nil); err != nil { + if p, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } else { t.Logf(p.String()) @@ -2985,7 +2988,7 @@ func TestContext2Apply_minimal(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3013,7 +3016,7 @@ func TestContext2Apply_badDiff(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3071,7 +3074,7 @@ func TestContext2Apply_cancel(t *testing.T) { }, nil } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3112,7 +3115,7 @@ func TestContext2Apply_compute(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3181,7 +3184,7 @@ func TestContext2Apply_countDecrease(t *testing.T) { State: s, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3241,7 +3244,7 @@ func TestContext2Apply_countDecreaseToOne(t *testing.T) { State: s, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3303,7 +3306,7 @@ func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) { State: s, }) - if p, err := ctx.Plan(nil); err != nil { + if p, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } else { testStringMatch(t, p, testTerraformApplyCountDecToOneCorruptedPlanStr) @@ -3354,7 +3357,7 @@ func TestContext2Apply_countTainted(t *testing.T) { State: s, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3382,7 +3385,7 @@ func TestContext2Apply_countVariable(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3410,7 +3413,7 @@ func TestContext2Apply_module(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3439,9 +3442,10 @@ func TestContext2Apply_moduleVarResourceCount(t *testing.T) { Variables: map[string]string{ "count": "2", }, + Destroy: true, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3459,7 +3463,7 @@ func TestContext2Apply_moduleVarResourceCount(t *testing.T) { }, }) - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3481,7 +3485,7 @@ func TestContext2Apply_moduleBool(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3515,7 +3519,7 @@ func TestContext2Apply_multiProvider(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3548,7 +3552,7 @@ func TestContext2Apply_nilDiff(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3588,7 +3592,7 @@ func TestContext2Apply_Provisioner_compute(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3633,7 +3637,7 @@ func TestContext2Apply_provisionerCreateFail(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3672,7 +3676,7 @@ func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3712,7 +3716,7 @@ func TestContext2Apply_provisionerFail(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3767,7 +3771,7 @@ func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) { State: state, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3816,7 +3820,7 @@ func TestContext2Apply_error_createBeforeDestroy(t *testing.T) { } p.DiffFn = testDiffFn - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3874,7 +3878,7 @@ func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) { } p.DiffFn = testDiffFn - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3931,7 +3935,7 @@ func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) { } } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3955,7 +3959,7 @@ aws_instance.web: (1 deposed) State: state, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -3983,7 +3987,7 @@ aws_instance.web: (2 deposed) } createdInstanceId = "qux" - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } state, err = ctx.Apply() @@ -4005,7 +4009,7 @@ aws_instance.web: (1 deposed) } createdInstanceId = "quux" - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } state, err = ctx.Apply() @@ -4045,7 +4049,7 @@ func TestContext2Apply_provisionerResourceRef(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4091,7 +4095,7 @@ func TestContext2Apply_provisionerSelfRef(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4144,7 +4148,7 @@ func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4192,7 +4196,7 @@ func TestContext2Apply_Provisioner_Diff(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4229,7 +4233,7 @@ func TestContext2Apply_Provisioner_Diff(t *testing.T) { State: state, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4300,7 +4304,7 @@ func TestContext2Apply_outputDiffVars(t *testing.T) { }, nil } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } if _, err := ctx.Apply(); err != nil { @@ -4363,7 +4367,7 @@ func TestContext2Apply_Provisioner_ConnInfo(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4399,22 +4403,32 @@ func TestContext2Apply_destroy(t *testing.T) { }) // First plan and apply a create operation - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } - if _, err := ctx.Apply(); err != nil { + state, err := ctx.Apply() + if err != nil { t.Fatalf("err: %s", err) } // Next, plan and apply a destroy operation - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { + h.Active = true + ctx = testContext2(t, &ContextOpts{ + Destroy: true, + State: state, + Module: m, + Hooks: []Hook{h}, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } - h.Active = true - - state, err := ctx.Apply() + state, err = ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } @@ -4430,7 +4444,7 @@ func TestContext2Apply_destroy(t *testing.T) { expected2 := []string{"aws_instance.bar", "aws_instance.foo"} actual2 := h.IDs if !reflect.DeepEqual(actual2, expected2) { - t.Fatalf("bad: %#v", actual2) + t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2) } } @@ -4449,22 +4463,33 @@ func TestContext2Apply_destroyOutputs(t *testing.T) { }) // First plan and apply a create operation - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } - if _, err := ctx.Apply(); err != nil { + state, err := ctx.Apply() + + if err != nil { t.Fatalf("err: %s", err) } // Next, plan and apply a destroy operation - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { + h.Active = true + ctx = testContext2(t, &ContextOpts{ + Destroy: true, + State: state, + Module: m, + Hooks: []Hook{h}, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } - h.Active = true - - state, err := ctx.Apply() + state, err = ctx.Apply() if err != nil { t.Fatalf("err: %s", err) } @@ -4520,7 +4545,7 @@ func TestContext2Apply_destroyOrphan(t *testing.T) { }, nil } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4577,10 +4602,11 @@ func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) { Provisioners: map[string]ResourceProvisionerFactory{ "shell": testProvisionerFuncFixed(pr), }, - State: s, + State: s, + Destroy: true, }) - if _, err := ctx.Plan(&PlanOpts{Destroy: true}); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4638,7 +4664,7 @@ func TestContext2Apply_error(t *testing.T) { }, nil } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4705,7 +4731,7 @@ func TestContext2Apply_errorPartial(t *testing.T) { }, nil } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4740,7 +4766,7 @@ func TestContext2Apply_hook(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4788,7 +4814,7 @@ func TestContext2Apply_idAttr(t *testing.T) { }, nil } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4822,7 +4848,7 @@ func TestContext2Apply_output(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4850,7 +4876,7 @@ func TestContext2Apply_outputInvalid(t *testing.T) { }, }) - _, err := ctx.Plan(nil) + _, err := ctx.Plan() if err == nil { t.Fatalf("err: %s", err) } @@ -4871,7 +4897,7 @@ func TestContext2Apply_outputList(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4899,7 +4925,7 @@ func TestContext2Apply_outputMulti(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4927,7 +4953,7 @@ func TestContext2Apply_outputMultiIndex(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -4992,7 +5018,7 @@ func TestContext2Apply_taint(t *testing.T) { State: s, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -5057,7 +5083,7 @@ func TestContext2Apply_taintDep(t *testing.T) { State: s, }) - if p, err := ctx.Plan(nil); err != nil { + if p, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } else { t.Logf("plan: %s", p) @@ -5120,7 +5146,7 @@ func TestContext2Apply_taintDepRequiresNew(t *testing.T) { State: s, }) - if p, err := ctx.Plan(nil); err != nil { + if p, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } else { t.Logf("plan: %s", p) @@ -5150,7 +5176,7 @@ func TestContext2Apply_unknownAttribute(t *testing.T) { }, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -5190,7 +5216,7 @@ func TestContext2Apply_vars(t *testing.T) { t.Fatalf("bad: %s", e) } - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -5248,7 +5274,7 @@ func TestContext2Apply_createBefore_depends(t *testing.T) { State: state, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } @@ -5357,7 +5383,7 @@ func TestContext2Apply_singleDestroy(t *testing.T) { State: state, }) - if _, err := ctx.Plan(nil); err != nil { + if _, err := ctx.Plan(); err != nil { t.Fatalf("err: %s", err) } diff --git a/terraform/graph_builder.go b/terraform/graph_builder.go index 4d5726954..d42f6bfe0 100644 --- a/terraform/graph_builder.go +++ b/terraform/graph_builder.go @@ -65,6 +65,13 @@ type BuiltinGraphBuilder struct { // Provisioners is the list of provisioners supported. Provisioners []string + + // Targets is the user-specified list of resources to target. + Targets []string + + // Destroy is set to true when we're in a `terraform destroy` or a + // `terraform plan -destroy` + Destroy bool } // Build builds the graph according to the steps returned by Steps. @@ -104,6 +111,10 @@ func (b *BuiltinGraphBuilder) Steps() []GraphTransformer { }, }, + // Optionally reduces the graph to a user-specified list of targets and + // their dependencies. + &TargetsTransformer{Targets: b.Targets, Destroy: b.Destroy}, + // Create the destruction nodes &DestroyTransformer{}, &CreateBeforeDestroyTransformer{}, diff --git a/terraform/plan.go b/terraform/plan.go index e73fde383..715136edc 100644 --- a/terraform/plan.go +++ b/terraform/plan.go @@ -18,15 +18,6 @@ func init() { gob.Register(make(map[string]string)) } -// PlanOpts are the options used to generate an execution plan for -// Terraform. -type PlanOpts struct { - // If set to true, then the generated plan will destroy all resources - // that are created. Otherwise, it will move towards the desired state - // specified in the configuration. - Destroy bool -} - // Plan represents a single Terraform execution plan, which contains // all the information necessary to make an infrastructure change. type Plan struct { diff --git a/terraform/test-fixtures/transform-targets-basic/main.tf b/terraform/test-fixtures/transform-targets-basic/main.tf new file mode 100644 index 000000000..b845a1de6 --- /dev/null +++ b/terraform/test-fixtures/transform-targets-basic/main.tf @@ -0,0 +1,16 @@ +resource "aws_vpc" "me" {} + +resource "aws_subnet" "me" { + vpc_id = "${aws_vpc.me.id}" +} + +resource "aws_instance" "me" { + subnet_id = "${aws_subnet.me.id}" +} + +resource "aws_vpc" "notme" {} +resource "aws_subnet" "notme" {} +resource "aws_instance" "notme" {} +resource "aws_instance" "notmeeither" { + name = "${aws_instance.me.id}" +} diff --git a/terraform/test-fixtures/transform-targets-destroy/main.tf b/terraform/test-fixtures/transform-targets-destroy/main.tf new file mode 100644 index 000000000..da99de43c --- /dev/null +++ b/terraform/test-fixtures/transform-targets-destroy/main.tf @@ -0,0 +1,18 @@ +resource "aws_vpc" "notme" {} + +resource "aws_subnet" "notme" { + vpc_id = "${aws_vpc.notme.id}" +} + +resource "aws_instance" "me" { + subnet_id = "${aws_subnet.notme.id}" +} + +resource "aws_instance" "notme" {} +resource "aws_instance" "metoo" { + name = "${aws_instance.me.id}" +} + +resource "aws_elb" "me" { + instances = "${aws_instance.me.*.id}" +} diff --git a/terraform/transform_targets.go b/terraform/transform_targets.go new file mode 100644 index 000000000..0897ef7f9 --- /dev/null +++ b/terraform/transform_targets.go @@ -0,0 +1,77 @@ +package terraform + +import "github.com/hashicorp/terraform/dag" + +// TargetsTransformer is a GraphTransformer that, when the user specifies a +// list of resources to target, limits the graph to only those resources and +// their dependencies. +type TargetsTransformer struct { + // List of targeted resource names specified by the user + Targets []string + + // Set to true when we're in a `terraform destroy` or a + // `terraform plan -destroy` + Destroy bool +} + +func (t *TargetsTransformer) Transform(g *Graph) error { + if len(t.Targets) > 0 { + targetedNodes, err := t.selectTargetedNodes(g) + if err != nil { + return err + } + + for _, v := range g.Vertices() { + if !targetedNodes.Include(v) { + g.Remove(v) + } + } + } + return nil +} + +func (t *TargetsTransformer) selectTargetedNodes(g *Graph) (*dag.Set, error) { + targetedNodes := new(dag.Set) + for _, v := range g.Vertices() { + // Keep all providers; they'll be pruned later if necessary + if r, ok := v.(GraphNodeProvider); ok { + targetedNodes.Add(r) + continue + } + + // For the remaining filter, we only care about Resources and their deps + r, ok := v.(*GraphNodeConfigResource) + if !ok { + continue + } + + if t.resourceIsTarget(r) { + targetedNodes.Add(r) + + var deps *dag.Set + var err error + if t.Destroy { + deps, err = g.Descendents(r) + } else { + deps, err = g.Ancestors(r) + } + if err != nil { + return nil, err + } + + for _, d := range deps.List() { + targetedNodes.Add(d) + } + } + } + return targetedNodes, nil +} + +func (t *TargetsTransformer) resourceIsTarget(r *GraphNodeConfigResource) bool { + for _, target := range t.Targets { + if target == r.Name() { + return true + } + } + return false +} diff --git a/terraform/transform_targets_test.go b/terraform/transform_targets_test.go new file mode 100644 index 000000000..2daa72827 --- /dev/null +++ b/terraform/transform_targets_test.go @@ -0,0 +1,71 @@ +package terraform + +import ( + "strings" + "testing" +) + +func TestTargetsTransformer(t *testing.T) { + mod := testModule(t, "transform-targets-basic") + + g := Graph{Path: RootModulePath} + { + tf := &ConfigTransformer{Module: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &TargetsTransformer{Targets: []string{"aws_instance.me"}} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(` +aws_instance.me + aws_subnet.me +aws_subnet.me + aws_vpc.me +aws_vpc.me + `) + if actual != expected { + t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual) + } +} + +func TestTargetsTransformer_destroy(t *testing.T) { + mod := testModule(t, "transform-targets-destroy") + + g := Graph{Path: RootModulePath} + { + tf := &ConfigTransformer{Module: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + { + transform := &TargetsTransformer{ + Targets: []string{"aws_instance.me"}, + Destroy: true, + } + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(` +aws_elb.me + aws_instance.me +aws_instance.me +aws_instance.metoo + aws_instance.me + `) + if actual != expected { + t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual) + } +} From 23d005b0e8146be3e31731ff7f27a02d5148d90d Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 27 Mar 2015 17:24:15 -0500 Subject: [PATCH 149/167] core: docs for targeted operations --- website/source/docs/commands/apply.html.markdown | 3 +++ website/source/docs/commands/destroy.html.markdown | 6 ++++++ website/source/docs/commands/plan.html.markdown | 3 +++ website/source/docs/commands/refresh.html.markdown | 3 +++ 4 files changed, 15 insertions(+) diff --git a/website/source/docs/commands/apply.html.markdown b/website/source/docs/commands/apply.html.markdown index c7e2c27da..6414f0741 100644 --- a/website/source/docs/commands/apply.html.markdown +++ b/website/source/docs/commands/apply.html.markdown @@ -44,6 +44,9 @@ The command-line flags are all optional. The list of available flags are: * `-state-out=path` - Path to write updated state file. By default, the `-state` path will be used. +* `-target=resource` - Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used multiple times. + * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/website/source/docs/commands/destroy.html.markdown b/website/source/docs/commands/destroy.html.markdown index 4ea84f880..0a0f3a738 100644 --- a/website/source/docs/commands/destroy.html.markdown +++ b/website/source/docs/commands/destroy.html.markdown @@ -21,3 +21,9 @@ confirmation before destroying. This command accepts all the flags that the [apply command](/docs/commands/apply.html) accepts. If `-force` is set, then the destroy confirmation will not be shown. + +The `-target` flag, instead of affecting "dependencies" will instead also +destroy any resources that _depend on_ the target(s) specified. + +The behavior of any `terraform destroy` command can be previewed at any time +with an equivalent `terraform plan -destroy` command. diff --git a/website/source/docs/commands/plan.html.markdown b/website/source/docs/commands/plan.html.markdown index 14c10c5da..fe5e90090 100644 --- a/website/source/docs/commands/plan.html.markdown +++ b/website/source/docs/commands/plan.html.markdown @@ -45,6 +45,9 @@ The command-line flags are all optional. The list of available flags are: * `-state=path` - Path to the state file. Defaults to "terraform.tfstate". +* `-target=resource` - Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used multiple times. + * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/website/source/docs/commands/refresh.html.markdown b/website/source/docs/commands/refresh.html.markdown index cc797ca38..6d15e1a40 100644 --- a/website/source/docs/commands/refresh.html.markdown +++ b/website/source/docs/commands/refresh.html.markdown @@ -36,6 +36,9 @@ The command-line flags are all optional. The list of available flags are: * `-state-out=path` - Path to write updated state file. By default, the `-state` path will be used. +* `-target=resource` - Resource to target. Operation will be limited to this + resource and its dependencies. This flag can be used multiple times. + * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. From 40ebfb5cccd27fa38d0f693e2d0b18ef58a0212e Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 30 Mar 2015 15:17:28 -0500 Subject: [PATCH 150/167] core: fill out context tests for targeted ops --- terraform/context_test.go | 164 ++++++++++++++++++ .../test-fixtures/apply-targeted/main.tf | 7 + terraform/test-fixtures/plan-targeted/main.tf | 7 + .../test-fixtures/refresh-targeted/main.tf | 8 + 4 files changed, 186 insertions(+) create mode 100644 terraform/test-fixtures/apply-targeted/main.tf create mode 100644 terraform/test-fixtures/plan-targeted/main.tf create mode 100644 terraform/test-fixtures/refresh-targeted/main.tf diff --git a/terraform/context_test.go b/terraform/context_test.go index 238f04d04..67abb101a 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -1339,6 +1339,40 @@ func TestContext2Plan_multiple_taint(t *testing.T) { } } +func TestContext2Plan_targeted(t *testing.T) { + m := testModule(t, "plan-targeted") + p := testProvider("aws") + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + Targets: []string{"aws_instance.foo"}, + }) + + plan, err := ctx.Plan() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(plan.String()) + expected := strings.TrimSpace(` +DIFF: + +CREATE: aws_instance.foo + num: "" => "2" + type: "" => "aws_instance" + +STATE: + + + `) + if actual != expected { + t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) + } +} + func TestContext2Plan_provider(t *testing.T) { m := testModule(t, "plan-provider") p := testProvider("aws") @@ -1460,6 +1494,47 @@ func TestContext2Refresh(t *testing.T) { } } +func TestContext2Refresh_targeted(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-targeted") + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), + "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), + "aws_instance.me": resourceState("aws_instance", "i-abc123"), + "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), + }, + }, + }, + }, + Targets: []string{"aws_instance.me"}, + }) + + refreshedResources := make([]string, 0, 2) + p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) { + refreshedResources = append(refreshedResources, i.Id) + return is, nil + } + + _, err := ctx.Refresh() + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := []string{"aws_vpc.metoo", "aws_instance.me"} + if !reflect.DeepEqual(refreshedResources, expected) { + t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) + } +} + func TestContext2Refresh_delete(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") @@ -5164,6 +5239,86 @@ func TestContext2Apply_taintDepRequiresNew(t *testing.T) { } } +func TestContext2Apply_targeted(t *testing.T) { + m := testModule(t, "apply-targeted") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + Targets: []string{"aws_instance.foo"}, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + mod := state.RootModule() + if len(mod.Resources) != 1 { + t.Fatalf("expected 1 resource, got: %#v", mod.Resources) + } + + checkStateString(t, state, ` +aws_instance.foo: + ID = foo + num = 2 + type = aws_instance + `) +} + +func TestContext2Apply_targetedDestroy(t *testing.T) { + m := testModule(t, "apply-targeted") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_instance.foo": resourceState("aws_instance", "i-bcd345"), + "aws_instance.bar": resourceState("aws_instance", "i-abc123"), + }, + }, + }, + }, + Targets: []string{"aws_instance.foo"}, + Destroy: true, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + mod := state.RootModule() + if len(mod.Resources) != 1 { + t.Fatalf("expected 1 resource, got: %#v", mod.Resources) + } + + checkStateString(t, state, ` +aws_instance.bar: + ID = i-abc123 + `) +} + func TestContext2Apply_unknownAttribute(t *testing.T) { m := testModule(t, "apply-unknown") p := testProvider("aws") @@ -5553,6 +5708,15 @@ func checkStateString(t *testing.T, state *State, expected string) { } } +func resourceState(resourceType, resourceID string) *ResourceState { + return &ResourceState{ + Type: resourceType, + Primary: &InstanceState{ + ID: resourceID, + }, + } +} + const testContextGraph = ` root: root aws_instance.bar diff --git a/terraform/test-fixtures/apply-targeted/main.tf b/terraform/test-fixtures/apply-targeted/main.tf new file mode 100644 index 000000000..b07fc97f4 --- /dev/null +++ b/terraform/test-fixtures/apply-targeted/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + num = "2" +} + +resource "aws_instance" "bar" { + foo = "bar" +} diff --git a/terraform/test-fixtures/plan-targeted/main.tf b/terraform/test-fixtures/plan-targeted/main.tf new file mode 100644 index 000000000..1b6cdae67 --- /dev/null +++ b/terraform/test-fixtures/plan-targeted/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + num = "2" +} + +resource "aws_instance" "bar" { + foo = "${aws_instance.foo.num}" +} diff --git a/terraform/test-fixtures/refresh-targeted/main.tf b/terraform/test-fixtures/refresh-targeted/main.tf new file mode 100644 index 000000000..3a7618464 --- /dev/null +++ b/terraform/test-fixtures/refresh-targeted/main.tf @@ -0,0 +1,8 @@ +resource "aws_vpc" "metoo" {} +resource "aws_instance" "notme" { } +resource "aws_instance" "me" { + vpc_id = "${aws_vpc.metoo.id}" +} +resource "aws_elb" "meneither" { + instances = ["${aws_instance.me.*.id}"] +} From c6300d511c51aa33e53e127484332d852f92c4f4 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 30 Mar 2015 19:02:36 -0500 Subject: [PATCH 151/167] core: formalize resource addressing Only used in targets for now. The plan is to use this for interpolation as well. This allows us to target: * individual resources expanded by `count` using bracket / index notation. * deposed / tainted resources with an `InstanceType` field after name Docs to follow. --- terraform/context_test.go | 205 +++++++++++++++++ terraform/graph_builder.go | 6 +- terraform/graph_config_node.go | 45 +++- terraform/instancetype.go | 13 ++ terraform/instancetype_string.go | 16 ++ terraform/resource_address.go | 98 +++++++++ terraform/resource_address_test.go | 207 ++++++++++++++++++ .../apply-targeted-count/main.tf | 7 + .../refresh-targeted-count/main.tf | 9 + terraform/transform_orphan.go | 13 ++ terraform/transform_resource.go | 48 +++- terraform/transform_targets.go | 44 +++- 12 files changed, 696 insertions(+), 15 deletions(-) create mode 100644 terraform/instancetype.go create mode 100644 terraform/instancetype_string.go create mode 100644 terraform/resource_address.go create mode 100644 terraform/resource_address_test.go create mode 100644 terraform/test-fixtures/apply-targeted-count/main.tf create mode 100644 terraform/test-fixtures/refresh-targeted-count/main.tf diff --git a/terraform/context_test.go b/terraform/context_test.go index 67abb101a..f582ae679 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -1535,6 +1535,98 @@ func TestContext2Refresh_targeted(t *testing.T) { } } +func TestContext2Refresh_targetedCount(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-targeted-count") + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), + "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), + "aws_instance.me.0": resourceState("aws_instance", "i-abc123"), + "aws_instance.me.1": resourceState("aws_instance", "i-cde567"), + "aws_instance.me.2": resourceState("aws_instance", "i-cde789"), + "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), + }, + }, + }, + }, + Targets: []string{"aws_instance.me"}, + }) + + refreshedResources := make([]string, 0, 2) + p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) { + refreshedResources = append(refreshedResources, i.Id) + return is, nil + } + + _, err := ctx.Refresh() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Target didn't specify index, so we should get all our instances + expected := []string{ + "aws_vpc.metoo", + "aws_instance.me.0", + "aws_instance.me.1", + "aws_instance.me.2", + } + if !reflect.DeepEqual(refreshedResources, expected) { + t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) + } +} + +func TestContext2Refresh_targetedCountIndex(t *testing.T) { + p := testProvider("aws") + m := testModule(t, "refresh-targeted-count") + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_vpc.metoo": resourceState("aws_vpc", "vpc-abc123"), + "aws_instance.notme": resourceState("aws_instance", "i-bcd345"), + "aws_instance.me.0": resourceState("aws_instance", "i-abc123"), + "aws_instance.me.1": resourceState("aws_instance", "i-cde567"), + "aws_instance.me.2": resourceState("aws_instance", "i-cde789"), + "aws_elb.meneither": resourceState("aws_elb", "lb-abc123"), + }, + }, + }, + }, + Targets: []string{"aws_instance.me[0]"}, + }) + + refreshedResources := make([]string, 0, 2) + p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) { + refreshedResources = append(refreshedResources, i.Id) + return is, nil + } + + _, err := ctx.Refresh() + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := []string{"aws_vpc.metoo", "aws_instance.me.0"} + if !reflect.DeepEqual(refreshedResources, expected) { + t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) + } +} + func TestContext2Refresh_delete(t *testing.T) { p := testProvider("aws") m := testModule(t, "refresh-basic") @@ -5274,6 +5366,66 @@ aws_instance.foo: `) } +func TestContext2Apply_targetedCount(t *testing.T) { + m := testModule(t, "apply-targeted-count") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + Targets: []string{"aws_instance.foo"}, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + checkStateString(t, state, ` +aws_instance.foo.0: + ID = foo +aws_instance.foo.1: + ID = foo +aws_instance.foo.2: + ID = foo + `) +} + +func TestContext2Apply_targetedCountIndex(t *testing.T) { + m := testModule(t, "apply-targeted-count") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + Targets: []string{"aws_instance.foo[1]"}, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + checkStateString(t, state, ` +aws_instance.foo.1: + ID = foo + `) +} + func TestContext2Apply_targetedDestroy(t *testing.T) { m := testModule(t, "apply-targeted") p := testProvider("aws") @@ -5319,6 +5471,59 @@ aws_instance.bar: `) } +func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) { + m := testModule(t, "apply-targeted-count") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + State: &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_instance.foo.0": resourceState("aws_instance", "i-bcd345"), + "aws_instance.foo.1": resourceState("aws_instance", "i-bcd345"), + "aws_instance.foo.2": resourceState("aws_instance", "i-bcd345"), + "aws_instance.bar.0": resourceState("aws_instance", "i-abc123"), + "aws_instance.bar.1": resourceState("aws_instance", "i-abc123"), + "aws_instance.bar.2": resourceState("aws_instance", "i-abc123"), + }, + }, + }, + }, + Targets: []string{ + "aws_instance.foo[2]", + "aws_instance.bar[1]", + }, + Destroy: true, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + checkStateString(t, state, ` +aws_instance.bar.0: + ID = i-abc123 +aws_instance.bar.2: + ID = i-abc123 +aws_instance.foo.0: + ID = i-bcd345 +aws_instance.foo.1: + ID = i-bcd345 + `) +} + func TestContext2Apply_unknownAttribute(t *testing.T) { m := testModule(t, "apply-unknown") p := testProvider("aws") diff --git a/terraform/graph_builder.go b/terraform/graph_builder.go index d42f6bfe0..03c59f958 100644 --- a/terraform/graph_builder.go +++ b/terraform/graph_builder.go @@ -89,7 +89,11 @@ func (b *BuiltinGraphBuilder) Steps() []GraphTransformer { return []GraphTransformer{ // Create all our resources from the configuration and state &ConfigTransformer{Module: b.Root}, - &OrphanTransformer{State: b.State, Module: b.Root}, + &OrphanTransformer{ + State: b.State, + Module: b.Root, + Targeting: (len(b.Targets) > 0), + }, // Provider-related transformations &MissingProviderTransformer{Providers: b.Providers}, diff --git a/terraform/graph_config_node.go b/terraform/graph_config_node.go index 625992f3f..ddb96da2c 100644 --- a/terraform/graph_config_node.go +++ b/terraform/graph_config_node.go @@ -21,6 +21,26 @@ type graphNodeConfig interface { GraphNodeDependent } +// GraphNodeAddressable is an interface that all graph nodes for the +// configuration graph need to implement in order to be be addressed / targeted +// properly. +type GraphNodeAddressable interface { + graphNodeConfig + + ResourceAddress() *ResourceAddress +} + +// GraphNodeTargetable is an interface for graph nodes to implement when they +// need to be told about incoming targets. This is useful for nodes that need +// to respect targets as they dynamically expand. Note that the list of targets +// provided will contain every target provided, and each implementing graph +// node must filter this list to targets considered relevant. +type GraphNodeTargetable interface { + GraphNodeAddressable + + SetTargets([]ResourceAddress) +} + // GraphNodeConfigModule represents a module within the configuration graph. type GraphNodeConfigModule struct { Path []string @@ -191,6 +211,9 @@ type GraphNodeConfigResource struct { // If this is set to anything other than destroyModeNone, then this // resource represents a resource that will be destroyed in some way. DestroyMode GraphNodeDestroyMode + + // Used during DynamicExpand to target indexes + Targets []ResourceAddress } func (n *GraphNodeConfigResource) DependableName() []string { @@ -279,6 +302,7 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) steps = append(steps, &ResourceCountTransformer{ Resource: n.Resource, Destroy: n.DestroyMode != DestroyNone, + Targets: n.Targets, }) } @@ -289,8 +313,9 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) // expand orphans, which have all the same semantics in a destroy // as a primary. steps = append(steps, &OrphanTransformer{ - State: state, - View: n.Resource.Id(), + State: state, + View: n.Resource.Id(), + Targeting: (len(n.Targets) > 0), }) steps = append(steps, &DeposedTransformer{ @@ -314,6 +339,22 @@ func (n *GraphNodeConfigResource) DynamicExpand(ctx EvalContext) (*Graph, error) return b.Build(ctx.Path()) } +// GraphNodeAddressable impl. +func (n *GraphNodeConfigResource) ResourceAddress() *ResourceAddress { + return &ResourceAddress{ + // Indicates no specific index; will match on other three fields + Index: -1, + InstanceType: TypePrimary, + Name: n.Resource.Name, + Type: n.Resource.Type, + } +} + +// GraphNodeTargetable impl. +func (n *GraphNodeConfigResource) SetTargets(targets []ResourceAddress) { + n.Targets = targets +} + // GraphNodeEvalable impl. func (n *GraphNodeConfigResource) EvalTree() EvalNode { return &EvalSequence{ diff --git a/terraform/instancetype.go b/terraform/instancetype.go new file mode 100644 index 000000000..08959717b --- /dev/null +++ b/terraform/instancetype.go @@ -0,0 +1,13 @@ +package terraform + +//go:generate stringer -type=InstanceType instancetype.go + +// InstanceType is an enum of the various types of instances store in the State +type InstanceType int + +const ( + TypeInvalid InstanceType = iota + TypePrimary + TypeTainted + TypeDeposed +) diff --git a/terraform/instancetype_string.go b/terraform/instancetype_string.go new file mode 100644 index 000000000..fc8697644 --- /dev/null +++ b/terraform/instancetype_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=InstanceType instancetype.go; DO NOT EDIT + +package terraform + +import "fmt" + +const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed" + +var _InstanceType_index = [...]uint8{0, 11, 22, 33, 44} + +func (i InstanceType) String() string { + if i < 0 || i+1 >= InstanceType(len(_InstanceType_index)) { + return fmt.Sprintf("InstanceType(%d)", i) + } + return _InstanceType_name[_InstanceType_index[i]:_InstanceType_index[i+1]] +} diff --git a/terraform/resource_address.go b/terraform/resource_address.go new file mode 100644 index 000000000..b54a923d8 --- /dev/null +++ b/terraform/resource_address.go @@ -0,0 +1,98 @@ +package terraform + +import ( + "fmt" + "regexp" + "strconv" +) + +// ResourceAddress is a way of identifying an individual resource (or, +// eventually, a subset of resources) within the state. It is used for Targets. +type ResourceAddress struct { + Index int + InstanceType InstanceType + Name string + Type string +} + +func ParseResourceAddress(s string) (*ResourceAddress, error) { + matches, err := tokenizeResourceAddress(s) + if err != nil { + return nil, err + } + resourceIndex := -1 + if matches["index"] != "" { + var err error + if resourceIndex, err = strconv.Atoi(matches["index"]); err != nil { + return nil, err + } + } + instanceType := TypePrimary + if matches["instance_type"] != "" { + var err error + if instanceType, err = ParseInstanceType(matches["instance_type"]); err != nil { + return nil, err + } + } + + return &ResourceAddress{ + Index: resourceIndex, + InstanceType: instanceType, + Name: matches["name"], + Type: matches["type"], + }, nil +} + +func (addr *ResourceAddress) Equals(raw interface{}) bool { + other, ok := raw.(*ResourceAddress) + if !ok { + return false + } + + indexMatch := (addr.Index == -1 || + other.Index == -1 || + addr.Index == other.Index) + + return (indexMatch && + addr.InstanceType == other.InstanceType && + addr.Name == other.Name && + addr.Type == other.Type) +} + +func ParseInstanceType(s string) (InstanceType, error) { + switch s { + case "primary": + return TypePrimary, nil + case "deposed": + return TypeDeposed, nil + case "tainted": + return TypeTainted, nil + default: + return TypeInvalid, fmt.Errorf("Unexpected value for InstanceType field: %q", s) + } +} + +func tokenizeResourceAddress(s string) (map[string]string, error) { + // Example of portions of the regexp below using the + // string "aws_instance.web.tainted[1]" + re := regexp.MustCompile(`\A` + + // "aws_instance" + `(?P\w+)\.` + + // "web" + `(?P\w+)` + + // "tainted" (optional, omission implies: "primary") + `(?:\.(?P\w+))?` + + // "1" (optional, omission implies: "0") + `(?:\[(?P\d+)\])?` + + `\z`) + groupNames := re.SubexpNames() + rawMatches := re.FindAllStringSubmatch(s, -1) + if len(rawMatches) != 1 { + return nil, fmt.Errorf("Problem parsing address: %q", s) + } + matches := make(map[string]string) + for i, m := range rawMatches[0] { + matches[groupNames[i]] = m + } + return matches, nil +} diff --git a/terraform/resource_address_test.go b/terraform/resource_address_test.go new file mode 100644 index 000000000..2a8caa1f8 --- /dev/null +++ b/terraform/resource_address_test.go @@ -0,0 +1,207 @@ +package terraform + +import ( + "reflect" + "testing" +) + +func TestParseResourceAddress(t *testing.T) { + cases := map[string]struct { + Input string + Expected *ResourceAddress + }{ + "implicit primary, no specific index": { + Input: "aws_instance.foo", + Expected: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: -1, + }, + }, + "implicit primary, explicit index": { + Input: "aws_instance.foo[2]", + Expected: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 2, + }, + }, + "explicit primary, explicit index": { + Input: "aws_instance.foo.primary[2]", + Expected: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 2, + }, + }, + "tainted": { + Input: "aws_instance.foo.tainted", + Expected: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypeTainted, + Index: -1, + }, + }, + "deposed": { + Input: "aws_instance.foo.deposed", + Expected: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypeDeposed, + Index: -1, + }, + }, + } + + for tn, tc := range cases { + out, err := ParseResourceAddress(tc.Input) + if err != nil { + t.Fatalf("unexpected err: %#v", err) + } + + if !reflect.DeepEqual(out, tc.Expected) { + t.Fatalf("bad: %q\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Expected, out) + } + } +} + +func TestResourceAddressEquals(t *testing.T) { + cases := map[string]struct { + Address *ResourceAddress + Other interface{} + Expect bool + }{ + "basic match": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Expect: true, + }, + "address does not set index": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: -1, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 3, + }, + Expect: true, + }, + "other does not set index": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 3, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: -1, + }, + Expect: true, + }, + "neither sets index": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: -1, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: -1, + }, + Expect: true, + }, + "different type": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Other: &ResourceAddress{ + Type: "aws_vpc", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Expect: false, + }, + "different name": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "bar", + InstanceType: TypePrimary, + Index: 0, + }, + Expect: false, + }, + "different instance type": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypeTainted, + Index: 0, + }, + Expect: false, + }, + "different index": { + Address: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 0, + }, + Other: &ResourceAddress{ + Type: "aws_instance", + Name: "foo", + InstanceType: TypePrimary, + Index: 1, + }, + Expect: false, + }, + } + + for tn, tc := range cases { + actual := tc.Address.Equals(tc.Other) + if actual != tc.Expect { + t.Fatalf("%q: expected equals: %t, got %t for:\n%#v\n%#v", + tn, tc.Expect, actual, tc.Address, tc.Other) + } + } +} diff --git a/terraform/test-fixtures/apply-targeted-count/main.tf b/terraform/test-fixtures/apply-targeted-count/main.tf new file mode 100644 index 000000000..cd861898f --- /dev/null +++ b/terraform/test-fixtures/apply-targeted-count/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + count = 3 +} + +resource "aws_instance" "bar" { + count = 3 +} diff --git a/terraform/test-fixtures/refresh-targeted-count/main.tf b/terraform/test-fixtures/refresh-targeted-count/main.tf new file mode 100644 index 000000000..f564b629c --- /dev/null +++ b/terraform/test-fixtures/refresh-targeted-count/main.tf @@ -0,0 +1,9 @@ +resource "aws_vpc" "metoo" {} +resource "aws_instance" "notme" { } +resource "aws_instance" "me" { + vpc_id = "${aws_vpc.metoo.id}" + count = 3 +} +resource "aws_elb" "meneither" { + instances = ["${aws_instance.me.*.id}"] +} diff --git a/terraform/transform_orphan.go b/terraform/transform_orphan.go index e2a9c7dcd..5de64c65c 100644 --- a/terraform/transform_orphan.go +++ b/terraform/transform_orphan.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "log" "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config/module" @@ -25,6 +26,11 @@ type OrphanTransformer struct { // using the graph path. Module *module.Tree + // Targets are user-specified resources to target. We need to be aware of + // these so we don't improperly identify orphans when they've just been + // filtered out of the graph via targeting. + Targeting bool + // View, if non-nil will set a view on the module state. View string } @@ -35,6 +41,13 @@ func (t *OrphanTransformer) Transform(g *Graph) error { return nil } + if t.Targeting { + log.Printf("Skipping orphan transformer because we have targets.") + // If we are in a run where we are targeting nodes, we won't process + // orphans for this run. + return nil + } + // Build up all our state representatives resourceRep := make(map[string]struct{}) for _, v := range g.Vertices() { diff --git a/terraform/transform_resource.go b/terraform/transform_resource.go index 8c2a00c78..21774e953 100644 --- a/terraform/transform_resource.go +++ b/terraform/transform_resource.go @@ -12,6 +12,7 @@ import ( type ResourceCountTransformer struct { Resource *config.Resource Destroy bool + Targets []ResourceAddress } func (t *ResourceCountTransformer) Transform(g *Graph) error { @@ -27,7 +28,7 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error { } // For each count, build and add the node - nodes := make([]dag.Vertex, count) + nodes := make([]dag.Vertex, 0, count) for i := 0; i < count; i++ { // Set the index. If our count is 1 we special case it so that // we handle the "resource.0" and "resource" boundary properly. @@ -49,9 +50,14 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error { } } + // Skip nodes if targeting excludes them + if !t.nodeIsTargeted(node) { + continue + } + // Add the node now - nodes[i] = node - g.Add(nodes[i]) + nodes = append(nodes, node) + g.Add(node) } // Make the dependency connections @@ -64,6 +70,25 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error { return nil } +func (t *ResourceCountTransformer) nodeIsTargeted(node dag.Vertex) bool { + // no targets specified, everything stays in the graph + if len(t.Targets) == 0 { + return true + } + addressable, ok := node.(GraphNodeAddressable) + if !ok { + return false + } + + addr := addressable.ResourceAddress() + for _, targetAddr := range t.Targets { + if targetAddr.Equals(addr) { + return true + } + } + return false +} + type graphNodeExpandedResource struct { Index int Resource *config.Resource @@ -77,6 +102,23 @@ func (n *graphNodeExpandedResource) Name() string { return fmt.Sprintf("%s #%d", n.Resource.Id(), n.Index) } +// GraphNodeAddressable impl. +func (n *graphNodeExpandedResource) ResourceAddress() *ResourceAddress { + // We want this to report the logical index properly, so we must undo the + // special case from the expand + index := n.Index + if index == -1 { + index = 0 + } + return &ResourceAddress{ + Index: index, + // TODO: kjkjkj + InstanceType: TypePrimary, + Name: n.Resource.Name, + Type: n.Resource.Type, + } +} + // GraphNodeDependable impl. func (n *graphNodeExpandedResource) DependableName() []string { return []string{ diff --git a/terraform/transform_targets.go b/terraform/transform_targets.go index 0897ef7f9..29a6d53c6 100644 --- a/terraform/transform_targets.go +++ b/terraform/transform_targets.go @@ -16,13 +16,20 @@ type TargetsTransformer struct { func (t *TargetsTransformer) Transform(g *Graph) error { if len(t.Targets) > 0 { - targetedNodes, err := t.selectTargetedNodes(g) + // TODO: duplicated in OrphanTransformer; pull up parsing earlier + addrs, err := t.parseTargetAddresses() + if err != nil { + return err + } + + targetedNodes, err := t.selectTargetedNodes(g, addrs) if err != nil { return err } for _, v := range g.Vertices() { - if !targetedNodes.Include(v) { + if targetedNodes.Include(v) { + } else { g.Remove(v) } } @@ -30,7 +37,20 @@ func (t *TargetsTransformer) Transform(g *Graph) error { return nil } -func (t *TargetsTransformer) selectTargetedNodes(g *Graph) (*dag.Set, error) { +func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) { + addrs := make([]ResourceAddress, len(t.Targets)) + for i, target := range t.Targets { + ta, err := ParseResourceAddress(target) + if err != nil { + return nil, err + } + addrs[i] = *ta + } + return addrs, nil +} + +func (t *TargetsTransformer) selectTargetedNodes( + g *Graph, addrs []ResourceAddress) (*dag.Set, error) { targetedNodes := new(dag.Set) for _, v := range g.Vertices() { // Keep all providers; they'll be pruned later if necessary @@ -39,14 +59,18 @@ func (t *TargetsTransformer) selectTargetedNodes(g *Graph) (*dag.Set, error) { continue } - // For the remaining filter, we only care about Resources and their deps - r, ok := v.(*GraphNodeConfigResource) + // For the remaining filter, we only care about addressable nodes + r, ok := v.(GraphNodeAddressable) if !ok { continue } - if t.resourceIsTarget(r) { + if t.nodeIsTarget(r, addrs) { targetedNodes.Add(r) + // If the node would like to know about targets, tell it. + if n, ok := r.(GraphNodeTargetable); ok { + n.SetTargets(addrs) + } var deps *dag.Set var err error @@ -67,9 +91,11 @@ func (t *TargetsTransformer) selectTargetedNodes(g *Graph) (*dag.Set, error) { return targetedNodes, nil } -func (t *TargetsTransformer) resourceIsTarget(r *GraphNodeConfigResource) bool { - for _, target := range t.Targets { - if target == r.Name() { +func (t *TargetsTransformer) nodeIsTarget( + r GraphNodeAddressable, addrs []ResourceAddress) bool { + addr := r.ResourceAddress() + for _, targetAddr := range addrs { + if targetAddr.Equals(addr) { return true } } From 596511078ab8193a648f86ab690cf52ab77fdb93 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 31 Mar 2015 14:48:57 -0700 Subject: [PATCH 152/167] website: note on docker --- website/source/docs/providers/docker/index.html.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website/source/docs/providers/docker/index.html.markdown b/website/source/docs/providers/docker/index.html.markdown index 73807c12e..9a1f4abe1 100644 --- a/website/source/docs/providers/docker/index.html.markdown +++ b/website/source/docs/providers/docker/index.html.markdown @@ -16,6 +16,12 @@ API hosts. Use the navigation to the left to read about the available resources. +
+Note: The Docker provider is new as of Terraform 0.4. +It is ready to be used but many features are still being added. If there +is a Docker feature missing, please report it in the GitHub repo. +
+ ## Example Usage ``` From d00f32a4f242e2ac5ee7cfff50ddce0b231f031f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 31 Mar 2015 14:53:16 -0700 Subject: [PATCH 153/167] update CHANGELOG --- CHANGELOG.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2485613f8..5d921e071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ BACKWARDS INCOMPATIBILITIES: FEATURES: * **New provider: `dme` (DNSMadeEasy)** [GH-855] + * **New provider: `docker` (Docker)** - Manage container lifecycle + using the standard Docker API. [GH-855] * **New command: `taint`** - Manually mark a resource as tainted, causing a destroy and recreate on the next plan/apply. * **New resource: `aws_vpn_gateway`** [GH-1137] @@ -24,8 +26,8 @@ FEATURES: or system killing Terraform. * **Math operations** in interpolations. You can now do things like `${count.index+1}`. [GH-1068] - * **New AWS SDK:** Move to `aws-sdk-go` (hashicorp/aws-sdk-go), - a fork of the offical `awslabs` repo. We forked for stability while + * **New AWS SDK:** Move to `aws-sdk-go` (hashicorp/aws-sdk-go), + a fork of the offical `awslabs` repo. We forked for stability while `awslabs` refactored the library, and will move back to the officially supported version in the next release. @@ -49,9 +51,9 @@ IMPROVEMENTS: automatically done initially. * providers/google: Add `size` option to disk blocks for instances. [GH-1284] * providers/aws: Improve support for tagging resources. - * providers/aws: Add a short syntax for Route 53 Record names, e.g. + * providers/aws: Add a short syntax for Route 53 Record names, e.g. `www` instead of `www.example.com`. - * providers/aws: Improve dependency violation error handling, when deleting + * providers/aws: Improve dependency violation error handling, when deleting Internet Gateways or Auto Scaling groups [GH-1325]. BUG FIXES: @@ -73,15 +75,15 @@ BUG FIXES: * providers/aws: Longer wait times for route53 records (30 mins). [GH-1164] * providers/aws: Fix support for TXT records in Route 53. [GH-1213] * providers/aws: Fix support for wildcard records in Route 53. [GH-1222] - * providers/aws: Fix issue with ignoring the 'self' attribute of a + * providers/aws: Fix issue with ignoring the 'self' attribute of a Security Group rule. [GH-1223] - * providers/aws: Fix issue with `sql_mode` in RDS parameter group always + * providers/aws: Fix issue with `sql_mode` in RDS parameter group always causing an update. [GH-1225] - * providers/aws: Fix dependency violation with subnets and security groups + * providers/aws: Fix dependency violation with subnets and security groups [GH-1252] - * providers/aws: Fix issue with refreshing `db_subnet_groups` causing an error + * providers/aws: Fix issue with refreshing `db_subnet_groups` causing an error instead of updating state [GH-1254] - * providers/aws: Prevent empty string to be used as default + * providers/aws: Prevent empty string to be used as default `health_check_type` [GH-1052] * providers/aws: Add tags on AWS IG creation, not just on update [GH-1176] * providers/digitalocean: Waits until droplet is ready to be destroyed [GH-1057] From dfee25fc47118a7992b1b3ae83ac57b3b6862b34 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 31 Mar 2015 17:00:44 -0500 Subject: [PATCH 154/167] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d921e071..24336b5ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ FEATURES: * **New provider: `dme` (DNSMadeEasy)** [GH-855] * **New provider: `docker` (Docker)** - Manage container lifecycle using the standard Docker API. [GH-855] + * **New provider: `openstack` (OpenStack)** - Interact with the many resources + provided by OpenStack. [GH-924] * **New command: `taint`** - Manually mark a resource as tainted, causing a destroy and recreate on the next plan/apply. * **New resource: `aws_vpn_gateway`** [GH-1137] From a90b9db3971a3b27e05bbc2fcf9180bc750c641d Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Fri, 27 Mar 2015 15:47:28 +0100 Subject: [PATCH 155/167] Bugfix on floating IP assignment The `getFirstNetworkID` does not work correctly because the first network is not always the private network of the instance. As long as the `GET /networks` gives a list containing also public networks we don't have any guarantee that the first network is the one we want. Furthermore, with a loop over the network list we are not able to determine which network is the one we want. Instead of retrieving the network ID and then finding the port ID, it's better to basically take the first port ID of the instance. --- .../resource_openstack_compute_instance_v2.go | 44 +++++-------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 5a4c16c7e..b5fe36a10 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -20,7 +20,6 @@ import ( "github.com/rackspace/gophercloud/openstack/compute/v2/images" "github.com/rackspace/gophercloud/openstack/compute/v2/servers" "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" - "github.com/rackspace/gophercloud/openstack/networking/v2/networks" "github.com/rackspace/gophercloud/openstack/networking/v2/ports" "github.com/rackspace/gophercloud/pagination" ) @@ -303,7 +302,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e } err = assignFloatingIP(networkingClient, extractFloatingIPFromIP(allFloatingIPs, floatingIP), server.ID) if err != nil { - fmt.Errorf("Error assigning floating IP to OpenStack compute instance: %s", err) + return fmt.Errorf("Error assigning floating IP to OpenStack compute instance: %s", err) } } @@ -770,44 +769,18 @@ func extractFloatingIPFromIP(ips []floatingips.FloatingIP, IP string) *floatingi } func assignFloatingIP(networkingClient *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, instanceID string) error { - networkID, err := getFirstNetworkID(networkingClient, instanceID) + portID, err := getInstancePortID(networkingClient, instanceID) if err != nil { return err } - portID, err := getInstancePortID(networkingClient, instanceID, networkID) - _, err = floatingips.Update(networkingClient, floatingIP.ID, floatingips.UpdateOpts{ + return floatingips.Update(networkingClient, floatingIP.ID, floatingips.UpdateOpts{ PortID: portID, - }).Extract() - return err + }).Err } -func getFirstNetworkID(networkingClient *gophercloud.ServiceClient, instanceID string) (string, error) { - pager := networks.List(networkingClient, networks.ListOpts{}) - - var networkdID string - err := pager.EachPage(func(page pagination.Page) (bool, error) { - networkList, err := networks.ExtractNetworks(page) - if err != nil { - return false, err - } - - if len(networkList) > 0 { - networkdID = networkList[0].ID - return false, nil - } - return false, fmt.Errorf("No network found for the instance %s", instanceID) - }) - if err != nil { - return "", err - } - return networkdID, nil - -} - -func getInstancePortID(networkingClient *gophercloud.ServiceClient, instanceID, networkID string) (string, error) { +func getInstancePortID(networkingClient *gophercloud.ServiceClient, instanceID string) (string, error) { pager := ports.List(networkingClient, ports.ListOpts{ - DeviceID: instanceID, - NetworkID: networkID, + DeviceID: instanceID, }) var portID string @@ -826,6 +799,11 @@ func getInstancePortID(networkingClient *gophercloud.ServiceClient, instanceID, if err != nil { return "", err } + + if portID == "" { + return "", fmt.Errorf("Cannot find port for instance %s", instanceID) + } + return portID, nil } From ad19adfdc0b0934d44d1079bdbd25ad22275a3aa Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Tue, 31 Mar 2015 15:49:10 -0700 Subject: [PATCH 156/167] providers/heroku: Add region to example ...since it's required --- website/source/docs/providers/heroku/r/app.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/source/docs/providers/heroku/r/app.html.markdown b/website/source/docs/providers/heroku/r/app.html.markdown index d05bd2fb0..9e51d62f2 100644 --- a/website/source/docs/providers/heroku/r/app.html.markdown +++ b/website/source/docs/providers/heroku/r/app.html.markdown @@ -17,6 +17,7 @@ create and manage applications on Heroku. # Create a new Heroku app resource "heroku_app" "default" { name = "my-cool-app" + region = "us" config_vars { FOOBAR = "baz" From 6d3425f9619a56252e1eacd21a3a4f930208455c Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Tue, 31 Mar 2015 15:50:00 -0700 Subject: [PATCH 157/167] providers/heroku: Document environment variables --- website/source/docs/providers/heroku/index.html.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/heroku/index.html.markdown b/website/source/docs/providers/heroku/index.html.markdown index b04fd001c..696a41963 100644 --- a/website/source/docs/providers/heroku/index.html.markdown +++ b/website/source/docs/providers/heroku/index.html.markdown @@ -33,6 +33,8 @@ resource "heroku_app" "default" { The following arguments are supported: -* `api_key` - (Required) Heroku API token -* `email` - (Required) Email to be notified by Heroku +* `api_key` - (Required) Heroku API token. It must be provided, but it can also + be sourced from the `HEROKU_API_KEY` environment variable. +* `email` - (Required) Email to be notified by Heroku. It must be provided, but + it can also be sourced from the `HEROKU_EMAIL` environment variable. From 91a3d22a9f36b48348a1018e3518273576396b7c Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 31 Mar 2015 18:48:54 -0500 Subject: [PATCH 158/167] docs: resource addressing --- .../source/docs/commands/apply.html.markdown | 6 +- .../source/docs/commands/plan.html.markdown | 6 +- .../docs/commands/refresh.html.markdown | 6 +- .../resource-addressing.html.markdown | 57 +++++++++++++++++++ website/source/layouts/docs.erb | 4 ++ 5 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 website/source/docs/internals/resource-addressing.html.markdown diff --git a/website/source/docs/commands/apply.html.markdown b/website/source/docs/commands/apply.html.markdown index 6414f0741..9bb5acdbf 100644 --- a/website/source/docs/commands/apply.html.markdown +++ b/website/source/docs/commands/apply.html.markdown @@ -44,8 +44,10 @@ The command-line flags are all optional. The list of available flags are: * `-state-out=path` - Path to write updated state file. By default, the `-state` path will be used. -* `-target=resource` - Resource to target. Operation will be limited to this - resource and its dependencies. This flag can be used multiple times. +* `-target=resource` - A [Resource + Address](/docs/internals/resource-addressing.html) to target. Operation will + be limited to this resource and its dependencies. This flag can be used + multiple times. * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/website/source/docs/commands/plan.html.markdown b/website/source/docs/commands/plan.html.markdown index fe5e90090..e05c460ce 100644 --- a/website/source/docs/commands/plan.html.markdown +++ b/website/source/docs/commands/plan.html.markdown @@ -45,8 +45,10 @@ The command-line flags are all optional. The list of available flags are: * `-state=path` - Path to the state file. Defaults to "terraform.tfstate". -* `-target=resource` - Resource to target. Operation will be limited to this - resource and its dependencies. This flag can be used multiple times. +* `-target=resource` - A [Resource + Address](/docs/internals/resource-addressing.html) to target. Operation will + be limited to this resource and its dependencies. This flag can be used + multiple times. * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/website/source/docs/commands/refresh.html.markdown b/website/source/docs/commands/refresh.html.markdown index 6d15e1a40..0fc3fc938 100644 --- a/website/source/docs/commands/refresh.html.markdown +++ b/website/source/docs/commands/refresh.html.markdown @@ -36,8 +36,10 @@ The command-line flags are all optional. The list of available flags are: * `-state-out=path` - Path to write updated state file. By default, the `-state` path will be used. -* `-target=resource` - Resource to target. Operation will be limited to this - resource and its dependencies. This flag can be used multiple times. +* `-target=resource` - A [Resource + Address](/docs/internals/resource-addressing.html) to target. Operation will + be limited to this resource and its dependencies. This flag can be used + multiple times. * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. diff --git a/website/source/docs/internals/resource-addressing.html.markdown b/website/source/docs/internals/resource-addressing.html.markdown new file mode 100644 index 000000000..b4b994a88 --- /dev/null +++ b/website/source/docs/internals/resource-addressing.html.markdown @@ -0,0 +1,57 @@ +--- +layout: "docs" +page_title: "Internals: Resource Address" +sidebar_current: "docs-internals-resource-addressing" +description: |- + Resource addressing is used to target specific resources in a larger + infrastructure. +--- + +# Resource Addressing + +A __Resource Address__ is a string that references a specific resource in a +larger infrastructure. The syntax of a resource address is: + +``` +.[optional fields] +``` + +Required fields: + + * `resource_type` - Type of the resource being addressed. + * `resource_name` - User-defined name of the resource. + +Optional fields may include: + + * `[N]` - where `N` is a `0`-based index into a resource with multiple + instances specified by the `count` meta-parameter. Omitting an index when + addressing a resource where `count > 1` means that the address references + all instances. + + +## Examples + +Given a Terraform config that includes: + +``` +resource "aws_instance" "web" { + # ... + count = 4 +} +``` + +An address like this: + + +``` +aws_instance.web[3] +``` + +Refers to only the last instance in the config, and an address like this: + +``` +aws_instance.web +``` + + +Refers to all four "web" instances. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index a0b31127a..bbdecd4c8 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -215,6 +215,10 @@ > Resource Lifecycle + + > + Resource Addressing + From 9b3826cf368f01d8309b852df62e98466576103c Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Tue, 31 Mar 2015 16:23:20 -0700 Subject: [PATCH 159/167] providers/heroku: Add heroku-postgres to example --- website/source/docs/providers/heroku/r/addon.html.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/website/source/docs/providers/heroku/r/addon.html.markdown b/website/source/docs/providers/heroku/r/addon.html.markdown index d39cb1e8b..f9907597a 100644 --- a/website/source/docs/providers/heroku/r/addon.html.markdown +++ b/website/source/docs/providers/heroku/r/addon.html.markdown @@ -19,6 +19,12 @@ resource "heroku_app" "default" { name = "test-app" } +# Create a database, and configure the app to use it +resource "heroku_addon" "database" { + app = "${heroku_app.default.name}" + plan = "heroku-postgresql:hobby-basic" +} + # Add a web-hook addon for the app resource "heroku_addon" "webhook" { app = "${heroku_app.default.name}" From c0b85d4939b05e74a1e878ad72f3d6101561d029 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 1 Apr 2015 11:24:51 +0200 Subject: [PATCH 160/167] Use env var OS_POOL_NAME as default for pool attribute To have the same behaviour for openstack_networking_floatingip_v2 and openstack_compute_foatingip_v2. --- .../resource_openstack_networking_floatingip_v2.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go index cbc430764..fd8b3fc65 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2.go @@ -28,9 +28,10 @@ func resourceNetworkingFloatingIPV2() *schema.Resource { Computed: true, }, "pool": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: envDefaultFunc("OS_POOL_NAME"), }, }, } From 56aa764b947fac3a0bb94fedcc049b5aaeeb2e73 Mon Sep 17 00:00:00 2001 From: Guillaume Giamarchi Date: Wed, 1 Apr 2015 11:27:56 +0200 Subject: [PATCH 161/167] Add floating IP association in aceptance tests --- .../resource_openstack_compute_floatingip_v2_test.go | 9 +++++++-- ...source_openstack_networking_floatingip_v2_test.go | 12 +++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go index c246d1a51..a298a87d1 100644 --- a/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_floatingip_v2_test.go @@ -81,6 +81,11 @@ func testAccCheckComputeV2FloatingIPExists(t *testing.T, n string, kp *floatingi } } -var testAccComputeV2FloatingIP_basic = fmt.Sprintf(` +var testAccComputeV2FloatingIP_basic = ` resource "openstack_compute_floatingip_v2" "foo" { - }`) + } + + resource "openstack_compute_instance_v2" "bar" { + name = "terraform-acc-floating-ip-test" + floating_ip = "${openstack_compute_floatingip_v2.foo.address}" + }` diff --git a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go index cd08ea512..5c8ae38e3 100644 --- a/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_floatingip_v2_test.go @@ -81,9 +81,11 @@ func testAccCheckNetworkingV2FloatingIPExists(t *testing.T, n string, kp *floati } } -var testAccNetworkingV2FloatingIP_basic = fmt.Sprintf(` +var testAccNetworkingV2FloatingIP_basic = ` resource "openstack_networking_floatingip_v2" "foo" { - region = "%s" - pool = "%s" - }`, - OS_REGION_NAME, OS_POOL_NAME) + } + + resource "openstack_compute_instance_v2" "bar" { + name = "terraform-acc-floating-ip-test" + floating_ip = "${openstack_networking_floatingip_v2.foo.address}" + }` From dc67b043fac9c4d45066e56bd8e0a00e33015a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Guminiak?= Date: Wed, 1 Apr 2015 14:33:08 +0200 Subject: [PATCH 162/167] Avoid panics when DBName is not set provider/aws: The DBName in RDS instance is optional and when not set, the Read function return null. --- builtin/providers/aws/resource_aws_db_instance.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index e99744a0f..11b05c81b 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -304,7 +304,11 @@ func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error { return nil } - d.Set("name", *v.DBName) + if v.DBName != nil { + d.Set("name", *v.DBName) + } else { + d.Set("name", "") + } d.Set("username", *v.MasterUsername) d.Set("engine", *v.Engine) d.Set("engine_version", *v.EngineVersion) From 27f0873de7c24cf768d2ede9dec81c82830c6885 Mon Sep 17 00:00:00 2001 From: Phil Frost Date: Wed, 1 Apr 2015 08:57:50 -0400 Subject: [PATCH 163/167] Don't error when reading s3 bucket with no tags s3.GetBucketTagging returns an error if there are no tags associated with a bucket. Consequently, any configuration with a tagless s3 bucket would fail with an error, "the TagSet does not exist". Handle that error more appropriately, interpreting it as an empty set of tags. --- .../providers/aws/resource_aws_s3_bucket.go | 6 ++---- builtin/providers/aws/s3_tags.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_s3_bucket.go b/builtin/providers/aws/resource_aws_s3_bucket.go index cb32d5fa3..a33f9b35f 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket.go +++ b/builtin/providers/aws/resource_aws_s3_bucket.go @@ -88,14 +88,12 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { return err } - resp, err := s3conn.GetBucketTagging(&s3.GetBucketTaggingRequest{ - Bucket: aws.String(d.Id()), - }) + tagSet, err := getTagSetS3(s3conn, d.Id()) if err != nil { return err } - if err := d.Set("tags", tagsToMapS3(resp.TagSet)); err != nil { + if err := d.Set("tags", tagsToMapS3(tagSet)); err != nil { return err } diff --git a/builtin/providers/aws/s3_tags.go b/builtin/providers/aws/s3_tags.go index 43678952b..4b8234b9b 100644 --- a/builtin/providers/aws/s3_tags.go +++ b/builtin/providers/aws/s3_tags.go @@ -110,3 +110,22 @@ func tagsToMapS3(ts []s3.Tag) map[string]string { return result } + +// return a slice of s3 tags associated with the given s3 bucket. Essentially +// s3.GetBucketTagging, except returns an empty slice instead of an error when +// there are no tags. +func getTagSetS3(s3conn *s3.S3, bucket string) ([]s3.Tag, error) { + request := &s3.GetBucketTaggingRequest{ + Bucket: aws.String(bucket), + } + + response, err := s3conn.GetBucketTagging(request) + if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "NoSuchTagSet" { + // There is no tag set associated with the bucket. + return []s3.Tag{}, nil + } else if err != nil { + return nil, err + } + + return response.TagSet, nil +} From 3d659982828a706006c73c746dc8f8c2df73c572 Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Wed, 1 Apr 2015 09:24:26 -0500 Subject: [PATCH 164/167] provider/aws: Fix issue with tainted ASG groups failing to re-create --- builtin/providers/aws/resource_aws_autoscaling_group.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group.go b/builtin/providers/aws/resource_aws_autoscaling_group.go index de3bbe9cc..60b22ff26 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group.go @@ -287,7 +287,12 @@ func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{}) return err } - return nil + return resource.Retry(5*time.Minute, func() error { + if g, _ = getAwsAutoscalingGroup(d, meta); g != nil { + return fmt.Errorf("Auto Scaling Group still exists") + } + return nil + }) } func getAwsAutoscalingGroup( From 84e448de1a5c58c97c96a6d8c0ce59331eafb1d5 Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Wed, 1 Apr 2015 10:42:53 -0500 Subject: [PATCH 165/167] Fix hashcode for ASG test --- builtin/providers/aws/resource_aws_autoscaling_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_group_test.go b/builtin/providers/aws/resource_aws_autoscaling_group_test.go index 661e71fe8..09a4d73a6 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_group_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_group_test.go @@ -26,7 +26,7 @@ func TestAccAWSAutoScalingGroup_basic(t *testing.T) { testAccCheckAWSAutoScalingGroupExists("aws_autoscaling_group.bar", &group), testAccCheckAWSAutoScalingGroupAttributes(&group), resource.TestCheckResourceAttr( - "aws_autoscaling_group.bar", "availability_zones.1807834199", "us-west-2a"), + "aws_autoscaling_group.bar", "availability_zones.2487133097", "us-west-2a"), resource.TestCheckResourceAttr( "aws_autoscaling_group.bar", "name", "foobar3-terraform-test"), resource.TestCheckResourceAttr( From 816c4b475fd0f1a159229562b7b7c10ceaa518de Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 1 Apr 2015 11:11:19 -0500 Subject: [PATCH 166/167] core: [tests] fix order dependent test --- terraform/context_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terraform/context_test.go b/terraform/context_test.go index f582ae679..b9de8d79d 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -1579,6 +1579,8 @@ func TestContext2Refresh_targetedCount(t *testing.T) { "aws_instance.me.1", "aws_instance.me.2", } + sort.Strings(expected) + sort.Strings(refreshedResources) if !reflect.DeepEqual(refreshedResources, expected) { t.Fatalf("expected: %#v, got: %#v", expected, refreshedResources) } From 815b79753a21582d124642f44502f73e96bdd277 Mon Sep 17 00:00:00 2001 From: Clint Shryock Date: Wed, 1 Apr 2015 14:49:50 -0500 Subject: [PATCH 167/167] return error if failed to set tags on Route 53 zone --- builtin/providers/aws/resource_aws_route53_zone.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_route53_zone.go b/builtin/providers/aws/resource_aws_route53_zone.go index e6c8be571..b60c91a79 100644 --- a/builtin/providers/aws/resource_aws_route53_zone.go +++ b/builtin/providers/aws/resource_aws_route53_zone.go @@ -105,7 +105,10 @@ func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error if resp.ResourceTagSet != nil { tags = resp.ResourceTagSet.Tags } - d.Set("tags", tagsToMapR53(tags)) + + if err := d.Set("tags", tagsToMapR53(tags)); err != nil { + return err + } return nil }