def setUp(self): self.lp_for_snaps = Launchpad( username="******", token=getenv("SNAP_BUILDS_TOKEN", "secret"), secret=getenv("SNAP_BUILDS_SECRET", "secret"), session=requests.Session(), ) return super().setUp()
def test_01_build_image(self): lp_for_images = Launchpad( username="******", token=getenv("IMAGE_BUILDS_TOKEN", "secret"), secret=getenv("IMAGE_BUILDS_SECRET", "secret"), session=requests.Session(), ) response = lp_for_images.build_image(board="cm3", system="core16", snaps=["code", "toto"]) self.assertEqual(response.status_code, 201)
class LaunchpadTest(VCRTestCase): def _get_vcr_kwargs(self): """ This removes the authorization header from VCR so we don't record auth parameters """ return {"filter_headers": ["Authorization"]} def setUp(self): self.lp_for_snaps = Launchpad( username="******", token=getenv("SNAP_BUILDS_TOKEN", "secret"), secret=getenv("SNAP_BUILDS_SECRET", "secret"), session=requests.Session(), ) return super().setUp() def test_01_build_image(self): lp_for_images = Launchpad( username="******", token=getenv("IMAGE_BUILDS_TOKEN", "secret"), secret=getenv("IMAGE_BUILDS_SECRET", "secret"), session=requests.Session(), ) response = lp_for_images.build_image(board="cm3", system="core16", snaps=["code", "toto"]) self.assertEqual(response.status_code, 201) def test_02_get_snap_by_store_name(self): snap = self.lp_for_snaps.get_snap_by_store_name("toto") self.assertEqual("toto", snap["store_name"]) snap = self.lp_for_snaps.get_snap_by_store_name( "snap-that-does-not-exist") self.assertEqual(None, snap) def test_03_create_snap(self): snap_name = "new-test-snap" git_repo = "https://github.com/build-staging-snapcraft-io/test1" self.lp_for_snaps.create_snap(snap_name, git_repo, "macaroon") # Check that the snap exist new_snap = self.lp_for_snaps.get_snap_by_store_name("new-test-snap") self.assertEqual(git_repo, new_snap["git_repository_url"]) def test_04_build_snap(self): result = self.lp_for_snaps.build_snap("toto") self.assertEqual(True, result)
def notify_build(): """ An endpoint to trigger an update about a build event to be sent. This will usually be triggered by a webhook from Launchpad """ # Verify contents signature = hmac.new( flask.current_app.config["SECRET_KEY"].encode("utf-8"), flask.request.data, hashlib.sha1, ).hexdigest() if "X-Hub-Signature" not in flask.request.headers: return "No X-Hub-Signature provided\n", 403 if not hmac.compare_digest( signature, flask.request.headers["X-Hub-Signature"].split("=")[1] ): try: raise HTTPError(400) except HTTPError: flask.current_app.extensions["sentry"].captureException( extra={ "request_headers": str(flask.request.headers.keys()), "message": "x-hub-signature did not match", "expected_signature": signature, "header_contents": flask.request.headers[ "X-Hub-Signature" ], "extracted_signature": flask.request.headers[ "X-Hub-Signature" ].split("=")[1], } ) return "X-Hub-Signature does not match\n", 400 event_content = flask.request.json status = event_content["status"] build_url = ( "https://api.launchpad.net/devel" + event_content["livefs_build"] ) launchpad = Launchpad( username=os.environ["LAUNCHPAD_IMAGE_BUILD_USER"], token=os.environ["LAUNCHPAD_IMAGE_BUILD_TOKEN"], secret=os.environ["LAUNCHPAD_IMAGE_BUILD_SECRET"], session=session, auth_consumer=os.environ["LAUNCHPAD_IMAGE_BUILD_AUTH_CONSUMER"], ) build = launchpad.request(build_url).json() author_json = ( gnupg.GPG() .decrypt( build["metadata_override"]["_author_data"], passphrase=flask.current_app.config["SECRET_KEY"], ) .data ) if author_json: author = json.loads(author_json) else: return "_author_data could not be decoded\n", 400 email = author["email"] names = author["name"].split(" ") board = author["board"] snaps = ", ".join(build["metadata_override"]["extra_snaps"]) codename = build["distro_series_link"].split("/")[-1] version = Data().by_codename(codename).version arch = build["distro_arch_series_link"].split("/")[-1] build_link = build["web_link"] build_id = build_link.split("/")[-1] download_url = None if status == "Successfully built": download_url = launchpad.request( f"{build_url}?ws.op=getFileUrls" ).json()[0] session.post( "https://pages.ubuntu.com/index.php/leadCapture/save", data={ "FirstName": " ".join(names[:-1]), "LastName": names[-1] if len(names) > 1 else "", "Email": email, "formid": "3546", "lpId": "2154", "subId": "30", "munchkinId": "066-EOV-335", "imageBuilderVersion": version, "imageBuilderArchitecture": arch, "imageBuilderBoard": board, "imageBuilderSnaps": snaps, "imageBuilderID": build_id, "imageBuilderBuildlink": build_link, "imageBuilderStatus": status, "imageBuilderDownloadlink": download_url, }, ) return "Submitted\n", 202
def post_build(): """ Once they submit the build form on /core/build, kick off the build with Launchpad """ opt_in = flask.request.values.get("canonicalUpdatesOptIn") full_name = flask.request.values.get("FullName") names = full_name.split(" ") email = flask.request.values.get("Email") board = flask.request.values.get("board") system = flask.request.values.get("system") snaps = flask.request.values.get("snaps", "").split(",") arch = flask.request.values.get("arch") if not user_info(flask.session): flask.abort(401) launchpad = Launchpad( username=os.environ["LAUNCHPAD_IMAGE_BUILD_USER"], token=os.environ["LAUNCHPAD_IMAGE_BUILD_TOKEN"], secret=os.environ["LAUNCHPAD_IMAGE_BUILD_SECRET"], session=session, auth_consumer=os.environ["LAUNCHPAD_IMAGE_BUILD_AUTH_CONSUMER"], ) context = {} # Submit user to marketo session.post( "https://pages.ubuntu.com/index.php/leadCapture/save", data={ "canonicalUpdatesOptIn": opt_in, "FirstName": " ".join(names[:-1]), "LastName": names[-1] if len(names) > 1 else "", "Email": email, "formid": "3546", "lpId": "2154", "subId": "30", "munchkinId": "066-EOV-335", "imageBuilderStatus": "NULL", }, ) # Ensure webhook is created if flask.request.host == "ubuntu.com": launchpad.create_update_system_build_webhook( system=system, delivery_url="https://ubuntu.com/core/build/notify", secret=flask.current_app.config["SECRET_KEY"], ) # Kick off image build try: response = launchpad.build_image( board=board, system=system, snaps=snaps, author_info={"name": full_name, "email": email, "board": board}, gpg_passphrase=flask.current_app.config["SECRET_KEY"], arch=arch, ) context["build_info"] = launchpad.session.get( response.headers["Location"] ).json() except HTTPError as http_error: if http_error.response.status_code == 400: return ( flask.render_template( "core/build/error.html", build_error=http_error.response.content.decode(), ), 400, ) else: raise http_error return flask.render_template("core/build/index.html", **context)
import json import os import flask from canonicalwebteam.launchpad import Launchpad from ruamel.yaml import YAML from webapp.api.requests import PublisherSession, Session _yaml = YAML(typ="rt") _yaml_safe = YAML(typ="safe") api_session = Session() api_publisher_session = PublisherSession() launchpad = Launchpad( username=os.getenv("LP_API_USERNAME"), token=os.getenv("LP_API_TOKEN"), secret=os.getenv("LP_API_TOKEN_SECRET"), session=api_publisher_session, ) def get_yaml_loader(typ="safe"): if typ == "safe": return _yaml_safe return _yaml def get_licenses(): try: with open("webapp/licenses.json") as f: licenses = json.load(f)["licenses"]
# Local from webapp.api import dashboard as api from webapp.api.exceptions import ApiError, ApiResponseErrorList from webapp.api.github import GitHub, InvalidYAML from webapp.decorators import login_required from webapp.extensions import csrf from webapp.publisher.snaps.builds import map_build_and_upload_states from webapp.publisher.views import _handle_error, _handle_error_list from werkzeug.exceptions import Unauthorized GITHUB_SNAPCRAFT_USER_TOKEN = os.getenv("GITHUB_SNAPCRAFT_USER_TOKEN") BUILDS_PER_PAGE = 15 launchpad = Launchpad( username=os.getenv("LP_API_USERNAME"), token=os.getenv("LP_API_TOKEN"), secret=os.getenv("LP_API_TOKEN_SECRET"), session=talisker.requests.get_session(), ) def get_builds(lp_snap, selection): builds = launchpad.get_snap_builds(lp_snap["store_name"]) total_builds = len(builds) builds = builds[selection] snap_builds = [] for build in builds: status = map_build_and_upload_states(build["buildstate"],
class LaunchpadTest(VCRTestCase): def _get_vcr_kwargs(self): """ This removes the authorization header from VCR so we don't record auth parameters """ return {"filter_headers": ["Authorization"]} def setUp(self): self.lp_for_snaps = Launchpad( username="******", token=getenv("SNAP_BUILDS_TOKEN", "secret"), secret=getenv("SNAP_BUILDS_SECRET", "secret"), session=requests.Session(), ) self.lp_for_images = Launchpad( username="******", token=getenv("IMAGE_BUILDS_TOKEN", "secret"), secret=getenv("IMAGE_BUILDS_SECRET", "secret"), session=requests.Session(), ) return super().setUp() def test_01_build_image(self): response = self.lp_for_images.build_image( board="cm3", system="core16", snaps=["code", "toto"], author_info={ "name": "somename", "email": "someemail" }, gpg_passphrase="fakepassword", ) self.assertEqual(response.status_code, 201) def test_02_create_webhooks(self): print(getenv("IMAGE_BUILDS_TOKEN", "secret")) print(getenv("IMAGE_BUILDS_SECRET", "secret")) with self.assertRaises(WebhookExistsError): self.lp_for_images.create_system_build_webhook( "core18", "https://design.staging.ubuntu.com/?image.build", "fake-secret", ) response = self.lp_for_images.create_system_build_webhook( "classic18.04", "https://design.staging.ubuntu.com/?image.build", "fake-secret", ) self.assertEqual(response.status_code, 201) def test_03_get_snap_by_store_name(self): snap = self.lp_for_snaps.get_snap_by_store_name("toto") self.assertEqual("toto", snap["store_name"]) snap = self.lp_for_snaps.get_snap_by_store_name( "snap-that-does-not-exist") self.assertEqual(None, snap) def test_04_create_snap(self): snap_name = "new-test-snap" git_repo = "https://github.com/build-staging-snapcraft-io/test1" self.lp_for_snaps.create_snap(snap_name, git_repo, "macaroon") # Check that the snap exist new_snap = self.lp_for_snaps.get_snap_by_store_name("new-test-snap") self.assertEqual(git_repo, new_snap["git_repository_url"]) def test_05_build_snap(self): result = self.lp_for_snaps.build_snap("toto") self.assertEqual(True, result) def test_05_delete_snap(self): result = self.lp_for_snaps.delete_snap("new-test-snap") self.assertEqual(True, result)
from canonicalwebteam.launchpad import Launchpad from requests import Session from sentry_sdk import capture_exception, init from sentry_sdk.integrations.logging import ignore_logger from src import helper from src.exceptions import GitHubRateLimit, InvalidGitHubRepo from src.github import GitHub # Set up Sentry init(os.getenv("SENTRY_DSN")) ignore_logger("script.output") launchpad = Launchpad( username="******", token=os.getenv("LP_API_TOKEN"), secret=os.getenv("LP_API_TOKEN_SECRET"), session=Session(), ) github = GitHub(os.getenv("GITHUB_SNAPCRAFT_POLLER_TOKENS").split(), Session()) # Skip Snaps built in the last 24 hours threshold = datetime.datetime.now() - dateutil.relativedelta.relativedelta( days=1) def needs_building(snap, logger): if not snap["store_name"]: logger.info( f"SKIP {snap['name']}: Launchpad snap doesn't have store name") return False