diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bb7dc4cb6559..80a0f700cf1b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -74,6 +74,7 @@ /recaptcha_enterprise/**/* @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/python-samples-reviewers /recaptcha_enterprise/demosite/* @Sita04 @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/recaptcha-customer-obsession-reviewers @GoogleCloudPlatform/python-samples-reviewers /run/**/* @GoogleCloudPlatform/torus-dpe @GoogleCloudPlatform/python-samples-reviewers +/scheduler/**/* @GoogleCloudPlatform/torus-dpe @GoogleCloudPlatform/python-samples-reviewers /secretmanager/**/* @GoogleCloudPlatform/torus-dpe @GoogleCloudPlatform/python-samples-reviewers /securitycenter/**/* @GoogleCloudPlatform/dee-infra @GoogleCloudPlatform/python-samples-reviewers /speech/**/* @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/python-samples-reviewers diff --git a/scheduler/README.md b/scheduler/README.md deleted file mode 100644 index 88d88e9ccb37..000000000000 --- a/scheduler/README.md +++ /dev/null @@ -1,3 +0,0 @@ -These samples have been moved. - -https://github.com/googleapis/python-scheduler/tree/main/samples diff --git a/scheduler/snippets/app.yaml b/scheduler/snippets/app.yaml new file mode 100644 index 000000000000..e0e08d4312e1 --- /dev/null +++ b/scheduler/snippets/app.yaml @@ -0,0 +1,18 @@ +# Copyright 2019 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. + +# [START cloudscheduler_python_yaml] +runtime: python37 +service: my-service +# [END cloudscheduler_python_yaml] diff --git a/scheduler/snippets/create_job.py b/scheduler/snippets/create_job.py new file mode 100644 index 000000000000..7a863cdb637e --- /dev/null +++ b/scheduler/snippets/create_job.py @@ -0,0 +1,75 @@ +# Copyright 2019 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. + + +def create_scheduler_job(project_id, location_id, service_id): + """Create a job with an App Engine target via the Cloud Scheduler API""" + # [START cloudscheduler_create_job] + from google.cloud import scheduler + + # Create a client. + client = scheduler.CloudSchedulerClient() + + # TODO(developer): Uncomment and set the following variables + # project_id = 'PROJECT_ID' + # location_id = 'LOCATION_ID' + # service_id = 'my-service' + + # Construct the fully qualified location path. + parent = f"projects/{project_id}/locations/{location_id}" + + # Construct the request body. + job = { + "app_engine_http_target": { + "app_engine_routing": {"service": service_id}, + "relative_uri": "/log_payload", + "http_method": 1, + "body": "Hello World".encode(), + }, + "schedule": "* * * * *", + "time_zone": "America/Los_Angeles", + } + + # Use the client to send the job creation request. + response = client.create_job(request={"parent": parent, "job": job}) + + print("Created job: {}".format(response.name)) + # [END cloudscheduler_create_job] + return response + + +def delete_scheduler_job(project_id, location_id, job_id): + """Delete a job via the Cloud Scheduler API""" + # [START cloudscheduler_delete_job] + from google.api_core.exceptions import GoogleAPICallError + from google.cloud import scheduler + + # Create a client. + client = scheduler.CloudSchedulerClient() + + # TODO(developer): Uncomment and set the following variables + # project_id = 'PROJECT_ID' + # location_id = 'LOCATION_ID' + # job_id = 'JOB_ID' + + # Construct the fully qualified job path. + job = f"projects/{project_id}/locations/{location_id}/jobs/{job_id}" + + # Use the client to send the job deletion request. + try: + client.delete_job(name=job) + print("Job deleted.") + except GoogleAPICallError as e: + print("Error: %s" % e) + # [END cloudscheduler_delete_job] diff --git a/scheduler/snippets/create_job_test.py b/scheduler/snippets/create_job_test.py new file mode 100644 index 000000000000..09d88bb7b676 --- /dev/null +++ b/scheduler/snippets/create_job_test.py @@ -0,0 +1,34 @@ +# Copyright 2019 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. + +import os + +import create_job + +TEST_PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT") +TEST_LOCATION = os.getenv("LOCATION_ID", "us-central1") + + +def test_create_job(capsys): + create_result = create_job.create_scheduler_job( + TEST_PROJECT_ID, TEST_LOCATION, "my-service" + ) + out, _ = capsys.readouterr() + assert "Created job:" in out + + job_name = create_result.name.split("/")[-1] + create_job.delete_scheduler_job(TEST_PROJECT_ID, TEST_LOCATION, job_name) + + out, _ = capsys.readouterr() + assert "Job deleted." in out diff --git a/scheduler/snippets/main.py b/scheduler/snippets/main.py new file mode 100644 index 000000000000..1c98ca2aef4d --- /dev/null +++ b/scheduler/snippets/main.py @@ -0,0 +1,44 @@ +# Copyright 2019 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. + +"""App Engine app to serve as an endpoint for Cloud Scheduler samples.""" + +# [START cloudscheduler_app] +from flask import Flask, request + +app = Flask(__name__) + + +# Define relative URI for job endpoint +@app.route("/log_payload", methods=["POST"]) +def example_task_handler(): + """Log the job payload.""" + payload = request.get_data(as_text=True) or "(empty payload)" + print("Received job with payload: {}".format(payload)) + return "Printed job payload: {}".format(payload) + + +# [END cloudscheduler_app] + + +@app.route("/") +def hello(): + """Basic index to verify app is serving.""" + return "Hello World!" + + +if __name__ == "__main__": + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See entrypoint in app.yaml. + app.run(host="127.0.0.1", port=8080, debug=True) diff --git a/scheduler/snippets/main_test.py b/scheduler/snippets/main_test.py new file mode 100644 index 000000000000..6419f1366ec7 --- /dev/null +++ b/scheduler/snippets/main_test.py @@ -0,0 +1,46 @@ +# Copyright 2019 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. + +import pytest + + +@pytest.fixture +def app(): + import main + + main.app.testing = True + return main.app.test_client() + + +def test_index(app): + r = app.get("/") + assert r.status_code == 200 + + +def test_log_payload(capsys, app): + payload = "test_payload" + + r = app.post("/log_payload", data=payload) + assert r.status_code == 200 + + out, _ = capsys.readouterr() + assert payload in out + + +def test_empty_payload(capsys, app): + r = app.post("/log_payload") + assert r.status_code == 200 + + out, _ = capsys.readouterr() + assert "empty payload" in out diff --git a/scheduler/snippets/noxfile_config.py b/scheduler/snippets/noxfile_config.py new file mode 100644 index 000000000000..4622f7616789 --- /dev/null +++ b/scheduler/snippets/noxfile_config.py @@ -0,0 +1,42 @@ +# Copyright 2021 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. + +# Default TEST_CONFIG_OVERRIDE for python repos. + +# You can copy this file into your directory, then it will be imported from +# the noxfile.py. + +# The source of truth: +# https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/noxfile_config.py + +TEST_CONFIG_OVERRIDE = { + # You can opt out from the test for specific Python versions. + "ignored_versions": ["2.7", "3.6"], + # Old samples are opted out of enforcing Python type hints + # All new samples should feature them + "enforce_type_hints": False, + # An envvar key for determining the project id to use. Change it + # to 'BUILD_SPECIFIC_GCLOUD_PROJECT' if you want to opt in using a + # build specific Cloud project. You can also use your own string + # to use your own Cloud project. + "gcloud_project_env": "GOOGLE_CLOUD_PROJECT", + # 'gcloud_project_env': 'BUILD_SPECIFIC_GCLOUD_PROJECT', + # If you need to use a specific version of pip, + # change pip_version_override to the string representation + # of the version number, for example, "20.2.4" + "pip_version_override": None, + # A dictionary you want to inject into your test. Don't put any + # secrets here. These values will override predefined values. + "envs": {}, +} diff --git a/scheduler/snippets/requirements-test.txt b/scheduler/snippets/requirements-test.txt new file mode 100644 index 000000000000..c021c5b5b702 --- /dev/null +++ b/scheduler/snippets/requirements-test.txt @@ -0,0 +1 @@ +pytest==7.2.2 diff --git a/scheduler/snippets/requirements.txt b/scheduler/snippets/requirements.txt new file mode 100644 index 000000000000..8ac51b47973d --- /dev/null +++ b/scheduler/snippets/requirements.txt @@ -0,0 +1,3 @@ +Flask==2.2.3 +gunicorn==20.1.0 +google-cloud-scheduler==2.11.0