Skip to content

Commit 8b7a9b3

Browse files
authored
Merge pull request #3968 from upodroid/deploy-ar
Deploy Artifact Registry to Production Project
2 parents 362cd88 + cee0b5d commit 8b7a9b3

File tree

4 files changed

+151
-9
lines changed

4 files changed

+151
-9
lines changed

infra/gcp/bash/ensure-prod-storage.sh

+41-7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ readonly PROD_PROJECT_SERVICES=(
7979
containerregistry.googleapis.com
8080
# prod projects host binaries in GCS
8181
storage-component.googleapis.com
82+
# prod projects host containers in AR
83+
artifactregistry.googleapis.com
8284
)
8385

8486
readonly PROD_PROJECT_DISABLED_SERVICES=(
@@ -87,7 +89,9 @@ readonly PROD_PROJECT_DISABLED_SERVICES=(
8789
)
8890

8991
# Regions for prod GCR.
90-
PROD_REGIONS=(us eu asia)
92+
GCR_PROD_REGIONS=(us eu asia)
93+
# Regions for prod AR.
94+
AR_PROD_REGIONS=(asia-east1 asia-south1 asia-northeast1 asia-northeast2 australia-southeast1 europe-north1 europe-southeast1 europe-west1 europe-west2 europe-west4 europe-west8 europe-west9 southamerica-west1 us-central1 us-east1 us-east4 us-east5 us-south1 us-west1 us-west2)
9195

9296
# Minimum time we expect to keep prod GCS artifacts.
9397
PROD_RETENTION="10y"
@@ -102,8 +106,8 @@ function ensure_prod_gcr() {
102106
fi
103107
local project="${1}"
104108

105-
color 6 "Ensuring prod GCR for regions: ${PROD_REGIONS[*]}"
106-
for region in "${PROD_REGIONS[@]}"; do
109+
color 6 "Ensuring prod GCR for regions: ${GCR_PROD_REGIONS[*]}"
110+
for region in "${GCR_PROD_REGIONS[@]}"; do
107111
local gcr_bucket="gs://${region}.artifacts.${project}.appspot.com"
108112

109113
color 3 "region: ${region}"
@@ -121,6 +125,33 @@ function ensure_prod_gcr() {
121125
done 2>&1 | indent
122126
}
123127

128+
# Make a prod AR repository and grant access to it.
129+
#
130+
# $1: The GCP project name (GCR names == project names)
131+
function ensure_prod_ar() {
132+
if [ $# != 1 ] || [ -z "$1" ]; then
133+
echo "ensure_prod_ar(project) requires 1 argument" >&2
134+
return 1
135+
fi
136+
local project="${1}"
137+
local serviceaccount
138+
139+
color 6 "Ensuring prod AR registry for locations: ${AR_PROD_REGIONS[*]}"
140+
for region in "${AR_PROD_REGIONS[@]}"; do
141+
142+
color 3 "region: ${region}"
143+
color 6 "Ensuring an AR repo exists in location: ${region} for project: ${project}"
144+
ensure_ar_repo "${project}" "${region}"
145+
146+
color 6 "Ensuring GCR admins can admin AR in location: ${region} for project: ${project}"
147+
empower_ar_admins "${project}" "${region}"
148+
149+
color 6 "Empowering image promoter with roles/artifactregistry.repoAdmin in project: ${project}"
150+
serviceaccount=$(svc_acct_email "${project}" "${IMAGE_PROMOTER_SVCACCT}")
151+
ensure_project_role_binding "${project}" "serviceAccount:$serviceaccount" "artifactregistry.repoAdmin"
152+
done 2>&1 | indent
153+
}
154+
124155
# Make a prod GCS bucket and grant access to it. We need whole buckets for
125156
# this because we want to grant minimal permissions, but there's no concept of
126157
# permissions on a "subdirectory" of a bucket. If we had a GCS promoter akin
@@ -175,7 +206,7 @@ function empower_group_to_fake_prod() {
175206
empower_group_as_viewer "${project}" "${group}"
176207

177208
color 6 "Empowering $group for GCR in $project"
178-
for r in "${PROD_REGIONS[@]}"; do
209+
for r in "${GCR_PROD_REGIONS[@]}"; do
179210
color 3 "region $r"
180211
empower_group_to_write_gcr "${group}" "${project}" "${r}"
181212
done
@@ -198,15 +229,18 @@ function ensure_all_prod_projects() {
198229
color 6 "Ensuring project exists: ${prj}"
199230
ensure_project "${prj}"
200231

201-
color 6 "Ensuring Services to host and analyze aritfacts: ${prj}"
232+
color 6 "Ensuring Services to host and analyze artifacts: ${prj}"
202233
ensure_services "${prj}" "${PROD_PROJECT_SERVICES[@]}" 2>&1 | indent
203234

204235
color 6 "Ensuring disabled services for prod project: ${prj}"
205236
ensure_disabled_services "${prj}" "${PROD_PROJECT_DISABLED_SERVICES[@]}" 2>&1 | indent
206237

207-
color 6 "Ensuring the GCR repository: ${prj}"
238+
color 6 "Ensuring the GCR repositories: ${prj}"
208239
ensure_prod_gcr "${prj}" 2>&1 | indent
209240

241+
color 6 "Ensuring the AR repositories: ${prj}"
242+
ensure_prod_ar "${prj}" 2>&1 | indent
243+
210244
color 6 "Ensuring the GCS bucket: gs://${prj}"
211245
ensure_prod_gcs_bucket "${prj}" "gs://${prj}" 2>&1 | indent
212246
done
@@ -289,7 +323,7 @@ function ensure_all_prod_special_cases() {
289323
# real $PRODBAK_PROJECT). We don't want this same power for the non-test
290324
# backup system, so a compromised promoter can't nuke backups.
291325
color 6 "Empowering backup-test-prod promoter to backup-test-prod GCR"
292-
for r in "${PROD_REGIONS[@]}"; do
326+
for r in "${GCR_PROD_REGIONS[@]}"; do
293327
color 3 "region $r"
294328
empower_svcacct_to_write_gcr \
295329
"$(svc_acct_email "${GCR_BACKUP_TEST_PRODBAK_PROJECT}" "${IMAGE_PROMOTER_SVCACCT}")" \

infra/gcp/bash/lib.sh

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ trap 'cleanup_tmpdir' EXIT
4444
. "$(dirname "${BASH_SOURCE[0]}")/lib_gcs.sh"
4545

4646
# order doesn't matter here, so keep sorted
47+
. "$(dirname "${BASH_SOURCE[0]}")/lib_ar.sh"
4748
. "$(dirname "${BASH_SOURCE[0]}")/lib_gcr.sh"
4849
. "$(dirname "${BASH_SOURCE[0]}")/lib_gsm.sh"
4950
. "$(dirname "${BASH_SOURCE[0]}")/lib_services.sh"

infra/gcp/bash/lib_ar.sh

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2022 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Artifact Registry utility functions
18+
#
19+
# This is intended to be very general-purpose and "low-level". Higher-level
20+
# policy does not belong here.
21+
#
22+
# This MUST NOT be used directly. Source it via lib.sh instead.
23+
24+
# Grant write privileges on a AR to a group
25+
# $1: The googlegroups group email
26+
# $2: The GCP project
27+
# $3: The AR region
28+
function empower_group_to_write_ar() {
29+
if [ $# -lt 3 ] || [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
30+
echo "empower_group_to_write_ar(group_name, project, location) requires 3 arguments" >&2
31+
return 1
32+
fi
33+
local group="$1"
34+
local project="$2"
35+
local location="$3"
36+
37+
ensure_repository_role_binding "images" "${group}" "artifactregistry.repoAdmin" "${project}" "${location}"
38+
}
39+
40+
function ensure_public_ar_registry() {
41+
if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ]; then
42+
echo "${FUNCNAME[0]}(project, location) requires 2 arguments" >&2
43+
return 1
44+
fi
45+
local project="$1"
46+
local location="$2"
47+
48+
ensure_repository_role_binding "images" "allUsers" "artifactregistry.reader" "${project}" "${location}"
49+
}
50+
51+
function empower_ar_admins() {
52+
# Reusing GCR Admins groups
53+
if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ]; then
54+
echo "empower_ar_admins(project, location) requires 2 arguments" >&2
55+
return 1
56+
fi
57+
local project="$1"
58+
local location="$2"
59+
60+
ensure_repository_role_binding "images" "group:${GCR_ADMINS}" "artifactregistry.admin" "${project}" "${location}"
61+
}
62+
63+
# Ensure the AR registry exists and is world-readable.
64+
# $1: The GCP project
65+
# $2: The AR location (optional)
66+
function ensure_ar_repo() {
67+
if [ $# -lt 2 ] || [ -z "$1" ] || [ -z "$2" ]; then
68+
echo "ensure_ar_repo(project, location) requires 2 arguments" >&2
69+
return 1
70+
fi
71+
local project="$1"
72+
local location="$2"
73+
# AR Repos will always be called images. Format LOCATION-docker.pkg.dev/PROJECT_ID/images/foobar:latest
74+
if ! gcloud artifacts repositories describe images --location="${location}" --project="${project}" >/dev/null 2>&1; then
75+
gcloud artifacts repositories create images \
76+
--repository-format=docker \
77+
--location="${location}" \
78+
--project="${project}"
79+
fi
80+
81+
ensure_public_ar_registry "${project}" "${location}"
82+
}
83+
84+
# Ensure that IAM binding is present for repositories
85+
# Arguments:
86+
# $1: The repository name (e.g. "images")
87+
# $2: The principal (e.g. "group:[email protected]")
88+
# $3: The role name (e.g. "roles/storage.objectAdmin")
89+
# $4: The project (e.g. "k8s-artifacts-prod")
90+
# $5: The location (e.g. "europe")
91+
function ensure_ar_repository_role_binding() {
92+
if [ ! $# -eq 5 ] || [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ]; then
93+
echo "ensure_ar_repository_role_binding(repository, principal, role, project, location) requires 5 arguments" >&2
94+
return 1
95+
fi
96+
97+
local repository="${1}"
98+
local principal="${2}"
99+
local role="${3}"
100+
101+
_ensure_resource_role_binding "artifacts repositories" "${repository}" "${principal}" "${role}" "${project}" "${location}"
102+
}

infra/gcp/bash/lib_iam.sh

+7-2
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ function ensure_project_role_binding() {
197197
_ensure_resource_role_binding "projects" "${project}" "${principal}" "${role}"
198198
}
199199

200-
# Ensure that IAM binding is present for project
200+
# Ensure that IAM binding is present for secrets
201201
# Arguments:
202202
# $1: The fully qualified secret id (e.g. "projects/k8s-infra-foo/secrets/my-secret-id")
203203
# $2: The principal (e.g. "group:[email protected]")
@@ -435,7 +435,7 @@ function _format_iam_policy() {
435435
# [$5]: (Optional) the id of the project hosting the resource (e.g. "k8s-infra-foo")
436436
function _ensure_resource_role_binding() {
437437
if [ $# -lt 4 ] || [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then
438-
echo "${FUNCNAME[0]}(resource, id, principal, role, [project]) requires at least 4 arguments" >&2
438+
echo "${FUNCNAME[0]}(resource, id, principal, role, [project], [location]) requires at least 4 arguments" >&2
439439
return 1
440440
fi
441441

@@ -444,12 +444,17 @@ function _ensure_resource_role_binding() {
444444
local principal="${3}"
445445
local role="${4}"
446446
local project="${5:-""}"
447+
local location="${6:-""}"
447448

448449
local flags=()
449450
if [ -n "${project}" ]; then
450451
flags+=(--project "${project}")
451452
fi
452453

454+
if [ -n "${location}" ]; then
455+
flags+=(--location "${location}")
456+
fi
457+
453458
local before="${TMPDIR}/iam-bind.before.yaml"
454459
local after="${TMPDIR}/iam-bind.after.yaml"
455460

0 commit comments

Comments
 (0)