Skip to content

Commit e11b56a

Browse files
chidambaram27onematchfoxthe-technat
authored
Add support for Kustomize patches to argocd_application (#360)
* chore: tried force push same feat with signoff Signed-off-by: chidambaram27 <[email protected]> * chore: tried force push same feat with signoff Signed-off-by: chidambaram27 <[email protected]> * WIP on feature/kustomize-patches Signed-off-by: Nathanael Liechti <[email protected]> * fix(kustomize-patches): make target required Signed-off-by: Nathanael Liechti <[email protected]> * fix: remove test-patch without target Signed-off-by: Nathanael Liechti <[email protected]> --------- Signed-off-by: chidambaram27 <[email protected]> Signed-off-by: Nathanael Liechti <[email protected]> Co-authored-by: OneMatchFox <[email protected]> Co-authored-by: Nathanael Liechti <[email protected]>
1 parent 173c893 commit e11b56a

File tree

7 files changed

+1767
-9
lines changed

7 files changed

+1767
-9
lines changed

argocd/resource_argocd_application_test.go

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,69 @@ func TestAccArgoCDApplication_Kustomize(t *testing.T) {
212212
})
213213
}
214214

215+
func TestAccArgoCDApplication_KustomizePatches(t *testing.T) {
216+
resource.ParallelTest(t, resource.TestCase{
217+
PreCheck: func() { testAccPreCheck(t); testAccPreCheckFeatureSupported(t, features.ApplicationKustomizePatches) },
218+
ProviderFactories: testAccProviders,
219+
Steps: []resource.TestStep{
220+
{
221+
Config: testAccArgoCDApplicationKustomizePatches(
222+
acctest.RandomWithPrefix("test-acc")),
223+
Check: resource.ComposeTestCheckFunc(
224+
resource.TestCheckResourceAttrSet(
225+
"argocd_application.kustomize_patches",
226+
"metadata.0.uid",
227+
),
228+
resource.TestCheckResourceAttr(
229+
"argocd_application.kustomize_patches",
230+
"spec.0.source.0.target_revision",
231+
"release-kustomize-v3.7",
232+
),
233+
resource.TestCheckResourceAttr(
234+
"argocd_application.kustomize_patches",
235+
"spec.0.source.0.kustomize.0.name_suffix",
236+
"-bar",
237+
),
238+
resource.TestCheckResourceAttrSet(
239+
"argocd_application.kustomize_patches",
240+
"spec.0.source.0.kustomize.0.patches.0.patch",
241+
),
242+
resource.TestCheckResourceAttr(
243+
"argocd_application.kustomize_patches",
244+
"spec.0.source.0.kustomize.0.patches.0.target.0.group",
245+
"apps",
246+
),
247+
resource.TestCheckResourceAttr(
248+
"argocd_application.kustomize_patches",
249+
"spec.0.source.0.kustomize.0.patches.0.target.0.version",
250+
"v1",
251+
),
252+
resource.TestCheckResourceAttr(
253+
"argocd_application.kustomize_patches",
254+
"spec.0.source.0.kustomize.0.patches.0.target.0.kind",
255+
"Deployment",
256+
),
257+
resource.TestCheckResourceAttrSet(
258+
"argocd_application.kustomize_patches",
259+
"spec.0.source.0.kustomize.0.patches.1.patch",
260+
),
261+
resource.TestCheckResourceAttr(
262+
"argocd_application.kustomize_patches",
263+
"spec.0.source.0.kustomize.0.patches.1.target.0.label_selector",
264+
"app=hello",
265+
),
266+
),
267+
},
268+
{
269+
ResourceName: "argocd_application.kustomize_patches",
270+
ImportState: true,
271+
ImportStateVerify: true,
272+
ImportStateVerifyIgnore: []string{"wait", "cascade", "metadata.0.generation", "metadata.0.resource_version", "status", "validate"},
273+
},
274+
},
275+
})
276+
}
277+
215278
func TestAccArgoCDApplication_IgnoreDifferences(t *testing.T) {
216279
resource.ParallelTest(t, resource.TestCase{
217280
PreCheck: func() { testAccPreCheck(t) },
@@ -1293,13 +1356,13 @@ resource "argocd_application" "helm_file_parameters" {
12931356
func testAccArgoCDApplicationKustomize(name string, path string, validate bool) string {
12941357
return fmt.Sprintf(`
12951358
resource "argocd_application" "kustomize" {
1296-
metadata {
1297-
name = "%s"
1298-
namespace = "argocd"
1299-
labels = {
1300-
acceptance = "true"
1301-
}
1302-
}
1359+
metadata {
1360+
name = "%s"
1361+
namespace = "argocd"
1362+
labels = {
1363+
acceptance = "true"
1364+
}
1365+
}
13031366
13041367
spec {
13051368
source {
@@ -1330,9 +1393,75 @@ resource "argocd_application" "kustomize" {
13301393
}
13311394
13321395
validate = %t
1396+
}
1397+
`, name, path, validate)
1398+
}
13331399

1400+
func testAccArgoCDApplicationKustomizePatches(name string) string {
1401+
return fmt.Sprintf(`
1402+
resource "argocd_application" "kustomize_patches" {
1403+
metadata {
1404+
name = "%s"
1405+
namespace = "argocd"
1406+
labels = {
1407+
acceptance = "true"
1408+
}
1409+
}
1410+
1411+
spec {
1412+
source {
1413+
repo_url = "https://github.com/kubernetes-sigs/kustomize"
1414+
path = "examples/helloWorld"
1415+
target_revision = "release-kustomize-v3.7"
1416+
kustomize {
1417+
name_prefix = "foo-"
1418+
name_suffix = "-bar"
1419+
images = [
1420+
"foo=hashicorp/terraform:light",
1421+
]
1422+
common_labels = {
1423+
"this.is.a.common" = "la-bel"
1424+
"another.io/one" = "true"
1425+
}
1426+
common_annotations = {
1427+
"this.is.a.common" = "anno-tation"
1428+
"another.io/one" = "false"
1429+
}
1430+
1431+
patches {
1432+
target {
1433+
group = "apps"
1434+
version = "v1"
1435+
kind = "Deployment"
1436+
}
1437+
1438+
patch = <<-EOT
1439+
- op: add
1440+
path: /spec/template/metadata/labels/app.kubernetes.io~1version
1441+
value: 1.21.0
1442+
EOT
1443+
}
1444+
patches {
1445+
target {
1446+
label_selector = "app=hello"
1447+
}
1448+
1449+
patch = <<-EOT
1450+
- op: add
1451+
path: /spec/template/spec/containers/0/image
1452+
value: nginx:1.21.0
1453+
EOT
1454+
}
1455+
}
1456+
}
1457+
1458+
destination {
1459+
server = "https://kubernetes.default.svc"
1460+
namespace = "default"
1461+
}
1462+
}
13341463
}
1335-
`, name, path, validate)
1464+
`, name)
13361465
}
13371466

13381467
func testAccArgoCDApplicationDirectoryNoPath(name string) string {

argocd/schema_application.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,78 @@ func applicationSpecSchemaV4(allOptional bool) *schema.Schema {
14411441
Elem: &schema.Schema{Type: schema.TypeString},
14421442
ValidateFunc: validateMetadataAnnotations,
14431443
},
1444+
"patches": {
1445+
Type: schema.TypeList,
1446+
Description: "A list of [Kustomize patches](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/) to apply.",
1447+
Optional: true,
1448+
Elem: &schema.Resource{
1449+
Schema: map[string]*schema.Schema{
1450+
"target": {
1451+
Type: schema.TypeList,
1452+
Description: "Target(s) to patch",
1453+
Required: true,
1454+
MaxItems: 1,
1455+
Elem: &schema.Resource{
1456+
Schema: map[string]*schema.Schema{
1457+
"group": {
1458+
Type: schema.TypeString,
1459+
Description: "The Kubernetes resource Group to match for.",
1460+
Optional: true,
1461+
},
1462+
"kind": {
1463+
Type: schema.TypeString,
1464+
Description: "The Kubernetes resource Kind to match for.",
1465+
Optional: true,
1466+
},
1467+
"name": {
1468+
Type: schema.TypeString,
1469+
Description: "The Kubernetes resource Name to match for.",
1470+
Optional: true,
1471+
},
1472+
"namespace": {
1473+
Type: schema.TypeString,
1474+
Description: "The Kubernetes resource Namespace to match for.",
1475+
Optional: true,
1476+
},
1477+
"label_selector": {
1478+
Type: schema.TypeString,
1479+
Description: "Label selector to use when matching the Kubernetes resource.",
1480+
Optional: true,
1481+
},
1482+
"annotation_selector": {
1483+
Type: schema.TypeString,
1484+
Description: "Annotation selector to use when matching the Kubernetes resource.",
1485+
Optional: true,
1486+
},
1487+
"version": {
1488+
Type: schema.TypeString,
1489+
Description: "The Kubernetes resource Version to match for.",
1490+
Optional: true,
1491+
},
1492+
},
1493+
},
1494+
},
1495+
"patch": {
1496+
Type: schema.TypeString,
1497+
Description: "Inline Kustomize patch to apply.",
1498+
Optional: true,
1499+
Elem: &schema.Schema{Type: schema.TypeString},
1500+
},
1501+
"path": {
1502+
Type: schema.TypeString,
1503+
Description: "Path to a file containing the patch to apply.",
1504+
Optional: true,
1505+
Elem: &schema.Schema{Type: schema.TypeString},
1506+
},
1507+
"options": {
1508+
Type: schema.TypeMap,
1509+
Description: "Additional [options](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/#name-and-kind-changes).",
1510+
Optional: true,
1511+
Elem: &schema.Schema{Type: schema.TypeBool},
1512+
},
1513+
},
1514+
},
1515+
},
14441516
},
14451517
},
14461518
},

argocd/structure_application.go

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,71 @@ func expandApplicationSourceKustomize(in []interface{}) *application.Application
234234
}
235235
}
236236

237+
if patches, ok := a["patches"]; ok {
238+
for _, v := range patches.([]interface{}) {
239+
patchMap := v.(map[string]interface{})
240+
kustomizePatch := application.KustomizePatch{}
241+
242+
if patch, ok := patchMap["patch"]; ok {
243+
kustomizePatch.Patch = patch.(string)
244+
}
245+
246+
if target, ok := patchMap["target"]; ok {
247+
kustomizePatch.Target = expandApplicationSourceKustomizePatchTarget(target.([]interface{}))
248+
}
249+
250+
if options, ok := patchMap["options"]; ok {
251+
kustomizePatch.Options = expandBoolMap(options.(map[string]interface{}))
252+
}
253+
254+
result.Patches = append(result.Patches, kustomizePatch)
255+
}
256+
}
257+
258+
return result
259+
}
260+
261+
func expandApplicationSourceKustomizePatchTarget(in []interface{}) *application.KustomizeSelector {
262+
if len(in) == 0 {
263+
return nil
264+
}
265+
266+
result := &application.KustomizeSelector{
267+
KustomizeResId: application.KustomizeResId{
268+
KustomizeGvk: application.KustomizeGvk{},
269+
},
270+
}
271+
272+
t := in[0].(map[string]interface{})
273+
274+
if group, ok := t["group"]; ok {
275+
result.KustomizeResId.KustomizeGvk.Group = group.(string)
276+
}
277+
278+
if version, ok := t["version"]; ok {
279+
result.KustomizeResId.KustomizeGvk.Version = version.(string)
280+
}
281+
282+
if kind, ok := t["kind"]; ok {
283+
result.KustomizeResId.KustomizeGvk.Kind = kind.(string)
284+
}
285+
286+
if name, ok := t["name"]; ok {
287+
result.KustomizeResId.Name = name.(string)
288+
}
289+
290+
if namespace, ok := t["namespace"]; ok {
291+
result.KustomizeResId.Namespace = namespace.(string)
292+
}
293+
294+
if label_selector, ok := t["label_selector"]; ok {
295+
result.LabelSelector = label_selector.(string)
296+
}
297+
298+
if annotation_selector, ok := t["annotation_selector"]; ok {
299+
result.AnnotationSelector = annotation_selector.(string)
300+
}
301+
237302
return result
238303
}
239304

@@ -757,7 +822,37 @@ func flattenApplicationSourceKustomize(as []*application.ApplicationSourceKustom
757822
images = append(images, string(i))
758823
}
759824

825+
var patches []map[string]interface{}
826+
827+
for _, p := range a.Patches {
828+
patch := map[string]interface{}{
829+
"patch": p.Patch,
830+
"path": p.Path,
831+
}
832+
833+
if p.Target != nil {
834+
patch["target"] = []map[string]interface{}{
835+
{
836+
"group": p.Target.KustomizeResId.KustomizeGvk.Group,
837+
"version": p.Target.KustomizeResId.KustomizeGvk.Version,
838+
"kind": p.Target.KustomizeResId.KustomizeGvk.Kind,
839+
"name": p.Target.KustomizeResId.Name,
840+
"namespace": p.Target.KustomizeResId.Namespace,
841+
"label_selector": p.Target.LabelSelector,
842+
"annotation_selector": p.Target.AnnotationSelector,
843+
},
844+
}
845+
}
846+
847+
if p.Options != nil {
848+
patch["options"] = p.Options
849+
}
850+
851+
patches = append(patches, patch)
852+
}
853+
760854
result = append(result, map[string]interface{}{
855+
"patches": patches,
761856
"common_annotations": a.CommonAnnotations,
762857
"common_labels": a.CommonLabels,
763858
"images": images,
@@ -768,7 +863,7 @@ func flattenApplicationSourceKustomize(as []*application.ApplicationSourceKustom
768863
}
769864
}
770865

771-
return
866+
return result
772867
}
773868

774869
func flattenApplicationSourceHelm(as []*application.ApplicationSourceHelm) (result []map[string]interface{}) {

argocd/utils.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ func isKeyInMap(key string, d map[string]interface{}) bool {
4848
return false
4949
}
5050

51+
func expandBoolMap(m map[string]interface{}) map[string]bool {
52+
result := make(map[string]bool)
53+
54+
for k, v := range m {
55+
result[k] = v.(bool)
56+
}
57+
58+
return result
59+
}
60+
5161
func expandStringMap(m map[string]interface{}) map[string]string {
5262
result := make(map[string]string)
5363

0 commit comments

Comments
 (0)