Skip to content

Commit 621454e

Browse files
Stepan Rasputnygcf-owl-bot[bot]m-strzelczyk
authored
feat: batch create job with secret manager sample (GoogleCloudPlatform#11911)
* feat: batch create job with secret manager sample * fix: added name of the secret * fix: change secret name and service account * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Trying different arrangement * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Maciej Strzelczyk <[email protected]>
1 parent 39b0bb2 commit 621454e

File tree

2 files changed

+141
-8
lines changed

2 files changed

+141
-8
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# [START batch_create_using_secret_manager]
16+
from typing import Dict, Optional
17+
18+
from google.cloud import batch_v1
19+
20+
21+
def create_with_secret_manager(
22+
project_id: str,
23+
region: str,
24+
job_name: str,
25+
secrets: Dict[str, str],
26+
service_account_email: Optional[str] = None,
27+
) -> batch_v1.Job:
28+
"""
29+
This method shows how to create a sample Batch Job that will run
30+
a simple command on Cloud Compute instances with passing secrets from secret manager.
31+
Note: Job's service account should have the permissions to access secrets.
32+
- Secret Manager Secret Accessor (roles/secretmanager.secretAccessor) IAM role.
33+
34+
Args:
35+
project_id: project ID or project number of the Cloud project you want to use.
36+
region: name of the region you want to use to run the job. Regions that are
37+
available for Batch are listed on: https://cloud.google.com/batch/docs/get-started#locations
38+
job_name: the name of the job that will be created.
39+
It needs to be unique for each project and region pair.
40+
secrets: secrets, which should be passed to the job. Environment variables should be capitalized
41+
by convention https://google.github.io/styleguide/shellguide.html#constants-and-environment-variable-names
42+
The format should look like:
43+
- {'SECRET_NAME': 'projects/{project_id}/secrets/{SECRET_NAME}/versions/{version}'}
44+
version can be set to 'latest'.
45+
service_account_email (optional): custom service account email
46+
47+
Returns:
48+
A job object representing the job created.
49+
"""
50+
client = batch_v1.BatchServiceClient()
51+
52+
# Define what will be done as part of the job.
53+
task = batch_v1.TaskSpec()
54+
runnable = batch_v1.Runnable()
55+
runnable.script = batch_v1.Runnable.Script()
56+
runnable.script.text = (
57+
"echo Hello world! from task ${BATCH_TASK_INDEX}."
58+
+ f" ${next(iter(secrets.keys()))} is the value of the secret."
59+
)
60+
task.runnables = [runnable]
61+
task.max_retry_count = 2
62+
task.max_run_duration = "3600s"
63+
64+
envable = batch_v1.Environment()
65+
envable.secret_variables = secrets
66+
task.environment = envable
67+
68+
# Tasks are grouped inside a job using TaskGroups.
69+
# Currently, it's possible to have only one task group.
70+
group = batch_v1.TaskGroup()
71+
group.task_count = 4
72+
group.task_spec = task
73+
74+
# Policies are used to define on what kind of virtual machines the tasks will run on.
75+
# Read more about local disks here: https://cloud.google.com/compute/docs/disks/persistent-disks
76+
policy = batch_v1.AllocationPolicy.InstancePolicy()
77+
policy.machine_type = "e2-standard-4"
78+
instances = batch_v1.AllocationPolicy.InstancePolicyOrTemplate()
79+
instances.policy = policy
80+
allocation_policy = batch_v1.AllocationPolicy()
81+
allocation_policy.instances = [instances]
82+
83+
service_account = batch_v1.ServiceAccount()
84+
service_account.email = service_account_email
85+
allocation_policy.service_account = service_account
86+
87+
job = batch_v1.Job()
88+
job.task_groups = [group]
89+
job.allocation_policy = allocation_policy
90+
job.labels = {"env": "testing", "type": "script"}
91+
# We use Cloud Logging as it's an out of the box available option
92+
job.logs_policy = batch_v1.LogsPolicy()
93+
job.logs_policy.destination = batch_v1.LogsPolicy.Destination.CLOUD_LOGGING
94+
95+
create_request = batch_v1.CreateJobRequest()
96+
create_request.job = job
97+
create_request.job_id = job_name
98+
# The job's parent is the region in which the job will run
99+
create_request.parent = f"projects/{project_id}/locations/{region}"
100+
101+
return client.create_job(create_request)
102+
103+
104+
# [END batch_create_using_secret_manager]
105+
106+
if __name__ == "__main__":
107+
import google.auth
108+
109+
PROJECT = google.auth.default()[1]
110+
REGION = "europe-west4"
111+
# Existing service account name within the project specified above.
112+
name = "test-account-name"
113+
secret_name = "TEST_SECRET"
114+
secrets = {secret_name: f"projects/11111111/secrets/{secret_name}/versions/latest"}
115+
job = create_with_secret_manager(
116+
PROJECT, REGION, "secret-manager-job-batch", secrets
117+
)

batch/tests/test_basics.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@
2121
from flaky import flaky
2222

2323
import google.auth
24-
from google.cloud import batch_v1
25-
from google.cloud import resourcemanager_v3
24+
from google.cloud import batch_v1, resourcemanager_v3
2625
import pytest
2726

2827
from ..create.create_with_container_no_mounting import create_container_job
2928
from ..create.create_with_gpu_no_mounting import create_gpu_job
3029
from ..create.create_with_persistent_disk import create_with_pd_job
3130
from ..create.create_with_script_no_mounting import create_script_job
31+
from ..create.create_with_secret_manager import create_with_secret_manager
3232
from ..create.create_with_service_account import create_with_custom_service_account_job
3333
from ..create.create_with_ssd import create_local_ssd_job
3434

@@ -42,6 +42,12 @@
4242
PROJECT = google.auth.default()[1]
4343
REGION = "europe-central2"
4444
ZONE = "europe-central2-b"
45+
SECRET_NAME = "PERMANENT_BATCH_TESTING"
46+
PROJECT_NUMBER = (
47+
resourcemanager_v3.ProjectsClient()
48+
.get_project(name=f"projects/{PROJECT}")
49+
.name.split("/")[1]
50+
)
4551

4652
TIMEOUT = 600 # 10 minutes
4753

@@ -61,12 +67,7 @@ def job_name():
6167

6268
@pytest.fixture()
6369
def service_account() -> str:
64-
client = resourcemanager_v3.ProjectsClient()
65-
request = resourcemanager_v3.GetProjectRequest()
66-
request.name = f"projects/{PROJECT}"
67-
project = client.get_project(request)
68-
project_number = project.name.split("/")[-1]
69-
return f"{project_number}[email protected]"
70+
return f"{PROJECT_NUMBER}[email protected]"
7071

7172

7273
@pytest.fixture
@@ -132,6 +133,10 @@ def _check_service_account(job: batch_v1.Job, service_account_email: str):
132133
assert job.allocation_policy.service_account.email == service_account_email
133134

134135

136+
def _check_secret_set(job: batch_v1.Job, secret_name: str):
137+
assert secret_name in job.task_groups[0].task_spec.environment.secret_variables
138+
139+
135140
@flaky(max_runs=3, min_passes=1)
136141
def test_script_job(job_name, capsys):
137142
job = create_script_job(PROJECT, REGION, job_name)
@@ -160,6 +165,17 @@ def test_service_account_job(job_name, service_account):
160165
)
161166

162167

168+
@flaky(max_runs=3, min_passes=1)
169+
def test_secret_manager_job(job_name, service_account):
170+
secrets = {
171+
SECRET_NAME: f"projects/{PROJECT_NUMBER}/secrets/{SECRET_NAME}/versions/latest"
172+
}
173+
job = create_with_secret_manager(
174+
PROJECT, REGION, job_name, secrets, service_account
175+
)
176+
_test_body(job, additional_test=lambda: _check_secret_set(job, SECRET_NAME))
177+
178+
163179
@flaky(max_runs=3, min_passes=1)
164180
def test_ssd_job(job_name: str, disk_name: str, capsys: "pytest.CaptureFixture[str]"):
165181
job = create_local_ssd_job(PROJECT, REGION, job_name, disk_name)

0 commit comments

Comments
 (0)