github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/google/resource_google_project.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net/http"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  	"google.golang.org/api/cloudbilling/v1"
    12  	"google.golang.org/api/cloudresourcemanager/v1"
    13  	"google.golang.org/api/googleapi"
    14  )
    15  
    16  // resourceGoogleProject returns a *schema.Resource that allows a customer
    17  // to declare a Google Cloud Project resource.
    18  func resourceGoogleProject() *schema.Resource {
    19  	return &schema.Resource{
    20  		SchemaVersion: 1,
    21  
    22  		Create: resourceGoogleProjectCreate,
    23  		Read:   resourceGoogleProjectRead,
    24  		Update: resourceGoogleProjectUpdate,
    25  		Delete: resourceGoogleProjectDelete,
    26  
    27  		Importer: &schema.ResourceImporter{
    28  			State: schema.ImportStatePassthrough,
    29  		},
    30  		MigrateState: resourceGoogleProjectMigrateState,
    31  
    32  		Schema: map[string]*schema.Schema{
    33  			"id": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Optional: true,
    36  				Computed: true,
    37  				Removed:  "The id field has been removed. Use project_id instead.",
    38  			},
    39  			"project_id": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  			},
    44  			"skip_delete": &schema.Schema{
    45  				Type:     schema.TypeBool,
    46  				Optional: true,
    47  				Computed: true,
    48  			},
    49  			"name": &schema.Schema{
    50  				Type:     schema.TypeString,
    51  				Required: true,
    52  			},
    53  			"org_id": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  				ForceNew: true,
    57  			},
    58  			"policy_data": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Optional: true,
    61  				Computed: true,
    62  				Removed:  "Use the 'google_project_iam_policy' resource to define policies for a Google Project",
    63  			},
    64  			"policy_etag": &schema.Schema{
    65  				Type:     schema.TypeString,
    66  				Computed: true,
    67  				Removed:  "Use the the 'google_project_iam_policy' resource to define policies for a Google Project",
    68  			},
    69  			"number": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Computed: true,
    72  			},
    73  			"billing_account": &schema.Schema{
    74  				Type:     schema.TypeString,
    75  				Optional: true,
    76  			},
    77  		},
    78  	}
    79  }
    80  
    81  func resourceGoogleProjectCreate(d *schema.ResourceData, meta interface{}) error {
    82  	config := meta.(*Config)
    83  
    84  	var pid string
    85  	var err error
    86  	pid = d.Get("project_id").(string)
    87  
    88  	log.Printf("[DEBUG]: Creating new project %q", pid)
    89  	project := &cloudresourcemanager.Project{
    90  		ProjectId: pid,
    91  		Name:      d.Get("name").(string),
    92  		Parent: &cloudresourcemanager.ResourceId{
    93  			Id:   d.Get("org_id").(string),
    94  			Type: "organization",
    95  		},
    96  	}
    97  
    98  	op, err := config.clientResourceManager.Projects.Create(project).Do()
    99  	if err != nil {
   100  		return fmt.Errorf("Error creating project %s (%s): %s.", project.ProjectId, project.Name, err)
   101  	}
   102  
   103  	d.SetId(pid)
   104  
   105  	// Wait for the operation to complete
   106  	waitErr := resourceManagerOperationWait(config, op, "project to create")
   107  	if waitErr != nil {
   108  		// The resource wasn't actually created
   109  		d.SetId("")
   110  		return waitErr
   111  	}
   112  
   113  	// Set the billing account
   114  	if v, ok := d.GetOk("billing_account"); ok {
   115  		name := v.(string)
   116  		ba := cloudbilling.ProjectBillingInfo{
   117  			BillingAccountName: "billingAccounts/" + name,
   118  		}
   119  		_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
   120  		if err != nil {
   121  			d.Set("billing_account", "")
   122  			if _err, ok := err.(*googleapi.Error); ok {
   123  				return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), _err)
   124  			}
   125  			return fmt.Errorf("Error setting billing account %q for project %q: %v", name, prefixedProject(pid), err)
   126  		}
   127  	}
   128  
   129  	return resourceGoogleProjectRead(d, meta)
   130  }
   131  
   132  func resourceGoogleProjectRead(d *schema.ResourceData, meta interface{}) error {
   133  	config := meta.(*Config)
   134  	pid := d.Id()
   135  
   136  	// Read the project
   137  	p, err := config.clientResourceManager.Projects.Get(pid).Do()
   138  	if err != nil {
   139  		return handleNotFoundError(err, d, fmt.Sprintf("Project %q", pid))
   140  	}
   141  
   142  	d.Set("project_id", pid)
   143  	d.Set("number", strconv.FormatInt(int64(p.ProjectNumber), 10))
   144  	d.Set("name", p.Name)
   145  
   146  	if p.Parent != nil {
   147  		d.Set("org_id", p.Parent.Id)
   148  	}
   149  
   150  	// Read the billing account
   151  	ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
   152  	if err != nil {
   153  		return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
   154  	}
   155  	if ba.BillingAccountName != "" {
   156  		// BillingAccountName is contains the resource name of the billing account
   157  		// associated with the project, if any. For example,
   158  		// `billingAccounts/012345-567890-ABCDEF`. We care about the ID and not
   159  		// the `billingAccounts/` prefix, so we need to remove that. If the
   160  		// prefix ever changes, we'll validate to make sure it's something we
   161  		// recognize.
   162  		_ba := strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/")
   163  		if ba.BillingAccountName == _ba {
   164  			return fmt.Errorf("Error parsing billing account for project %q. Expected value to begin with 'billingAccounts/' but got %s", prefixedProject(pid), ba.BillingAccountName)
   165  		}
   166  		d.Set("billing_account", _ba)
   167  	}
   168  	return nil
   169  }
   170  
   171  func prefixedProject(pid string) string {
   172  	return "projects/" + pid
   173  }
   174  
   175  func resourceGoogleProjectUpdate(d *schema.ResourceData, meta interface{}) error {
   176  	config := meta.(*Config)
   177  	pid := d.Id()
   178  
   179  	// Read the project
   180  	// we need the project even though refresh has already been called
   181  	// because the API doesn't support patch, so we need the actual object
   182  	p, err := config.clientResourceManager.Projects.Get(pid).Do()
   183  	if err != nil {
   184  		if v, ok := err.(*googleapi.Error); ok && v.Code == http.StatusNotFound {
   185  			return fmt.Errorf("Project %q does not exist.", pid)
   186  		}
   187  		return fmt.Errorf("Error checking project %q: %s", pid, err)
   188  	}
   189  
   190  	// Project name has changed
   191  	if ok := d.HasChange("name"); ok {
   192  		p.Name = d.Get("name").(string)
   193  		// Do update on project
   194  		p, err = config.clientResourceManager.Projects.Update(p.ProjectId, p).Do()
   195  		if err != nil {
   196  			return fmt.Errorf("Error updating project %q: %s", p.Name, err)
   197  		}
   198  	}
   199  
   200  	// Billing account has changed
   201  	if ok := d.HasChange("billing_account"); ok {
   202  		name := d.Get("billing_account").(string)
   203  		ba := cloudbilling.ProjectBillingInfo{
   204  			BillingAccountName: "billingAccounts/" + name,
   205  		}
   206  		_, err = config.clientBilling.Projects.UpdateBillingInfo(prefixedProject(pid), &ba).Do()
   207  		if err != nil {
   208  			d.Set("billing_account", "")
   209  			if _err, ok := err.(*googleapi.Error); ok {
   210  				return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), _err)
   211  			}
   212  			return fmt.Errorf("Error updating billing account %q for project %q: %v", name, prefixedProject(pid), err)
   213  		}
   214  	}
   215  	return nil
   216  }
   217  
   218  func resourceGoogleProjectDelete(d *schema.ResourceData, meta interface{}) error {
   219  	config := meta.(*Config)
   220  	// Only delete projects if skip_delete isn't set
   221  	if !d.Get("skip_delete").(bool) {
   222  		pid := d.Id()
   223  		_, err := config.clientResourceManager.Projects.Delete(pid).Do()
   224  		if err != nil {
   225  			return fmt.Errorf("Error deleting project %q: %s", pid, err)
   226  		}
   227  	}
   228  	d.SetId("")
   229  	return nil
   230  }