diff --git a/client/context.go b/client/context.go new file mode 100644 index 00000000..c28c2790 --- /dev/null +++ b/client/context.go @@ -0,0 +1,132 @@ +package client + +import ( + "fmt" + "log" + "net/url" +) + +type ContextErrorResponse struct { + Status int `json:"status,omitempty"` + Message string `json:"message,omitempty"` + Error string `json:"error,omitempty"` +} + +type ContextMetadata struct { + Name string `json:"name,omitempty"` +} + +type Context struct { + Metadata ContextMetadata `json:"metadata,omitempty"` + Spec ContextSpec `json:"spec,omitempty"` + Version string `json:"version,omitempty"` +} + +type ContextSpec struct { + Type string `json:"type,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` +} + +func (context *Context) GetID() string { + return context.Metadata.Name +} + +func (client *Client) GetContext(name string) (*Context, error) { + fullPath := fmt.Sprintf("/contexts/%s?decrypt=true", url.PathEscape(name)) + opts := RequestOptions{ + Path: fullPath, + Method: "GET", + } + + resp, err := client.RequestAPI(&opts) + + if err != nil { + return nil, err + } + var respContext Context + err = DecodeResponseInto(resp, &respContext) + if err != nil { + return nil, err + } + + return &respContext, nil + +} + +func (client *Client) CreateContext(context *Context) (*Context, error) { + + body, err := EncodeToJSON(context) + + if err != nil { + return nil, err + } + opts := RequestOptions{ + Path: "/contexts", + Method: "POST", + Body: body, + } + + resp, err := client.RequestAPI(&opts) + log.Printf("[DEBUG] Called API for context with Body %v", body) + if err != nil { + log.Printf("[DEBUG] Call to API for context creation failed with Error = %v for Body %v", err, body) + return nil, err + } + + var respContext Context + err = DecodeResponseInto(resp, &respContext) + if err != nil { + return nil, err + } + + return &respContext, nil + +} + +func (client *Client) UpdateContext(context *Context) (*Context, error) { + + body, err := EncodeToJSON(context) + + if err != nil { + return nil, err + } + + fullPath := fmt.Sprintf("/contexts/%s", url.PathEscape(context.Metadata.Name)) + opts := RequestOptions{ + Path: fullPath, + Method: "PUT", + Body: body, + } + + resp, err := client.RequestAPI(&opts) + + if err != nil { + return nil, err + } + + var respContext Context + err = DecodeResponseInto(resp, &respContext) + if err != nil { + return nil, err + } + + return &respContext, nil + +} + +func (client *Client) DeleteContext(name string) error { + + fullPath := fmt.Sprintf("/contexts/%s", url.PathEscape(name)) + opts := RequestOptions{ + Path: fullPath, + Method: "DELETE", + } + + _, err := client.RequestAPI(&opts) + + if err != nil { + return err + } + + return nil +} diff --git a/codefresh/data_context.go b/codefresh/data_context.go new file mode 100644 index 00000000..327f9971 --- /dev/null +++ b/codefresh/data_context.go @@ -0,0 +1,69 @@ +package codefresh + +import ( + "fmt" + + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/ghodss/yaml" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceContext() *schema.Resource { + return &schema.Resource{ + Read: dataSourceContextRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "data": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceContextRead(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + var context *cfClient.Context + var err error + + if name, nameOk := d.GetOk("name"); nameOk { + context, err = client.GetContext(name.(string)) + } else { + return fmt.Errorf("data.codefresh_context - must specify name") + } + if err != nil { + return err + } + + if context == nil { + return fmt.Errorf("data.codefresh_context - cannot find context") + } + + return mapDataContextToResource(context, d) +} + +func mapDataContextToResource(context *cfClient.Context, d *schema.ResourceData) error { + + if context == nil || context.Metadata.Name == "" { + return fmt.Errorf("data.codefresh_context - failed to mapDataContextToResource") + } + d.SetId(context.Metadata.Name) + + d.Set("name", context.Metadata.Name) + d.Set("type", context.Spec.Type) + data, err := yaml.Marshal(context.Spec.Data) + if err != nil { + return err + } + d.Set("data", string(data)) + + return nil +} diff --git a/codefresh/provider.go b/codefresh/provider.go index beab329b..d34b124f 100644 --- a/codefresh/provider.go +++ b/codefresh/provider.go @@ -3,6 +3,7 @@ package codefresh import ( cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + //"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "os" ) @@ -32,10 +33,12 @@ func Provider() *schema.Provider { "codefresh_account": dataSourceAccount(), "codefresh_team": dataSourceTeam(), "codefresh_current_account": dataSourceCurrentAccount(), + "codefresh_context": dataSourceContext(), }, ResourcesMap: map[string]*schema.Resource{ "codefresh_project": resourceProject(), "codefresh_pipeline": resourcePipeline(), + "codefresh_context": resourceContext(), "codefresh_team": resourceTeam(), "codefresh_account": resourceAccount(), "codefresh_api_key": resourceApiKey(), diff --git a/codefresh/resource_context.go b/codefresh/resource_context.go new file mode 100644 index 00000000..0754ec24 --- /dev/null +++ b/codefresh/resource_context.go @@ -0,0 +1,301 @@ +package codefresh + +import ( + "log" + + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/ghodss/yaml" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + contextConfig = "config" + contextSecret = "secret" + contextYaml = "yaml" + contextSecretYaml = "secret-yaml" +) + +var supportedContextType = []string{ + contextConfig, + contextSecret, + contextYaml, + contextSecretYaml, +} + +func getConflictingContexts(context string) []string { + var conflictingTypes []string + normalizedContext := normalizeFieldName(context) + for _, value := range supportedContextType { + normlizedValue := normalizeFieldName(value) + if normlizedValue != normalizedContext { + conflictingTypes = append(conflictingTypes, "spec.0."+normlizedValue) + } + } + return conflictingTypes +} + +func resourceContext() *schema.Resource { + return &schema.Resource{ + Create: resourceContextCreate, + Read: resourceContextRead, + Update: resourceContextUpdate, + Delete: resourceContextDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "spec": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + normalizeFieldName(contextConfig): { + Type: schema.TypeList, + ForceNew: true, + Optional: true, + MaxItems: 1, + ConflictsWith: getConflictingContexts(contextConfig), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data": { + Type: schema.TypeMap, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + normalizeFieldName(contextSecret): { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + ConflictsWith: getConflictingContexts(contextSecret), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data": { + Type: schema.TypeMap, + Required: true, + Sensitive: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + normalizeFieldName(contextYaml): { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + ConflictsWith: getConflictingContexts(contextYaml), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data": { + Type: schema.TypeString, + Required: true, + ValidateFunc: stringIsYaml, + DiffSuppressFunc: suppressEquivalentYamlDiffs, + StateFunc: func(v interface{}) string { + template, _ := normalizeYamlString(v) + return template + }, + }, + }, + }, + }, + normalizeFieldName(contextSecretYaml): { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + ConflictsWith: getConflictingContexts(contextSecretYaml), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "data": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: stringIsYaml, + DiffSuppressFunc: suppressEquivalentYamlDiffs, + StateFunc: func(v interface{}) string { + template, _ := normalizeYamlString(v) + return template + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func resourceContextCreate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + context := *mapResourceToContext(d) + resp, err := client.CreateContext(&context) + if err != nil { + log.Printf("[DEBUG] Error while creating context. Error = %v", err) + return err + } + + d.SetId(resp.Metadata.Name) + return resourceContextRead(d, meta) +} + +func resourceContextRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cfClient.Client) + + contextName := d.Id() + + if contextName == "" { + d.SetId("") + return nil + } + + context, err := client.GetContext(contextName) + if err != nil { + log.Printf("[DEBUG] Error while getting context. Error = %v", contextName) + return err + } + + err = mapContextToResource(*context, d) + if err != nil { + log.Printf("[DEBUG] Error while mapping context to resource. Error = %v", err) + return err + } + + return nil +} + +func resourceContextUpdate(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + + context := *mapResourceToContext(d) + context.Metadata.Name = d.Id() + + _, err := client.UpdateContext(&context) + if err != nil { + log.Printf("[DEBUG] Error while updating context. Error = %v", err) + return err + } + + return resourceContextRead(d, meta) +} + +func resourceContextDelete(d *schema.ResourceData, meta interface{}) error { + + client := meta.(*cfClient.Client) + + err := client.DeleteContext(d.Id()) + if err != nil { + return err + } + + return nil +} + +func mapContextToResource(context cfClient.Context, d *schema.ResourceData) error { + + err := d.Set("name", context.Metadata.Name) + if err != nil { + return err + } + + err = d.Set("spec", flattenContextSpec(context.Spec)) + if err != nil { + log.Printf("[DEBUG] Failed to flatten Context spec = %v", context.Spec) + return err + } + + if err != nil { + return err + } + + return nil +} + +func flattenContextSpec(spec cfClient.ContextSpec) []interface{} { + + var res = make([]interface{}, 0) + m := make(map[string]interface{}) + + switch currentContextType := spec.Type; currentContextType { + case contextConfig, contextSecret: + m[normalizeFieldName(currentContextType)] = flattenContextConfig(spec) + case contextYaml, contextSecretYaml: + m[normalizeFieldName(currentContextType)] = flattenContextYaml(spec) + default: + log.Printf("[DEBUG] Invalid context type = %v", currentContextType) + return nil + } + + res = append(res, m) + return res +} + +func flattenContextConfig(spec cfClient.ContextSpec) []interface{} { + var res = make([]interface{}, 0) + m := make(map[string]interface{}) + m["data"] = spec.Data + res = append(res, m) + return res +} + +func flattenContextYaml(spec cfClient.ContextSpec) []interface{} { + var res = make([]interface{}, 0) + m := make(map[string]interface{}) + data, err := yaml.Marshal(spec.Data) + if err != nil { + return nil + } + m["data"] = string(data) + res = append(res, m) + return res +} + +func mapResourceToContext(d *schema.ResourceData) *cfClient.Context { + + var normalizedContextType string + var normalizedContextData map[string]interface{} + + if data, ok := d.GetOk("spec.0." + normalizeFieldName(contextConfig) + ".0.data"); ok { + normalizedContextType = contextConfig + normalizedContextData = data.(map[string]interface{}) + } else if data, ok := d.GetOk("spec.0." + normalizeFieldName(contextSecret) + ".0.data"); ok { + normalizedContextType = contextSecret + normalizedContextData = data.(map[string]interface{}) + } else if data, ok := d.GetOk("spec.0." + normalizeFieldName(contextYaml) + ".0.data"); ok { + normalizedContextType = contextYaml + yaml.Unmarshal([]byte(data.(string)), &normalizedContextData) + } else if data, ok := d.GetOk("spec.0." + normalizeFieldName(contextSecretYaml) + ".0.data"); ok { + normalizedContextType = contextSecretYaml + yaml.Unmarshal([]byte(data.(string)), &normalizedContextData) + } + + context := &cfClient.Context{ + Metadata: cfClient.ContextMetadata{ + Name: d.Get("name").(string), + }, + Spec: cfClient.ContextSpec{ + Type: normalizedContextType, + Data: normalizedContextData, + }, + } + + return context +} diff --git a/codefresh/resource_context_test.go b/codefresh/resource_context_test.go new file mode 100644 index 00000000..3eb016fe --- /dev/null +++ b/codefresh/resource_context_test.go @@ -0,0 +1,266 @@ +package codefresh + +import ( + "fmt" + "regexp" + "testing" + + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +var contextNamePrefix = "TerraformAccTest_" + +func TestAccCodefreshContextConfigWithCharactersToBeEscaped(t *testing.T) { + name := contextNamePrefix + "cf ctx/test +?#@ special" + acctest.RandString(10) + resourceName := "codefresh_context.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCodefreshContextDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCodefreshContextConfig(name, "config1", "value1", "config2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCodefreshContextExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "spec.0.config.0.data.config1", "value1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.config.0.data.config2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCodefreshContextConfig(t *testing.T) { + name := contextNamePrefix + acctest.RandString(10) + resourceName := "codefresh_context.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCodefreshContextDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCodefreshContextConfig(name, "config1", "value1", "config2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCodefreshContextExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "spec.0.config.0.data.config1", "value1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.config.0.data.config2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCodefreshContextSecret(t *testing.T) { + name := contextNamePrefix + acctest.RandString(10) + resourceName := "codefresh_context.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCodefreshContextDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCodefreshContextSecret(name, "config1", "value1", "config2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCodefreshContextExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "spec.0.secret.0.data.config1", "value1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.secret.0.data.config2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccCodefreshContextYaml(t *testing.T) { + name := contextNamePrefix + acctest.RandString(10) + resourceName := "codefresh_context.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCodefreshContextDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCodefreshContextYaml(name, "rootKey", "plainKey", "plainValue", "listKey", "listValue1", "listValue2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCodefreshContextExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "spec.0.yaml.0.data", "rootKey:\n listKey:\n - listValue1\n - listValue2\n plainKey: plainValue\n"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccCodefreshContextSecretYaml(t *testing.T) { + name := contextNamePrefix + acctest.RandString(10) + resourceName := "codefresh_context.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCodefreshContextDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCodefreshContextSecretYaml(name, "rootKey", "plainKey", "plainValue", "listKey", "listValue1", "listValue2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCodefreshContextExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "spec.0.secretyaml.0.data", "rootKey:\n listKey:\n - listValue1\n - listValue2\n plainKey: plainValue\n"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCodefreshContextExists(resource string) resource.TestCheckFunc { + return func(state *terraform.State) error { + + rs, ok := state.RootModule().Resources[resource] + if !ok { + return fmt.Errorf("Not found: %s", resource) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + contextID := rs.Primary.ID + + apiClient := testAccProvider.Meta().(*cfClient.Client) + _, err := apiClient.GetContext(contextID) + + if err != nil { + return fmt.Errorf("error fetching context with resource %s. %s", resource, err) + } + return nil + } +} + +func testAccCheckCodefreshContextDestroy(s *terraform.State) error { + apiClient := testAccProvider.Meta().(*cfClient.Client) + + for _, rs := range s.RootModule().Resources { + + if rs.Type != "codefresh_context" { + continue + } + + _, err := apiClient.GetContext(rs.Primary.ID) + + if err == nil { + return fmt.Errorf("Alert still exists") + } + + notFoundErr := "CONTEXT_NOT_FOUND_ERROR" + expectedErr := regexp.MustCompile(notFoundErr) + if !expectedErr.Match([]byte(err.Error())) { + return fmt.Errorf("expected %s, got %s", notFoundErr, err) + } + + } + + return nil +} + +// CONFIGS +func testAccCodefreshContextConfig(rName, dataKey1, dataValue1, dataKey2, dataValue2 string) string { + + return fmt.Sprintf(` +resource "codefresh_context" "test" { + + name = "%s" + + spec { + config { + data = { + %q = %q + %q = %q + } + } + } +} +`, rName, dataKey1, dataValue1, dataKey2, dataValue2) +} + +func testAccCodefreshContextSecret(rName, dataKey1, dataValue1, dataKey2, dataValue2 string) string { + + return fmt.Sprintf(` +resource "codefresh_context" "test" { + + name = "%s" + + spec { + secret { + data = { + %q = %q + %q = %q + } + } + } +} +`, rName, dataKey1, dataValue1, dataKey2, dataValue2) +} + +func testAccCodefreshContextYaml(rName, rootKey, plainKey, plainValue, listKey, listValue1, listValue2 string) string { + + return fmt.Sprintf(` +resource "codefresh_context" "test" { + + name = "%s" + + spec { + yaml { + data = "%s: \n %s: %s\n %s: \n - %s\n - %s" + } + } +} +`, rName, rootKey, plainKey, plainValue, listKey, listValue1, listValue2) +} + +func testAccCodefreshContextSecretYaml(rName, rootKey, plainKey, plainValue, listKey, listValue1, listValue2 string) string { + + return fmt.Sprintf(` +resource "codefresh_context" "test" { + + name = "%s" + + spec { + secretyaml { + data = "%s: \n %s: %s\n %s: \n - %s\n - %s" + } + } +} +`, rName, rootKey, plainKey, plainValue, listKey, listValue1, listValue2) +} diff --git a/codefresh/utils.go b/codefresh/utils.go index 69f2eb94..bd75e772 100644 --- a/codefresh/utils.go +++ b/codefresh/utils.go @@ -1,7 +1,13 @@ package codefresh import ( + "fmt" + "log" + "regexp" + cfClient "github.com/codefresh-io/terraform-provider-codefresh/client" + "github.com/ghodss/yaml" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func convertStringArr(ifaceArr []interface{}) []string { @@ -34,3 +40,60 @@ func flattenStringArr(sArr []string) []interface{} { } return iArr } + +func stringIsYaml(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + if _, err := normalizeYamlString(v); err != nil { + errors = append(errors, fmt.Errorf("%q contains an invalid YAML: %s", k, err)) + } + + return warnings, errors +} + +func normalizeFieldName(fieldName string) string { + reg, err := regexp.Compile("[^a-z0-9_]+") + if err != nil { + log.Printf("[DEBUG] Unable to compile regexp for field name normalization. Error = %v", err) + } + return reg.ReplaceAllString(fieldName, "") +} + +func normalizeYamlString(yamlString interface{}) (string, error) { + var j map[string]interface{} + + if yamlString == nil || yamlString.(string) == "" { + return "", nil + } + + s := yamlString.(string) + err := yaml.Unmarshal([]byte(s), &j) + if err != nil { + return s, err + } + + bytes, _ := yaml.Marshal(j) + return string(bytes[:]), nil +} + +func suppressEquivalentYamlDiffs(k, old, new string, d *schema.ResourceData) bool { + normalizedOld, err := normalizeYamlString(old) + + if err != nil { + log.Printf("[ERROR] Unable to normalize data body: %s", err) + return false + } + + normalizedNew, err := normalizeYamlString(new) + + if err != nil { + log.Printf("[ERROR] Unable to normalize data body: %s", err) + return false + } + + return normalizedOld == normalizedNew +} diff --git a/docs/data/context.md b/docs/data/context.md new file mode 100644 index 00000000..e4d9e573 --- /dev/null +++ b/docs/data/context.md @@ -0,0 +1,53 @@ +# Data Source: codefresh_context +This data source allows to retrieve information on any defined context + +## Example Usage + +```hcl +# Assuming runtimes-list is a context of type "config" with the following values +# runtime_a: dev +# runtime_b: test +# runtime_c: prod + +data "codefresh_context" "runtimes_list" { + name = "runtimes-list" +} + +resource "codefresh_project" "test" { + name = "myproject" +} + +resource "codefresh_pipeline" "test" { + + lifecycle { + ignore_changes = [ + revision + ] + } + + name = "${codefresh_project.test.name}/react-sample-app" + + runtime_environment { + name = yamldecode(data.codefresh_context.runtimes_list.data).runtime_a + } + + spec { + + spec_template { + repo = "codefresh-contrib/react-sample-app" + path = "./codefresh.yml" + revision = "master" + context = "git" + } + } +} +``` + +## Argument Reference + +* `name` - (Required) Name of the context to be retrived + +## Attributes Reference + +* `type` - String identifying the type of extracted context. E.g. `config`, `secret`, `git.github-app`, etc. +* `data` - The yaml string representing the context. Use the `yamldecode` function to access the values belonging the context. diff --git a/docs/resources/context.md b/docs/resources/context.md new file mode 100644 index 00000000..4efb6399 --- /dev/null +++ b/docs/resources/context.md @@ -0,0 +1,119 @@ +# Context Resource +A Context is an authentication/configuration that is used by Codefresh system and engine. +There are multiple types of context available in Codefresh but they all have the following main components to define them: +* Name: A unique identifier for the context +* Type: A string representing the type of context +* Data: A data structure that provide the information related to the Context. This differs based on the type of context selected +For more details of the Context spec see in the [CLI official documentation](https://codefresh-io.github.io/cli/contexts/spec/) + +## Supported types +Currently the provider support the following types of Context: +* config (Shared Config ) +* secret (Shared Secret) +* yaml (YAML Configuration Context) +* secret-yaml (Secret YAML Configuration Context) + +### Shared Configuration +A Shared Configuration is the entity in Codefresh that allow to create values in a central place that can then be consumed in pipelines to keep them DRY. +More details in the official [Shared Configuration documentation](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/shared-configuration/) + +#### Example Usage of config (Shared Config) +```hcl +resource "codefresh_context" "test-config" { + name = "my-shared-config" + spec { + config { + data = { + var1 = "value1" + var2 = "value2" + } + } + } +} +``` + +#### Example Usage of secret (Shared Secret) +```hcl +resource "codefresh_context" "test-secret" { + name = "my-shared-secret" + spec { + secret { + data = { + var1 = "value1" + var2 = "value2" + } + } + } +} +``` + +#### Example Usage of yaml (YAML Configuration Context) +```hcl +resource "codefresh_context" "test-yaml" { + name = "my-shared-yaml" + spec { + # NOTE: you can also load the yaml from a file with `yaml = file("PATH-TO-FILE.yaml")` + yaml = <