Skip to content

Commit 1877a9d

Browse files
Feat: Add external resources to pipeline spec (#144)
## What Add external resources to pipeline spec ## Why Closes #75 ## Notes <!-- Add any notes here --> ## Checklist * [x] _I have read [CONTRIBUTING.md](https://github.com/codefresh-io/terraform-provider-codefresh/blob/master/CONTRIBUTING.md)._ * [x] _I have [allowed changes to my fork to be made](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)._ * [x] _I have added tests, assuming new tests are warranted_. * [x] _I understand that the `/test` comment will be ignored by the CI trigger [unless it is made by a repo admin or collaborator](https://codefresh.io/docs/docs/pipelines/triggers/git-triggers/#support-for-building-pull-requests-from-forks)._ --------- Co-authored-by: Yonatan Koren <[email protected]>
1 parent e85e923 commit 1877a9d

File tree

4 files changed

+224
-1
lines changed

4 files changed

+224
-1
lines changed

codefresh/cfclient/pipeline.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ type RuntimeEnvironment struct {
9090
RequiredAvailableStorage string `json:"requiredAvailableStorage,omitempty"`
9191
}
9292

93+
type ExternalResource struct {
94+
ID string `json:"id,omitempty"`
95+
Type string `json:"type"`
96+
Source string `json:"source"`
97+
Context string `json:"context"`
98+
Destination string `json:"destination"`
99+
IsFolder bool `json:"isFolder"`
100+
Repo string `json:"repo"`
101+
Revision string `json:"revision"`
102+
}
103+
93104
func (t *Trigger) SetVariables(variables map[string]interface{}, encrypted bool) {
94105
for key, value := range variables {
95106
t.Variables = append(t.Variables, Variable{Key: key, Value: value.(string), Encrypted: encrypted})
@@ -123,6 +134,7 @@ type Spec struct {
123134
Hooks *Hooks `json:"hooks,omitempty"`
124135
Options map[string]bool `json:"options,omitempty"`
125136
PermitRestartFromFailedSteps bool `json:"permitRestartFromFailedSteps,omitempty"`
137+
ExternalResources []ExternalResource `json:"externalResources,omitempty"`
126138
}
127139

128140
type Steps struct {
@@ -149,7 +161,6 @@ func (d Hooks) MarshalJSON() ([]byte, error) {
149161
bytes := []byte(d.Hooks)
150162
return bytes, nil
151163
}
152-
153164
func (d *Steps) UnmarshalJSON(data []byte) error {
154165
d.Steps = string(data)
155166
return nil

codefresh/resource_pipeline.go

+92
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,58 @@ Pipeline concurrency policy: Builds on 'Pending Approval' state should be:
641641
},
642642
},
643643
},
644+
"external_resource": {
645+
Type: schema.TypeList,
646+
Optional: true,
647+
Elem: &schema.Resource{
648+
Schema: map[string]*schema.Schema{
649+
"id" : {
650+
Type: schema.TypeString,
651+
Computed: true,
652+
},
653+
"type" : {
654+
Type: schema.TypeString,
655+
Optional: true,
656+
Description: "Type of the external resource. Currently only 'git' is supported",
657+
ValidateFunc: validation.StringInSlice([]string{
658+
"git",
659+
}, false),
660+
Default: "git",
661+
},
662+
"repo" : {
663+
Type: schema.TypeString,
664+
Required: true,
665+
Description: "git repository url",
666+
},
667+
"context" : {
668+
Type: schema.TypeString,
669+
Required: true,
670+
Description: "Context name for the git repository",
671+
},
672+
"revision": {
673+
Type: schema.TypeString,
674+
Required: true,
675+
Description: "Revision/branch in the git repository",
676+
},
677+
"is_folder": {
678+
Type: schema.TypeBool,
679+
Description: "Whether or not the resource specified in source_path is a folder",
680+
Optional: true,
681+
Default: false,
682+
},
683+
"source_path": {
684+
Type: schema.TypeString,
685+
Description: "The source folder in the repository (use relative path)",
686+
Required: true,
687+
},
688+
"target_path": {
689+
Type: schema.TypeString,
690+
Description: "The target folder in the pipeline workspace where the file/folder will be copied to (use absolute path)",
691+
Required: true,
692+
},
693+
},
694+
},
695+
},
644696
},
645697
},
646698
},
@@ -832,6 +884,10 @@ func flattenSpec(spec cfclient.Spec) []map[string]interface{} {
832884
m["runtime_environment"] = flattenSpecRuntimeEnvironment(spec.RuntimeEnvironment)
833885
}
834886

887+
if len(spec.ExternalResources) > 0 {
888+
m["external_resource"] = flattenExternalResources(spec.ExternalResources)
889+
}
890+
835891
if len(spec.TerminationPolicy) > 0 {
836892
m["termination_policy"] = flattenSpecTerminationPolicy(spec.TerminationPolicy)
837893
}
@@ -988,6 +1044,25 @@ func flattenCronTriggers(cronTriggers []cfclient.CronTrigger) []map[string]inter
9881044
return res
9891045
}
9901046

1047+
func flattenExternalResources(externalResources []cfclient.ExternalResource) []map[string]interface{} {
1048+
var res = make([]map[string]interface{}, len(externalResources))
1049+
for i, externalResource := range externalResources {
1050+
m := make(map[string]interface{})
1051+
m["type"] = externalResource.Type
1052+
m["repo"] = externalResource.Repo
1053+
m["context"] = externalResource.Context
1054+
m["source_path"] = externalResource.Source
1055+
m["target_path"] = externalResource.Destination
1056+
m["revision"] = externalResource.Revision
1057+
m["is_folder"] = externalResource.IsFolder
1058+
m["id"] = externalResource.ID
1059+
1060+
res[i] = m
1061+
}
1062+
1063+
return res
1064+
}
1065+
9911066
func mapResourceToPipeline(d *schema.ResourceData) (*cfclient.Pipeline, error) {
9921067

9931068
tags := d.Get("tags").(*schema.Set).List()
@@ -1146,6 +1221,23 @@ func mapResourceToPipeline(d *schema.ResourceData) (*cfclient.Pipeline, error) {
11461221
}
11471222
}
11481223

1224+
if externalResources, ok := d.GetOk("spec.0.external_resource"); ok {
1225+
for idx := range externalResources.([]interface{}) {
1226+
codefreshExternalResource := cfclient.ExternalResource{
1227+
Type: d.Get(fmt.Sprintf("spec.0.external_resource.%v.type", idx)).(string),
1228+
Repo: d.Get(fmt.Sprintf("spec.0.external_resource.%v.repo", idx)).(string),
1229+
Revision: d.Get(fmt.Sprintf("spec.0.external_resource.%v.revision", idx)).(string),
1230+
Context: d.Get(fmt.Sprintf("spec.0.external_resource.%v.context", idx)).(string),
1231+
Source: d.Get(fmt.Sprintf("spec.0.external_resource.%v.source_path", idx)).(string),
1232+
Destination: d.Get(fmt.Sprintf("spec.0.external_resource.%v.target_path", idx)).(string),
1233+
IsFolder: d.Get(fmt.Sprintf("spec.0.external_resource.%v.is_folder", idx)).(bool),
1234+
ID: d.Get(fmt.Sprintf("spec.0.external_resource.%v.id", idx)).(string),
1235+
}
1236+
1237+
pipeline.Spec.ExternalResources = append(pipeline.Spec.ExternalResources, codefreshExternalResource)
1238+
}
1239+
}
1240+
11491241
var codefreshTerminationPolicy []map[string]interface{}
11501242

11511243
if _, ok := d.GetOk("spec.0.termination_policy.0.on_create_branch"); ok {

codefresh/resource_pipeline_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,61 @@ func TestAccCodefreshPipeline_IsPublic(t *testing.T) {
700700
})
701701
}
702702

703+
func TestAccCodefreshPipeline_ExternalResources(t *testing.T) {
704+
name := pipelineNamePrefix + acctest.RandString(10)
705+
resourceName := "codefresh_pipeline.test"
706+
var pipeline cfclient.Pipeline
707+
708+
resource.ParallelTest(t, resource.TestCase{
709+
PreCheck: func() { testAccPreCheck(t) },
710+
Providers: testAccProviders,
711+
CheckDestroy: testAccCheckCodefreshPipelineDestroy,
712+
Steps: []resource.TestStep{
713+
{
714+
Config: testAccCodefreshPipelineExternalResources(name, "codefresh-contrib/react-sample-app", "./codefresh.yml", "master", "git",
715+
"github", "codefresh-io/external-resources1", "master", "test.py", "/codefresh/volume/test.py",
716+
"github2", "codefresh-io/external-resources2", "main", "test2.py", "/codefresh/volume/test2.py"),
717+
Check: resource.ComposeTestCheckFunc(
718+
testAccCheckCodefreshPipelineExists(resourceName, &pipeline),
719+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.context", "github"),
720+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.repo", "codefresh-io/external-resources1"),
721+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.revision", "master"),
722+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.source_path", "test.py"),
723+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.target_path", "/codefresh/volume/test.py"),
724+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.context", "github2"),
725+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.repo", "codefresh-io/external-resources2"),
726+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.revision", "main"),
727+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.source_path", "test2.py"),
728+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.target_path", "/codefresh/volume/test2.py"),
729+
),
730+
},
731+
{
732+
ResourceName: resourceName,
733+
ImportState: true,
734+
ImportStateVerify: true,
735+
},
736+
{
737+
Config: testAccCodefreshPipelineExternalResources(name, "codefresh-contrib/react-sample-app", "./codefresh.yml", "master", "git",
738+
"github2", "codefresh-io/external-resources2", "main", "test2.py", "/codefresh/volume/test2.py",
739+
"github", "codefresh-io/external-resources1", "master", "test.py", "/codefresh/volume/test.py"),
740+
Check: resource.ComposeTestCheckFunc(
741+
testAccCheckCodefreshPipelineExists(resourceName, &pipeline),
742+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.context", "github"),
743+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.repo", "codefresh-io/external-resources1"),
744+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.revision", "master"),
745+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.source_path", "test.py"),
746+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.1.target_path", "/codefresh/volume/test.py"),
747+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.context", "github2"),
748+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.repo", "codefresh-io/external-resources2"),
749+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.revision", "main"),
750+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.source_path", "test2.py"),
751+
resource.TestCheckResourceAttr(resourceName, "spec.0.external_resource.0.target_path", "/codefresh/volume/test2.py"),
752+
),
753+
},
754+
},
755+
})
756+
}
757+
703758
func TestAccCodefreshPipelineOnCreateBranchIgnoreTrigger(t *testing.T) {
704759
name := pipelineNamePrefix + acctest.RandString(10)
705760
resourceName := "codefresh_pipeline.test"
@@ -1477,3 +1532,46 @@ resource "codefresh_pipeline" "test" {
14771532
}
14781533
`, rName, repo, path, revision, context, isPublic)
14791534
}
1535+
1536+
func testAccCodefreshPipelineExternalResources(rName, repo, path, revision, context, extResource1Context, extResource1Repo, extResource1Revision, extResourse1SourcePath, extResource1DestPath, extResource2Context, extResource2Repo, extResource2Revision, extResourse2SourcePath, extResource2DestPath string) string {
1537+
return fmt.Sprintf(`
1538+
resource "codefresh_pipeline" "test" {
1539+
1540+
lifecycle {
1541+
ignore_changes = [
1542+
revision
1543+
]
1544+
}
1545+
1546+
name = "%s"
1547+
1548+
spec {
1549+
spec_template {
1550+
repo = %q
1551+
path = %q
1552+
revision = %q
1553+
context = %q
1554+
}
1555+
1556+
external_resource {
1557+
context = %q
1558+
repo = %q
1559+
revision = %q
1560+
source_path = %q
1561+
target_path = %q
1562+
}
1563+
1564+
external_resource {
1565+
context = %q
1566+
repo = %q
1567+
revision = %q
1568+
source_path = %q
1569+
target_path = %q
1570+
}
1571+
}
1572+
}
1573+
`,
1574+
rName, repo, path, revision, context,
1575+
extResource1Context, extResource1Repo ,extResource1Revision, extResourse1SourcePath, extResource1DestPath,
1576+
extResource2Context, extResource2Repo ,extResource2Revision, extResourse2SourcePath, extResource2DestPath)
1577+
}

docs/resources/pipeline.md

+22
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ Optional:
129129
- `contexts` (List of String) A list of strings representing the contexts ([shared_configuration](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/shared-configuration/)) to be configured for the pipeline.
130130
- `cron_trigger` (Block List) The pipeline's cron triggers. Conflicts with the deprecated [codefresh_pipeline_cron_trigger](https://registry.terraform.io/providers/codefresh-io/codefresh/latest/docs/resources/pipeline_cron_trigger) resource. (see [below for nested schema](#nestedblock--spec--cron_trigger))
131131
- `encrypted_variables` (Map of String) Pipeline level encrypted variables. Please note that drift will not be detected for encrypted variables
132+
- `external_resource` (Block List) (see [below for nested schema](#nestedblock--spec--external_resource))
132133
- `options` (Block List, Max: 1) The options for the pipeline. (see [below for nested schema](#nestedblock--spec--options))
133134
- `pack_id` (String) SAAS pack (`5cd1746617313f468d669013` for Small; `5cd1746717313f468d669014` for Medium; `5cd1746817313f468d669015` for Large; `5cd1746817313f468d669017` for XL; `5cd1746817313f468d669018` for XXL); `5cd1746817313f468d669020` for 4XL).
134135
- `permit_restart_from_failed_steps` (Boolean) Defines whether it is permitted to restart builds in this pipeline from failed step. Defaults to true
@@ -185,6 +186,27 @@ Optional:
185186

186187

187188

189+
<a id="nestedblock--spec--external_resource"></a>
190+
### Nested Schema for `spec.external_resource`
191+
192+
Required:
193+
194+
- `context` (String) Context name for the git repository
195+
- `repo` (String) git repository url
196+
- `revision` (String) Revision/branch in the git repository
197+
- `source_path` (String) The source folder in the repository (use relative path)
198+
- `target_path` (String) The target folder in the pipeline workspace where the file/folder will be copied to (use absolute path)
199+
200+
Optional:
201+
202+
- `is_folder` (Boolean) Whether or not the resource specified in source_path is a folder
203+
- `type` (String) Type of the external resource. Currently only 'git' is supported
204+
205+
Read-Only:
206+
207+
- `id` (String) The ID of this resource.
208+
209+
188210
<a id="nestedblock--spec--options"></a>
189211
### Nested Schema for `spec.options`
190212

0 commit comments

Comments
 (0)