Exemple #1
0
 def test_resolve_provider(self):
     for p in list_providers(self.config["upload"]):
         print(p)
         info = resolve_provider(self.config["upload"], p)
         self.assertTrue("display" in info)
         self.assertTrue("supported_types" in info)
         self.assertTrue("settings-info" in info)
Exemple #2
0
def v1_compose_start():
    """Start a compose

    The body of the post should have these fields:
      blueprint_name - The blueprint name from /blueprints/list/
      compose_type   - The type of output to create, from /compose/types
      branch         - Optional, defaults to master, selects the git branch to use for the blueprint.

    **POST /api/v1/compose**

      Start a compose. The content type should be 'application/json' and the body of the POST
      should look like this. The "upload" object is optional.

      The upload object can specify either a pre-existing profile to use (as returned by
      `/uploads/providers`) or one-time use settings for the provider.

      Example with upload profile::

          {
            "blueprint_name": "http-server",
            "compose_type": "tar",
            "branch": "master",
            "upload": {
              "image_name": "My Image",
              "provider": "azure",
              "profile": "production-azure-settings"
            }
          }

      Example with upload settings::

          {
            "blueprint_name": "http-server",
            "compose_type": "tar",
            "branch": "master",
            "upload": {
              "image_name": "My Image",
              "provider": "azure",
              "settings": {
                "resource_group": "SOMEBODY",
                "storage_account_name": "ONCE",
                "storage_container": "TOLD",
                "location": "ME",
                "subscription_id": "THE",
                "client_id": "WORLD",
                "secret": "IS",
                "tenant": "GONNA"
              }
            }
          }

      Pass it the name of the blueprint, the type of output (from
      '/api/v1/compose/types'), and the blueprint branch to use. 'branch' is
      optional and will default to master. It will create a new build and add
      it to the queue. It returns the build uuid and a status if it succeeds.
      If an "upload" is given, it will schedule an upload to run when the build
      finishes.

      Example response::

          {
            "build_id": "e6fa6db4-9c81-4b70-870f-a697ca405cdf",
            "upload_id": "572eb0d0-5348-4600-9666-14526ba628bb",
            "status": true
          }
    """
    # Passing ?test=1 will generate a fake FAILED compose.
    # Passing ?test=2 will generate a fake FINISHED compose.
    try:
        test_mode = int(request.args.get("test", "0"))
    except ValueError:
        test_mode = 0

    compose = request.get_json(cache=False)

    errors = []
    if not compose:
        return jsonify(status=False,
                       errors=[{
                           "id": MISSING_POST,
                           "msg": "Missing POST body"
                       }]), 400

    if "blueprint_name" not in compose:
        errors.append({
            "id": UNKNOWN_BLUEPRINT,
            "msg": "No 'blueprint_name' in the JSON request"
        })
    else:
        blueprint_name = compose["blueprint_name"]

    if "branch" not in compose or not compose["branch"]:
        branch = "master"
    else:
        branch = compose["branch"]

    if "compose_type" not in compose:
        errors.append({
            "id": BAD_COMPOSE_TYPE,
            "msg": "No 'compose_type' in the JSON request"
        })
    else:
        compose_type = compose["compose_type"]

    if VALID_BLUEPRINT_NAME.match(blueprint_name) is None:
        errors.append({
            "id": INVALID_CHARS,
            "msg": "Invalid characters in API path"
        })

    if not blueprint_exists(api, branch, blueprint_name):
        errors.append({
            "id": UNKNOWN_BLUEPRINT,
            "msg": "Unknown blueprint name: %s" % blueprint_name
        })

    if "upload" in compose:
        try:
            image_name = compose["upload"]["image_name"]

            if "profile" in compose["upload"]:
                # Load a specific profile for this provider
                profile = compose["upload"]["profile"]
                provider_name = compose["upload"]["provider"]
                settings = load_settings(api.config["COMPOSER_CFG"]["upload"],
                                         provider_name, profile)
            else:
                provider_name = compose["upload"]["provider"]
                settings = compose["upload"]["settings"]
        except KeyError as e:
            errors.append({
                "id": UPLOAD_ERROR,
                "msg": f'Missing parameter {str(e)}!'
            })
        try:
            provider = resolve_provider(api.config["COMPOSER_CFG"]["upload"],
                                        provider_name)
            if "supported_types" in provider and compose_type not in provider[
                    "supported_types"]:
                raise RuntimeError(
                    f'Type "{compose_type}" is not supported by provider "{provider_name}"!'
                )
            validate_settings(api.config["COMPOSER_CFG"]["upload"],
                              provider_name, settings, image_name)
        except Exception as e:
            errors.append({"id": UPLOAD_ERROR, "msg": str(e)})

    if errors:
        return jsonify(status=False, errors=errors), 400

    try:
        build_id = start_build(api.config["COMPOSER_CFG"],
                               api.config["DNFLOCK"], api.config["GITLOCK"],
                               branch, blueprint_name, compose_type, test_mode)
    except Exception as e:
        if "Invalid compose type" in str(e):
            return jsonify(status=False,
                           errors=[{
                               "id": BAD_COMPOSE_TYPE,
                               "msg": str(e)
                           }]), 400
        else:
            return jsonify(status=False,
                           errors=[{
                               "id": BUILD_FAILED,
                               "msg": str(e)
                           }]), 400

    if "upload" in compose:
        upload_id = uuid_schedule_upload(api.config["COMPOSER_CFG"], build_id,
                                         provider_name, image_name, settings)
    else:
        upload_id = ""

    return jsonify(status=True, build_id=build_id, upload_id=upload_id)
Exemple #3
0
def v1_compose_uploads_schedule(compose_uuid):
    """Schedule an upload of a compose to a given cloud provider

    **POST /api/v1/uploads/schedule/<compose_uuid>**

      The body can specify either a pre-existing profile to use (as returned by
      `/uploads/providers`) or one-time use settings for the provider.

      Example with upload profile::

          {
            "image_name": "My Image",
            "provider": "azure",
            "profile": "production-azure-settings"
          }

      Example with upload settings::

          {
            "image_name": "My Image",
            "provider": "azure",
            "settings": {
              "resource_group": "SOMEBODY",
              "storage_account_name": "ONCE",
              "storage_container": "TOLD",
              "location": "ME",
              "subscription_id": "THE",
              "client_id": "WORLD",
              "secret": "IS",
              "tenant": "GONNA"
            }
          }

      Example response::

          {
            "status": true,
            "upload_id": "572eb0d0-5348-4600-9666-14526ba628bb"
          }
    """
    if VALID_API_STRING.match(compose_uuid) is None:
        error = {"id": INVALID_CHARS, "msg": "Invalid characters in API path"}
        return jsonify(status=False, errors=[error]), 400

    parsed = request.get_json(cache=False)
    if not parsed:
        return jsonify(status=False,
                       errors=[{
                           "id": MISSING_POST,
                           "msg": "Missing POST body"
                       }]), 400

    try:
        image_name = parsed["image_name"]
        provider_name = parsed["provider"]
        if "profile" in parsed:
            # Load a specific profile for this provider
            profile = parsed["profile"]
            settings = load_settings(api.config["COMPOSER_CFG"]["upload"],
                                     provider_name, profile)
        else:
            settings = parsed["settings"]
    except KeyError as e:
        error = {"id": UPLOAD_ERROR, "msg": f'Missing parameter {str(e)}!'}
        return jsonify(status=False, errors=[error]), 400
    try:
        compose_type = uuid_status(api.config["COMPOSER_CFG"],
                                   compose_uuid)["compose_type"]
        provider = resolve_provider(api.config["COMPOSER_CFG"]["upload"],
                                    provider_name)
        if "supported_types" in provider and compose_type not in provider[
                "supported_types"]:
            raise RuntimeError(
                f'Type "{compose_type}" is not supported by provider "{provider_name}"!'
            )
    except Exception as e:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(e)
                       }]), 400

    try:
        upload_id = uuid_schedule_upload(api.config["COMPOSER_CFG"],
                                         compose_uuid, provider_name,
                                         image_name, settings)
    except RuntimeError as e:
        return jsonify(status=False,
                       errors=[{
                           "id": UPLOAD_ERROR,
                           "msg": str(e)
                       }]), 400
    return jsonify(status=True, upload_id=upload_id)
Exemple #4
0
 def get_provider_info(provider_name):
     provider = resolve_provider(ucfg, provider_name)
     provider["profiles"] = load_profiles(ucfg, provider_name)
     return provider