Skip to content

Commit

Permalink
feat: Add circleci_environment_variable resource
Browse files Browse the repository at this point in the history
  • Loading branch information
edahlseng committed Dec 22, 2019
1 parent 41f0119 commit fae99f8
Show file tree
Hide file tree
Showing 6 changed files with 612 additions and 8 deletions.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,39 @@ provider "circleci" {
Resources
---------

### circleci_environment_variable

For reference, see [CircleCI's documentation on Using Environment Variables](https://circleci.com/docs/2.0/env-vars/).

#### Example Usage:

```hcl
resource "circleci_environment_variable" "example_environment_variable" {
project_id = "${circleci_project.example.id}"
name = "MyVariable"
value = "${var.environment_variable_value}"
}
```

#### Argument Reference:

The following arguments are supported:

* project_id (Required) - The ID of the project (in `<vcs_type>/<username>/<name>` format).
* name (Required) - The name of the environment variable.
* value (Required) - The value of the environment variable.

#### Attributes Reference

In addition to all arguments above, the following attributes are exported:

* id - A string with the format `<vcs_type>/<username>/<project_name>/<environment_variable_name>`.
* value_masked - A string with four `x` characters plus the last four ASCII characters of the environment variable's value, consistent with the display of environment variable values in the CircleCI website.

#### Import

The `circleci_environment_variable` resource does not support importing.

### circleci_project

For reference, see [CircleCI's Projects and Builds Documentation](https://circleci.com/docs/2.0/project-build/).
Expand Down
14 changes: 8 additions & 6 deletions circleci/circleci-go/circleci-go.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
)

type Client struct {
sling *sling.Sling
Projects *ProjectService
SshKeys *SshKeyService
sling *sling.Sling
Projects *ProjectService
SshKeys *SshKeyService
EnvironmentVariables *EnvironmentVariableService
}

func NewClient(authToken string) *Client {
Expand All @@ -19,8 +20,9 @@ func NewClient(authToken string) *Client {
base := sling.New().Client(client).Base("https://circleci.com/api/v1.1/").Set("Content-Type", "application/json")

return &Client{
sling: base,
Projects: newProjectService(base, authToken),
SshKeys: newSshKeyService(base, authToken),
sling: base,
Projects: newProjectService(base, authToken),
SshKeys: newSshKeyService(base, authToken),
EnvironmentVariables: newEnvironmentVariableService(base, authToken),
}
}
113 changes: 113 additions & 0 deletions circleci/circleci-go/environmentVariable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package circlecigo

import (
"encoding/json"
"fmt"
"github.com/dghubble/sling"
"net/http"
)

type EnvironmentVariableService struct {
sling *sling.Sling
token string
}

func newEnvironmentVariableService(sling *sling.Sling, token string) *EnvironmentVariableService {
return &EnvironmentVariableService{
sling: sling.New(),
token: token,
}
}

// -----------------------------------------------------------------------------
// Input
// -----------------------------------------------------------------------------

type EnvironmentVariable struct {
ProjectId string
Name string
Value string
ValueMasked string
}

type environmentVariableInputCreate struct {
Name string `json:"name"`
Value string `json:"value"`
}

type environmentVariableResponseType struct {
Name string `json:"name"`
ValueMasked string `json:"value"`
}

// -----------------------------------------------------------------------------
// CRUD
// -----------------------------------------------------------------------------

func (service *EnvironmentVariableService) Create(environmentVariable *EnvironmentVariable) (*EnvironmentVariable, *http.Response, error) {
if environmentVariable == nil {
return nil, nil, fmt.Errorf("Cannot create an environment variable that is nil")
}

requestBody := &environmentVariableInputCreate{Name: environmentVariable.Name, Value: environmentVariable.Value}

environmentVariableResponse := new(environmentVariableResponseType)
genericError := new(json.RawMessage)
resp, err := service.sling.New().Set("Accept", "application/json").Post("project/"+environmentVariable.ProjectId+"/envvar?circle-token="+service.token).BodyJSON(requestBody).Receive(environmentVariableResponse, genericError)
if err != nil {
return nil, nil, err
}

finalErr := relevantErrorFromStatusCode(resp, err)
var responseEnvironmentVariable *EnvironmentVariable = nil
if finalErr == nil {
responseEnvironmentVariable = &EnvironmentVariable{
ProjectId: environmentVariable.ProjectId,
Name: environmentVariableResponse.Name,
Value: environmentVariable.Value,
ValueMasked: environmentVariableResponse.ValueMasked,
}
}

return responseEnvironmentVariable, resp, finalErr
}

func (service *EnvironmentVariableService) Read(environmentVariable *EnvironmentVariable) (*EnvironmentVariable, *http.Response, error) {
if environmentVariable == nil {
return nil, nil, fmt.Errorf("Cannot read an environment variable that is nil")
}

environmentVariableResponse := new(environmentVariableResponseType)
genericError := new(json.RawMessage)
resp, err := service.sling.New().Set("Accept", "application/json").Get("project/"+environmentVariable.ProjectId+"/envvar/"+environmentVariable.Name+"?circle-token="+service.token).Receive(environmentVariableResponse, genericError)
if err != nil {
return nil, nil, err
}

finalErr := relevantErrorFromStatusCode(resp, err)
var responseEnvironmentVariable *EnvironmentVariable = nil
if finalErr == nil {
responseEnvironmentVariable = &EnvironmentVariable{
ProjectId: environmentVariable.ProjectId,
Name: environmentVariableResponse.Name,
Value: environmentVariable.Value,
ValueMasked: environmentVariableResponse.ValueMasked,
}
}

return responseEnvironmentVariable, resp, finalErr
}

func (service *EnvironmentVariableService) Delete(environmentVariable *EnvironmentVariable) (*http.Response, error) {
if environmentVariable == nil {
return nil, fmt.Errorf("Cannot delete an environment variable that is nil")
}

req, reqErr := service.sling.New().Delete("project/" + environmentVariable.ProjectId + "/envvar/" + environmentVariable.Name + "?circle-token=" + service.token).Request()
if reqErr != nil {
return nil, reqErr
}
resp, httpErr := http.DefaultClient.Do(req)

return resp, relevantErrorFromStatusCode(resp, httpErr)
}
5 changes: 3 additions & 2 deletions circleci/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"circleci_project": resourceCircleCiProject(),
"circleci_ssh_key": resourceCircleCiSshKey(),
"circleci_project": resourceCircleCiProject(),
"circleci_ssh_key": resourceCircleCiSshKey(),
"circleci_environment_variable": resourceCircleCiEnvironmentVariable(),
},

ConfigureFunc: providerConfigure,
Expand Down
102 changes: 102 additions & 0 deletions circleci/resource_circleci_environment_variable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package circleci

import (
"fmt"
circleci "github.com/edahlseng/terraform-provider-circleci/circleci/circleci-go"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceCircleCiEnvironmentVariable() *schema.Resource {
return &schema.Resource{
Create: resourceCircleCiEnvironmentVariableCreate,
Read: resourceCircleCiEnvironmentVariableRead,
Delete: resourceCircleCiEnvironmentVariableDelete,

Schema: map[string]*schema.Schema{
"project_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"value": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Sensitive: true,
},
},
}
}

func resourceCircleCiEnvironmentVariableCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*circleci.Client)

environmentVariableInput := &circleci.EnvironmentVariable{
ProjectId: d.Get("project_id").(string),
Name: d.Get("name").(string),
Value: d.Get("value").(string),
}

environmentVariable, _, err := client.EnvironmentVariables.Create(environmentVariableInput)
if err != nil {
return fmt.Errorf("Error creating CircleCI environment variable: %s", err)
}

if environmentVariable == nil {
return fmt.Errorf("Error creating CircleCI environment variable: environment variable is nil")
}

d.SetId(fmt.Sprintf("%s/%s", environmentVariable.ProjectId, environmentVariable.Name))
d.Set("project_id", environmentVariable.ProjectId)
d.Set("name", environmentVariable.Name)
d.Set("value", environmentVariable.Value)
d.Set("value_masked", environmentVariable.ValueMasked)

return nil
}

func resourceCircleCiEnvironmentVariableRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*circleci.Client)

environmentVariableInput := &circleci.EnvironmentVariable{
ProjectId: d.Get("project_id").(string),
Name: d.Get("name").(string),
}

environmentVariable, _, err := client.EnvironmentVariables.Read(environmentVariableInput)
if err != nil {
return fmt.Errorf("Error reading CircleCI environment variable: %s", err)
}

if environmentVariable == nil {
return fmt.Errorf("Error creating CircleCI environment variable: environment variable is nil")
}

d.Set("name", environmentVariable.Name)
d.Set("value_masked", environmentVariable.ValueMasked)

return nil
}

func resourceCircleCiEnvironmentVariableDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*circleci.Client)

environmentVariableInput := &circleci.EnvironmentVariable{
ProjectId: d.Get("project_id").(string),
Name: d.Get("name").(string),
Value: "",
ValueMasked: "",
}

_, err := client.EnvironmentVariables.Delete(environmentVariableInput)
if err != nil {
return fmt.Errorf("Error deleting environment variable: %s", err)
}

return nil
}
Loading

0 comments on commit fae99f8

Please sign in to comment.