Skip to content

Commit 23da103

Browse files
unlimitedbitsmorgantebharathkkb
authored
feat!: Add multi-repo support for Config Sync (#872)
* Add multi-repo support for Config Sync * Run make build * increase wait time for configsync api * Update modules/k8s-operator-crd-support/main.tf Co-authored-by: Bharath KKB <[email protected]> * Enhance wait_for_configsync_api step Co-authored-by: Morgante Pell <[email protected]> Co-authored-by: Bharath KKB <[email protected]> Co-authored-by: Morgante Pell <[email protected]>
1 parent 7531f90 commit 23da103

File tree

12 files changed

+231
-16
lines changed

12 files changed

+231
-16
lines changed

modules/acm/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ By default, this module will attempt to download the ACM operator from Google di
4949
| cluster\_name | GCP cluster Name used to reach cluster and which becomes the cluster name in the Config Sync kubernetes custom resource. | `string` | n/a | yes |
5050
| create\_ssh\_key | Controls whether a key will be generated for Git authentication | `bool` | `true` | no |
5151
| enable\_log\_denies | Whether to enable logging of all denies and dryrun failures for ACM Policy Controller. | `bool` | `false` | no |
52+
| enable\_multi\_repo | Whether to use ACM Config Sync [multi-repo mode](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/multi-repo). | `bool` | `false` | no |
5253
| enable\_policy\_controller | Whether to enable the ACM Policy Controller on the cluster | `bool` | `true` | no |
53-
| hierarchy\_controller | Configurations for Hierarchy Controller. See [Hierarchy Controller docs](https://cloud.google.com/anthos-config-management/docs/how-to/installing-hierarchy-controller) for more details | `map` | `null` | no |
54+
| hierarchy\_controller | Configurations for Hierarchy Controller. See [Hierarchy Controller docs](https://cloud.google.com/anthos-config-management/docs/how-to/installing-hierarchy-controller) for more details | `map(any)` | `null` | no |
5455
| install\_template\_library | Whether to install the default Policy Controller template library | `bool` | `true` | no |
5556
| location | GCP location used to reach cluster. | `string` | n/a | yes |
5657
| operator\_path | Path to the operator yaml config. If unset, will download from GCS releases. | `string` | `null` | no |
@@ -62,6 +63,7 @@ By default, this module will attempt to download the ACM operator from Google di
6263
| ssh\_auth\_key | Key for Git authentication. Overrides 'create\_ssh\_key' variable. Can be set using 'file(path/to/file)'-function. | `string` | `null` | no |
6364
| sync\_branch | ACM repo Git branch. If un-set, uses Config Management default. | `string` | `""` | no |
6465
| sync\_repo | ACM Git repo address | `string` | n/a | yes |
66+
| sync\_revision | ACM repo Git revision. If un-set, uses Config Management default. | `string` | `""` | no |
6567
| use\_existing\_context | Use existing kubecontext to auth kube-api. | `bool` | `false` | no |
6668

6769
## Outputs

modules/acm/main.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ module "acm_operator" {
3737
project_id = var.project_id
3838
location = var.location
3939
operator_path = var.operator_path
40+
enable_multi_repo = var.enable_multi_repo
4041
sync_repo = var.sync_repo
4142
sync_branch = var.sync_branch
43+
sync_revision = var.sync_revision
4244
policy_dir = var.policy_dir
4345
cluster_endpoint = var.cluster_endpoint
4446
create_ssh_key = var.create_ssh_key
@@ -56,4 +58,6 @@ module "acm_operator" {
5658
operator_cr_template_path = "${path.module}/templates/acm-config.yml.tpl"
5759
operator_credential_namespace = "config-management-system"
5860
operator_credential_name = "git-creds"
61+
62+
rootsync_cr_template_path = "${path.module}/templates/root-sync.yml.tpl"
5963
}

modules/acm/templates/acm-config.yml.tpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ spec:
99
enabled: ${enable_policy_controller}
1010
templateLibraryInstalled: ${install_template_library}
1111
logDeniesEnabled: ${enable_log_denies}
12+
%{ if enable_multi_repo ~}
13+
enableMultiRepo: true
14+
%{ else ~}
1215
git:
1316
syncRepo: ${sync_repo}
1417
secretType: ${secret_type}
1518
${policy_dir_node}
1619
${sync_branch_node}
20+
${sync_revision_node}
1721
${source_format_node}
22+
%{ endif ~}
1823
${hierarchy_controller_map_node}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: configsync.gke.io/v1beta1
2+
kind: RootSync
3+
metadata:
4+
name: root-sync
5+
namespace: config-management-system
6+
spec:
7+
${source_format_node}
8+
git:
9+
repo: ${sync_repo}
10+
auth: ${secret_type}
11+
${policy_dir_node}
12+
${sync_branch_node}
13+
${sync_revision_node}
14+
${secret_ref_node}

modules/acm/variables.tf

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ variable "operator_path" {
3535
default = null
3636
}
3737

38+
variable "enable_multi_repo" {
39+
description = "Whether to use ACM Config Sync [multi-repo mode](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/multi-repo)."
40+
type = bool
41+
default = false
42+
}
43+
3844
variable "sync_repo" {
3945
description = "ACM Git repo address"
4046
type = string
@@ -46,6 +52,12 @@ variable "sync_branch" {
4652
default = ""
4753
}
4854

55+
variable "sync_revision" {
56+
description = "ACM repo Git revision. If un-set, uses Config Management default."
57+
type = string
58+
default = ""
59+
}
60+
4961
variable "policy_dir" {
5062
description = "Subfolder containing configs in ACM Git repo. If un-set, uses Config Management default."
5163
type = string
@@ -95,7 +107,7 @@ variable "source_format" {
95107

96108
variable "hierarchy_controller" {
97109
description = "Configurations for Hierarchy Controller. See [Hierarchy Controller docs](https://cloud.google.com/anthos-config-management/docs/how-to/installing-hierarchy-controller) for more details"
98-
type = map
110+
type = map(any)
99111
default = null
100112
}
101113

modules/config-sync/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ To deploy this config:
5050
| cluster\_endpoint | Kubernetes cluster endpoint. | `string` | n/a | yes |
5151
| cluster\_name | GCP cluster name used to reach cluster and which becomes the cluster name in the Config Sync kubernetes custom resource. | `string` | n/a | yes |
5252
| create\_ssh\_key | Controls whether a key will be generated for Git authentication | `bool` | `true` | no |
53-
| hierarchy\_controller | Configurations for Hierarchy Controller. See [Hierarchy Controller docs](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/installing-hierarchy-controller) for more details. | `map` | `null` | no |
53+
| enable\_multi\_repo | Whether to use ACM Config Sync [multi-repo mode](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/multi-repo). | `bool` | `false` | no |
54+
| hierarchy\_controller | Configurations for Hierarchy Controller. See [Hierarchy Controller docs](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/installing-hierarchy-controller) for more details. | `map(any)` | `null` | no |
5455
| location | GCP location used to reach cluster. | `string` | n/a | yes |
5556
| operator\_path | Path to the operator yaml config. If unset, will download from GCS releases. | `string` | `null` | no |
5657
| policy\_dir | Subfolder containing configs in ACM Git repo. If un-set, uses Config Management default. | `string` | `""` | no |
@@ -60,6 +61,7 @@ To deploy this config:
6061
| ssh\_auth\_key | Key for Git authentication. Overrides 'create\_ssh\_key' variable. Can be set using 'file(path/to/file)'-function. | `string` | `null` | no |
6162
| sync\_branch | ACM repo Git branch. If un-set, uses Config Management default. | `string` | `""` | no |
6263
| sync\_repo | ACM Git repo address | `string` | n/a | yes |
64+
| sync\_revision | ACM repo Git revision. If un-set, uses Config Management default. | `string` | `""` | no |
6365

6466
## Outputs
6567

modules/config-sync/main.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ module "configsync_operator" {
2222
project_id = var.project_id
2323
location = var.location
2424
operator_path = var.operator_path
25+
enable_multi_repo = var.enable_multi_repo
2526
sync_repo = var.sync_repo
2627
sync_branch = var.sync_branch
28+
sync_revision = var.sync_revision
2729
policy_dir = var.policy_dir
2830
cluster_endpoint = var.cluster_endpoint
2931
create_ssh_key = var.create_ssh_key
@@ -36,4 +38,6 @@ module "configsync_operator" {
3638
operator_cr_template_path = "${path.module}/templates/config-sync-config.yml.tpl"
3739
operator_credential_namespace = "config-management-system"
3840
operator_credential_name = "git-creds"
41+
42+
rootsync_cr_template_path = "${path.module}/templates/root-sync.yml.tpl"
3943
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: configsync.gke.io/v1beta1
2+
kind: RootSync
3+
metadata:
4+
name: root-sync
5+
namespace: config-management-system
6+
spec:
7+
${source_format_node}
8+
git:
9+
repo: ${sync_repo}
10+
auth: ${secret_type}
11+
${policy_dir_node}
12+
${sync_branch_node}
13+
${sync_revision_node}
14+
${secret_ref_node}

modules/config-sync/variables.tf

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ variable "operator_path" {
3535
default = null
3636
}
3737

38+
variable "enable_multi_repo" {
39+
description = "Whether to use ACM Config Sync [multi-repo mode](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/multi-repo)."
40+
type = bool
41+
default = false
42+
}
43+
3844
variable "sync_repo" {
3945
description = "ACM Git repo address"
4046
type = string
@@ -46,6 +52,12 @@ variable "sync_branch" {
4652
default = ""
4753
}
4854

55+
variable "sync_revision" {
56+
description = "ACM repo Git revision. If un-set, uses Config Management default."
57+
type = string
58+
default = ""
59+
}
60+
4961
variable "policy_dir" {
5062
description = "Subfolder containing configs in ACM Git repo. If un-set, uses Config Management default."
5163
type = string
@@ -82,6 +94,6 @@ variable "source_format" {
8294

8395
variable "hierarchy_controller" {
8496
description = "Configurations for Hierarchy Controller. See [Hierarchy Controller docs](https://cloud.google.com/kubernetes-engine/docs/add-on/config-sync/how-to/installing-hierarchy-controller) for more details."
85-
type = map
97+
type = map(any)
8698
default = null
8799
}

modules/k8s-operator-crd-support/main.tf

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,21 @@
1515
*/
1616

1717
locals {
18-
cluster_endpoint = "https://${var.cluster_endpoint}"
19-
private_key = var.create_ssh_key && var.ssh_auth_key == null ? tls_private_key.k8sop_creds[0].private_key_pem : var.ssh_auth_key
20-
k8sop_creds_secret_key = var.secret_type == "cookiefile" ? "cookie_file" : var.secret_type
21-
should_download_manifest = var.operator_path == null ? true : false
22-
manifest_path = local.should_download_manifest ? "${path.root}/.terraform/tmp/${var.project_id}-${var.cluster_name}/config-management-operator.yaml" : var.operator_path
23-
sync_branch_node = var.sync_branch != "" ? format("syncBranch: %s", var.sync_branch) : ""
24-
policy_dir_node = var.policy_dir != "" ? format("policyDir: %s", var.policy_dir) : ""
25-
hierarchy_controller_map_node = var.hierarchy_controller == null ? "" : format("hierarchyController:\n %s", indent(4, replace(yamlencode(var.hierarchy_controller), "/((?:^|\n)[\\s-]*)\"([\\w-]+)\":/", "$1$2:")))
26-
source_format_node = var.source_format != "" ? format("sourceFormat: %s", var.source_format) : ""
27-
append_arg_use_existing_context_for_gatekeeper = var.use_existing_context ? "USE_EXISTING_CONTEXT_ARG" : ""
18+
cluster_endpoint = "https://${var.cluster_endpoint}"
19+
private_key = var.create_ssh_key && var.ssh_auth_key == null ? tls_private_key.k8sop_creds[0].private_key_pem : var.ssh_auth_key
20+
k8sop_creds_secret_key = var.secret_type == "cookiefile" ? "cookie_file" : var.secret_type
21+
should_download_manifest = var.operator_path == null ? true : false
22+
manifest_path = local.should_download_manifest ? "${path.root}/.terraform/tmp/${var.project_id}-${var.cluster_name}/config-management-operator.yaml" : var.operator_path
23+
sync_branch_property = var.enable_multi_repo ? "branch" : "syncBranch"
24+
sync_branch_node = var.sync_branch != "" ? format("%s: %s", local.sync_branch_property, var.sync_branch) : ""
25+
sync_revision_property = var.enable_multi_repo ? "revision" : "syncRev"
26+
sync_revision_node = var.sync_revision != "" ? format("%s: %s", local.sync_revision_property, var.sync_revision) : ""
27+
policy_dir_property = var.enable_multi_repo ? "dir" : "policyDir"
28+
policy_dir_node = var.policy_dir != "" ? format("%s: %s", local.policy_dir_property, var.policy_dir) : ""
29+
secret_ref_node = var.create_ssh_key == true || var.ssh_auth_key != null ? format("secretRef:\n name: %s", var.operator_credential_name) : ""
30+
hierarchy_controller_map_node = var.hierarchy_controller == null ? "" : format("hierarchyController:\n %s", indent(4, replace(yamlencode(var.hierarchy_controller), "/((?:^|\n)[\\s-]*)\"([\\w-]+)\":/", "$1$2:")))
31+
source_format_node = var.source_format != "" ? format("sourceFormat: %s", var.source_format) : ""
32+
append_arg_use_existing_context = var.use_existing_context ? "USE_EXISTING_CONTEXT_ARG" : ""
2833
}
2934

3035
module "k8sop_manifest" {
@@ -82,8 +87,10 @@ data "template_file" "k8sop_config" {
8287
template = file(var.operator_cr_template_path)
8388
vars = {
8489
cluster_name = var.cluster_name
90+
enable_multi_repo = var.enable_multi_repo
8591
sync_repo = var.sync_repo
8692
sync_branch_node = local.sync_branch_node
93+
sync_revision_node = local.sync_revision_node
8794
policy_dir_node = local.policy_dir_node
8895
secret_type = var.create_ssh_key ? "ssh" : var.secret_type
8996
enable_policy_controller = var.enable_policy_controller ? "true" : "false"
@@ -109,6 +116,58 @@ module "k8sop_config" {
109116
kubectl_destroy_command = "kubectl delete -f - <<EOF\n${data.template_file.k8sop_config.rendered}EOF"
110117
}
111118

119+
120+
data "template_file" "rootsync_config" {
121+
122+
template = file(var.rootsync_cr_template_path)
123+
vars = {
124+
sync_repo = var.sync_repo
125+
sync_branch_node = local.sync_branch_node
126+
sync_revision_node = local.sync_revision_node
127+
policy_dir_node = local.policy_dir_node
128+
secret_ref_node = local.secret_ref_node
129+
secret_type = var.create_ssh_key ? "ssh" : var.secret_type
130+
source_format_node = local.source_format_node
131+
}
132+
}
133+
134+
module "wait_for_configsync_api" {
135+
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
136+
version = "~> 2.0.2"
137+
enabled = var.enable_multi_repo
138+
139+
module_depends_on = [module.k8sop_config.wait]
140+
cluster_name = var.cluster_name
141+
project_id = var.project_id
142+
cluster_location = var.location
143+
create_cmd_triggers = {
144+
rootsync = data.template_file.rootsync_config.rendered,
145+
script_sha1 = sha1(file("${path.module}/scripts/wait_for_configsync.sh"))
146+
}
147+
service_account_key_file = var.service_account_key_file
148+
use_existing_context = var.use_existing_context
149+
150+
kubectl_create_command = "${path.module}/scripts/wait_for_configsync.sh ${var.project_id} ${var.cluster_name} ${var.location} ${local.append_arg_use_existing_context}"
151+
kubectl_destroy_command = ""
152+
}
153+
154+
module "rootsync_config" {
155+
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
156+
version = "~> 2.0.2"
157+
enabled = var.enable_multi_repo
158+
159+
module_depends_on = [module.wait_for_configsync_api.wait]
160+
cluster_name = var.cluster_name
161+
project_id = var.project_id
162+
cluster_location = var.location
163+
create_cmd_triggers = { rootsync = data.template_file.rootsync_config.rendered }
164+
service_account_key_file = var.service_account_key_file
165+
use_existing_context = var.use_existing_context
166+
167+
kubectl_create_command = "kubectl apply -f - <<EOF\n${data.template_file.rootsync_config.rendered}EOF"
168+
kubectl_destroy_command = "kubectl delete -f - <<EOF\n${data.template_file.rootsync_config.rendered}EOF"
169+
}
170+
112171
module "wait_for_gatekeeper" {
113172
source = "terraform-google-modules/gcloud/google//modules/kubectl-wrapper"
114173
version = "~> 2.0.2"
@@ -121,6 +180,6 @@ module "wait_for_gatekeeper" {
121180
service_account_key_file = var.service_account_key_file
122181
use_existing_context = var.use_existing_context
123182

124-
kubectl_create_command = "${path.module}/scripts/wait_for_gatekeeper.sh ${var.project_id} ${var.cluster_name} ${var.location} ${local.append_arg_use_existing_context_for_gatekeeper}"
183+
kubectl_create_command = "${path.module}/scripts/wait_for_gatekeeper.sh ${var.project_id} ${var.cluster_name} ${var.location} ${local.append_arg_use_existing_context}"
125184
kubectl_destroy_command = ""
126185
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2018 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
is_configsync_ready() {
17+
# Check if config-management-system namespace exists
18+
kubectl --context "$1" get namespace config-management-system &> /dev/null
19+
export exit_code=$?
20+
while [ ! " ${exit_code} " -eq 0 ]
21+
do
22+
sleep 5
23+
echo -e "Waiting for namespace config-mangement-system in cluster $1 to be created..."
24+
kubectl --context "$1" get namespace config-management-system &> /dev/null
25+
export exit_code=$?
26+
done
27+
echo -e "Namespace config-management-system in cluster $1 created."
28+
29+
# Once namespace is created, check if config-managment pods are ready
30+
kubectl --context "$1" -n config-management-system wait --timeout 60s --for=condition=Ready pod --all &> /dev/null
31+
export exit_code=$?
32+
33+
while [ ! " ${exit_code} " -eq 0 ]
34+
do
35+
sleep 5
36+
echo -e "Waiting for config-management pods in cluster $1 to become ready..."
37+
kubectl --context "$1" -n config-management-system wait --timeout 60s --for=condition=Ready pod --all &> /dev/null
38+
export exit_code=$?
39+
done
40+
41+
echo -e "Config-management pods in cluster $1 are ready."
42+
return
43+
}
44+
45+
if [ "$#" -lt 3 ]; then
46+
>&2 echo "Not all expected arguments set."
47+
exit 1
48+
fi
49+
50+
PROJECT_ID=$1
51+
CLUSTER_NAME=$2
52+
CLUSTER_LOCATION=$3
53+
USE_EXISTING_CONTEXT=$4
54+
55+
# Check if we need to use the current context
56+
if [ -z ${USE_EXISTING_CONTEXT+x} ]; then
57+
# GKE Cluster. Use the GKE cluster context
58+
is_configsync_ready gke_"${PROJECT_ID}"_"${CLUSTER_LOCATION}"_"${CLUSTER_NAME}"
59+
else
60+
echo "USE_EXISTING_CONTEXT variable is set. Using current context to wait for deployment to be ready."
61+
# Get the current context. This can be used for non GKE Clusters
62+
CURRENT_CONTEXT=$(kubectl config current-context)
63+
is_configsync_ready "${CURRENT_CONTEXT}"
64+
fi

0 commit comments

Comments
 (0)