Add network/subnet option to allow running in the non-default
This will enable the users to use private subnets based on their
requirement.
BUG=b/218525570
TEST=manual and ./run_tests
Change-Id: Iec5be6d46d073ce51d8696825d1cb559ceefe010
Reviewed-on: https://cos-review.googlesource.com/c/cos/tools/+/31043
Reviewed-by: Robert Kolchmeyer <rkolchmeyer@google.com>
Tested-by: Varsha Teratipally <teratipally@google.com>
Cloud-Build: GCB Service account <228075978874@cloudbuild.gserviceaccount.com>
diff --git a/src/cmd/cos_customizer/README.md b/src/cmd/cos_customizer/README.md
index 2f45930..e9d772e 100644
--- a/src/cmd/cos_customizer/README.md
+++ b/src/cmd/cos_customizer/README.md
@@ -264,6 +264,17 @@
separate from the Cloud Build machine type option, which sets the machine type
of the Cloud Build VM, which is different from the COS Customizer preload VM.
+-`network`: The network/VPC to use for the COS Customizer preload VM.
+The network must have access to Google Cloud Storage. Defaults to
+default network `global/networks/default`. If -subnet is also specified subnet
+must be a subnetwork of network specified by -network.
+
+-`subnet`: The subnet to use for the COS Customizer preload VM. Defaults to
+default network `global/networks/default`. If the network is in auto subnet mode,
+the subnetwork is optional. If the network is in custom subnet mode, then this
+field should be specified. Zone should be specified if this field is specified.
+
+
An example `finish-image-build` step looks like the following:
- name: 'gcr.io/cos-cloud/cos-customizer'
@@ -273,6 +284,18 @@
'-image-name=my-custom-image',
'-image-project=$PROJECT_ID']
+An example `finish-image-build` step with `network` and `subnet` looks like the following:
+
+ - name: 'gcr.io/cos-cloud/cos-customizer'
+ args: ['finish-image-build',
+ '-zone=us-west1-b',
+ '-project=$PROJECT_ID',
+ '-network=global/networks/auto-vpc',
+ '-subnet=regions/us-west1/subnetworks/auto-vpc-subnet-us-west1',
+ '-image-name=my-custom-image',
+ '-image-project=$PROJECT_ID']
+
+
### Optional build steps
The rest of the build steps provided by COS Customizer are optional; if they are
diff --git a/src/cmd/cos_customizer/finish_image_build.go b/src/cmd/cos_customizer/finish_image_build.go
index e69870b..51232f5 100644
--- a/src/cmd/cos_customizer/finish_image_build.go
+++ b/src/cmd/cos_customizer/finish_image_build.go
@@ -45,6 +45,8 @@
imageName string
imageSuffix string
imageFamily string
+ network string
+ subnet string
deprecateOld bool
oldImageTTLSec int
labels *mapVar
@@ -88,6 +90,14 @@
flags.StringVar(&f.zone, "zone", "", "Zone to make GCE resources in.")
flags.StringVar(&f.project, "project", "", "Project to make GCE resources in.")
flags.StringVar(&f.machineType, "machine-type", "n1-standard-1", "Machine type to use during the build.")
+ flags.StringVar(&f.network, "network", "", "Network to use"+
+ " during the build. The network must have access to Google Cloud Storage."+
+ " If not specified, the network named default is used."+
+ " If -subnet is also specified subnet must be a subnetwork of network specified by -network.")
+ flags.StringVar(&f.subnet, "subnet", "", "SubNetwork to use"+
+ " during the build. If not provided, default subnet would be used."+
+ " If the network is in auto subnet mode, the subnetwork is optional. "+
+ " If the network is in custom subnet mode, then this field should be specified. Zone should be specified if this field is specified.")
if f.labels == nil {
f.labels = newMapVar()
}
@@ -157,6 +167,8 @@
buildConfig.Zone = f.zone
buildConfig.MachineType = f.machineType
buildConfig.DiskSize = f.diskSize
+ buildConfig.Network = f.network
+ buildConfig.Subnet = f.subnet
buildConfig.Timeout = f.timeout.String()
provConfig := &provisioner.Config{}
if err := config.LoadFromFile(files.ProvConfig, provConfig); err != nil {
diff --git a/src/cmd/provisioner/embeds_linux_amd64.go b/src/cmd/provisioner/embeds_linux_amd64.go
index f72e1b8..427f337 100644
--- a/src/cmd/provisioner/embeds_linux_amd64.go
+++ b/src/cmd/provisioner/embeds_linux_amd64.go
@@ -24,4 +24,3 @@
//go:embed docker-credential-gcr_amd64
var dockerCredentialGCR []byte
-
diff --git a/src/cmd/provisioner/embeds_linux_arm64.go b/src/cmd/provisioner/embeds_linux_arm64.go
index 65b88cb..b3c56b2 100644
--- a/src/cmd/provisioner/embeds_linux_arm64.go
+++ b/src/cmd/provisioner/embeds_linux_arm64.go
@@ -24,4 +24,3 @@
//go:embed docker-credential-gcr_arm64
var dockerCredentialGCR []byte
-
diff --git a/src/data/build_image.wf.json b/src/data/build_image.wf.json
index 8b280ae..4c96e3d 100644
--- a/src/data/build_image.wf.json
+++ b/src/data/build_image.wf.json
@@ -8,7 +8,9 @@
"cidata_img": {"Required": true, "Description": "Path to CIDATA vfat image containing cloud-init user-data and the provisioner program. Must be in .tar.gz format."},
"disk_size_gb": {"Value": "10", "Description": "The disk size to use for preloading."},
"host_maintenance": {"Value": "MIGRATE", "Description": "VM behavior when there is maintenance."},
- "machine_type": {"Required": true, "Description": "Machine type of the preload VM."}
+ "machine_type": {"Required": true, "Description": "Machine type of the preload VM."},
+ "network": {"Value": "", "Description": "Network to use for preload VM."},
+ "subnet": {"Value": "", "Description": "Subnetwork used for the preload VM."}
},
"Sources": {
"cloud-config": "/data/startup.yaml",
@@ -56,6 +58,12 @@
"scheduling": {
"onHostMaintenance": "${host_maintenance}"
},
+ "networkInterfaces": [
+ {
+ "network": "${network}",
+ "subnetwork": "${subnet}"
+ }
+ ],
"Metadata": {
"user-data": "${SOURCE:cloud-config}",
"block-project-ssh-keys": "TRUE",
diff --git a/src/pkg/config/config.go b/src/pkg/config/config.go
index 498882b..57debde 100644
--- a/src/pkg/config/config.go
+++ b/src/pkg/config/config.go
@@ -72,6 +72,8 @@
Timeout string
GCSFiles []string
GCEEndpoint string
+ Network string
+ Subnet string
}
// SaveConfigToFile clears the target config file and then saves the new config
diff --git a/src/pkg/preloader/preload.go b/src/pkg/preloader/preload.go
index f135031..1e8973f 100644
--- a/src/pkg/preloader/preload.go
+++ b/src/pkg/preloader/preload.go
@@ -310,6 +310,10 @@
buildSpec.MachineType,
"-var:host_maintenance",
hostMaintenance,
+ "-var:network",
+ buildSpec.Network,
+ "-var:subnet",
+ buildSpec.Subnet,
"-gcs_path",
gcs.managedDirURL(),
"-project",
diff --git a/src/pkg/preloader/preload_test.go b/src/pkg/preloader/preload_test.go
index 09510e7..2f7f1ee 100644
--- a/src/pkg/preloader/preload_test.go
+++ b/src/pkg/preloader/preload_test.go
@@ -351,6 +351,20 @@
want: []string{"-var:machine_type", "n1-standard-1"},
},
{
+ testName: "Network",
+ inputImage: config.NewImage("", ""),
+ outputImage: config.NewImage("", ""),
+ buildConfig: &config.Build{Network: "global/networks/vpc", GCSBucket: "bucket", GCSDir: "dir"},
+ want: []string{"-var:network", "global/networks/vpc"},
+ },
+ {
+ testName: "Subnet",
+ inputImage: config.NewImage("", ""),
+ outputImage: config.NewImage("", ""),
+ buildConfig: &config.Build{Subnet: "regions/us-west1/subnetworks/auto-vpc-subnet-us-west1", GCSBucket: "bucket", GCSDir: "dir"},
+ want: []string{"-var:subnet", "regions/us-west1/subnetworks/auto-vpc-subnet-us-west1"},
+ },
+ {
testName: "Timeout",
inputImage: config.NewImage("", ""),
outputImage: config.NewImage("", ""),
diff --git a/testing/network_subnet_test.yaml b/testing/network_subnet_test.yaml
new file mode 100644
index 0000000..4c39fc6
--- /dev/null
+++ b/testing/network_subnet_test.yaml
@@ -0,0 +1,60 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the License);
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# This test uses subnetwork `cos-customizer-test` in default VPC for the preload VM.
+
+substitutions:
+ "_TEST": "network_subnet_test"
+ "_INPUT_IMAGE": "cos-85-13310-1260-8"
+ "_INPUT_PROJECT": "cos-cloud"
+steps:
+- name: "gcr.io/cloud-builders/bazel"
+ args: ["run", "--spawn_strategy=standalone", ":cos_customizer", "--", "--norun"]
+- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
+ args: [ 'gcloud', 'compute', 'networks', 'subnets',
+ 'create', 'cos-customizer-test',
+ '--project', '${PROJECT_ID}', '--network', 'default', '--region', 'us-central1',
+ '--range', '10.124.0.0/20'
+ ]
+- name: "bazel:cos_customizer"
+ args: ["start-image-build",
+ "-build-context=testing/${_TEST}",
+ "-image-name=${_INPUT_IMAGE}",
+ "-image-project=${_INPUT_PROJECT}",
+ "-gcs-bucket=${PROJECT_ID}_cloudbuild",
+ "-gcs-workdir=customizer-$BUILD_ID"]
+- name: "bazel:cos_customizer"
+ args: ["run-script",
+ "-script=preload.sh"]
+- name: "bazel:cos_customizer"
+ args: ["finish-image-build",
+ "-machine-type=n1-standard-8",
+ "-zone=us-central1-a",
+ "-project=$PROJECT_ID",
+ "-subnet=regions/us-central1/subnetworks/cos-customizer-test",
+ "-image-name=preload-test-$BUILD_ID",
+ "-image-project=$PROJECT_ID"]
+- name: "gcr.io/compute-image-tools/daisy"
+ args: ["-project=$PROJECT_ID", "-zone=us-west1-b", "-var:image_name",
+ "preload-test-$BUILD_ID", "-var:image_project", "$PROJECT_ID",
+ "-var:test_cfg", "../${_TEST}/preload_test.cfg", "testing/util/run_test.wf.json"]
+- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
+ args: [ 'gcloud', 'compute', 'networks', 'subnets',
+ 'delete', 'cos-customizer-test',
+ '--project', '${PROJECT_ID}', '--region', 'us-central1',
+ ]
+options:
+ machineType: "N1_HIGHCPU_8"
+timeout: "7200s"
diff --git a/testing/network_subnet_test/preload.sh b/testing/network_subnet_test/preload.sh
new file mode 100644
index 0000000..fa6feac
--- /dev/null
+++ b/testing/network_subnet_test/preload.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eu
+
+echo "Getting auth token."
+AUTH_DATA="$(curl -s -f -m 10 "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" -H "Metadata-Flavor: Google")"
+R=$?
+if [ ${R} -ne 0 ]; then
+ echo "Getting auth token error, exited with status ${R}" >&2
+ exit ${R}
+fi
+
+AUTH="$(echo "${AUTH_DATA}" \
+| tr -d '{}' \
+| sed 's/,/\n/g' \
+| awk -F ':' '/access_token/ { print $2 }' \
+| tr -d '"\n')"
+
+if [ -z "${AUTH}" ]; then
+ echo "Auth token not found in AUTH_DATA ${AUTH_DATA}" >&2
+ exit 1
+fi
+
+echo "Getting instance project and zone."
+PROJ_ZONE="$(curl -s -f -m 10 "http://metadata.google.internal/computeMetadata/v1/instance/zone" -H "Metadata-Flavor: Google")"
+
+R=$?
+if [ ${R} -ne 0 ]; then
+ echo "Getting project and zone error, exited with status ${R}" >&2
+ exit ${R}
+fi
+
+echo "Getting instance name."
+INSTANCE_NAME="$(curl -s -f -m 10 "http://metadata.google.internal/computeMetadata/v1/instance/name" -H "Metadata-Flavor: Google")"
+R=$?
+if [ ${R} -ne 0 ]; then
+ echo "Getting instance name error, exited with status ${R}" >&2
+ exit ${R}
+fi
+
+# Save the output of the instance detail to a local destination
+curl "https://www.googleapis.com/compute/v1/${PROJ_ZONE}/instances/${INSTANCE_NAME}" -H "Authorization":"Bearer ${AUTH}" --header 'Accept: application/json' --compressed > /var/lib/instance_info.json
diff --git a/testing/network_subnet_test/preload_test.cfg b/testing/network_subnet_test/preload_test.cfg
new file mode 100644
index 0000000..b078a0e
--- /dev/null
+++ b/testing/network_subnet_test/preload_test.cfg
@@ -0,0 +1,80 @@
+#cloud-config
+#
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+write_files:
+ - path: /tmp/preloader-test/test.sh
+ permissions: 0644
+ owner: root
+ content: |
+ set -o errexit
+ set -o pipefail
+
+ trap 'fail exiting due to errors' EXIT
+
+ fail() {
+ echo "TestFail: $@"
+ }
+
+ testNetworkSubnet() {
+ if [[ ! -f "/var/lib/instance_info.json" ]]; then
+ echo "/var/lib/instance_info.json is missing"
+ echo "testNetworkSubnet failed"
+ RESULT="fail"
+ return
+ fi
+ textPatternFound=$(grep -R regions/us-central1/subnetworks/cos-customizer-test "/var/lib/instance_info.json")
+ if [[ -z ${textPatternFound} ]]; then
+ echo "/var/lib/instance_info.json: got $(cat "/var/lib/instance_info.json")"
+ echo "testNetworkSubnet fail"
+ RESULT="fail"
+ return
+ fi
+ echo "testNetworkSubnet pass"
+ }
+
+ main() {
+ RESULT="pass"
+ testNetworkSubnet
+ if [[ "${RESULT}" == "fail" ]]; then
+ exit 1
+ fi
+ }
+
+ main 2>&1 | sed "s/^/TestStatus: /"
+ trap - EXIT
+ echo "TestPass: all tests passed"
+
+ - path: /etc/systemd/system/preloader-test.service
+ permissions: 0644
+ owner: root
+ content: |
+ [Unit]
+ Description=Preloader test
+ Wants=network-online.target gcr-online.target docker.service
+ After=network-online.target gcr-online.target docker.service
+
+ [Service]
+ Type=oneshot
+ RemainAfterExit=yes
+ User=root
+ ExecStart=/bin/bash /tmp/preloader-test/test.sh
+ StandardOutput=tty
+ StandardError=tty
+ TTYPath=/dev/ttyS1
+
+runcmd:
+ - systemctl daemon-reload
+ - systemctl --no-block start preloader-test.service