Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Point build at akamai-apis OpenAPI spec #661

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,4 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build the Docker image
run: docker build . --file Dockerfile --tag linode/cli:$(date +%s) --build-arg="github_token=$GITHUB_TOKEN"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: docker build . --file Dockerfile --tag linode/cli:$(date +%s)
4 changes: 1 addition & 3 deletions .github/workflows/e2e-suite-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ jobs:

- name: Install the CLI
run: make install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- run: make INTEGRATION_TEST_PATH="${{ inputs.test_path }}" testint
env:
Expand Down Expand Up @@ -102,4 +100,4 @@ jobs:
status: 'completed',
conclusion: process.env.conclusion
});
return result;
return result;
2 changes: 0 additions & 2 deletions .github/workflows/e2e-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ jobs:

- name: Install Package
run: make install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Set LINODE_CLI_TOKEN
run: |
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/nightly-smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ jobs:

- name: Install Linode CLI
run: make install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run smoke tests
id: smoke_tests
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/publish-oci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,3 @@ jobs:
tags: linode/cli:${{ steps.cli_version.outputs.result }},linode/cli:latest
build-args: |
linode_cli_version=${{ steps.cli_version.outputs.result }}
github_token=${{ secrets.GITHUB_TOKEN }}
1 change: 0 additions & 1 deletion .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ jobs:
- name: Build the package
run: make build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LINODE_CLI_VERSION: ${{ github.event.release.tag_name }}

- name: Publish the release artifacts to PyPI
Expand Down
74 changes: 0 additions & 74 deletions .github/workflows/remote-release-trigger.yml

This file was deleted.

4 changes: 0 additions & 4 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ jobs:

- name: Install Package
run: make install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run the unit test suite
run: make test
Expand Down Expand Up @@ -59,8 +57,6 @@ jobs:
shell: pwsh
run: |
make install
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run the unit test suite
run: make test
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
FROM python:3.11-slim AS builder

ARG linode_cli_version
ARG github_token

WORKDIR /src

Expand All @@ -12,7 +11,7 @@ COPY . .

RUN make requirements

RUN LINODE_CLI_VERSION=$linode_cli_version GITHUB_TOKEN=$github_token make build
RUN LINODE_CLI_VERSION=$linode_cli_version make build

FROM python:3.11-slim

Expand Down
10 changes: 4 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#
# Makefile for more convenient building of the Linode CLI and its baked content
#

SPEC := https://raw.githubusercontent.com/akamai/akamai-apis/main/apis/linode-api/v4/openapi.json

# Test-related arguments
MODULE :=
TEST_CASE_COMMAND :=
TEST_ARGS :=
Expand All @@ -9,12 +13,6 @@ ifdef TEST_CASE
TEST_CASE_COMMAND = -k $(TEST_CASE)
endif


SPEC_VERSION ?= latest
ifndef SPEC
override SPEC = $(shell ./resolve_spec_url ${SPEC_VERSION})
endif

# Version-related variables
VERSION_FILE := ./linodecli/version.py
VERSION_MODULE_DOCSTRING ?= \"\"\"\nThe version of the Linode CLI.\n\"\"\"\n\n
Expand Down
9 changes: 2 additions & 7 deletions linodecli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
from linodecli import plugins
from linodecli.exit_codes import ExitCodes

from .arg_helpers import (
bake_command,
register_args,
register_plugin,
remove_plugin,
)
from .arg_helpers import register_args, register_plugin, remove_plugin
from .cli import CLI
from .completion import get_completions
from .configuration import ENV_TOKEN_NAME
Expand Down Expand Up @@ -103,7 +98,7 @@ def main(): # pylint: disable=too-many-branches,too-many-statements
if parsed.action is None:
print("No spec provided, cannot bake")
sys.exit(ExitCodes.ARGUMENT_ERROR)
bake_command(cli, parsed.action)
cli.bake(parsed.action)
sys.exit(ExitCodes.SUCCESS)
elif cli.ops is None:
# if not spec was found and we weren't baking, we're doomed
Expand Down
27 changes: 0 additions & 27 deletions linodecli/arg_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@
Argument parser for the linode CLI
"""

import os
import sys
from importlib import import_module

import requests
import yaml

from linodecli import plugins
from linodecli.exit_codes import ExitCodes
from linodecli.helpers import (
register_args_shared,
register_debug_arg,
Expand Down Expand Up @@ -166,24 +160,3 @@ def remove_plugin(plugin_name, config):

config.write_config()
return f"Plugin {plugin_name} removed", 0


def bake_command(cli, spec_loc):
"""
Handle a bake command from args
"""
try:
if os.path.exists(os.path.expanduser(spec_loc)):
with open(os.path.expanduser(spec_loc), encoding="utf-8") as f:
spec = yaml.safe_load(f.read())
else: # try to GET it
resp = requests.get(spec_loc, timeout=120)
if resp.status_code == 200:
spec = yaml.safe_load(resp.content)
else:
raise RuntimeError(f"Request failed to {spec_loc}")
except Exception as e:
print(f"Could not load spec: {e}")
sys.exit(ExitCodes.REQUEST_FAILED)

cli.bake(spec)
2 changes: 1 addition & 1 deletion linodecli/baked/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# Sentence delimiter, split on a period followed by any type of
# whitespace (space, new line, tab, etc.)
REGEX_SENTENCE_DELIMITER = re.compile(r"\.(?:\s|$)")
REGEX_SENTENCE_DELIMITER = re.compile(r"\W(?:\s|$)")

# Matches on pattern __prefix__ at the beginning of a description
# or after a comma
Expand Down
102 changes: 99 additions & 3 deletions linodecli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@
Responsible for managing spec and routing commands to operations.
"""

import contextlib
import json
import os
import pickle
import sys
from json import JSONDecodeError
from sys import version_info
from typing import IO, Any, ContextManager, Dict

import requests
import yaml
from openapi3 import OpenAPI

from linodecli.api_request import do_request, get_all_pages
Expand Down Expand Up @@ -40,11 +46,19 @@ def __init__(self, version, base_url, skip_config=False):
self.config = CLIConfig(self.base_url, skip_config=skip_config)
self.load_baked()

def bake(self, spec):
def bake(self, spec_location: str):
"""
Generates ops and bakes them to a pickle
Generates ops and bakes them to a pickle.

:param spec_location: The URL or file path of the OpenAPI spec to parse.
"""
spec = OpenAPI(spec)

try:
spec = self._load_openapi_spec(spec_location)
except Exception as e:
print(f"Failed to load spec: {e}")
sys.exit(ExitCodes.REQUEST_FAILED)

self.spec = spec
self.ops = {}
ext = {
Expand Down Expand Up @@ -205,3 +219,85 @@ def user_agent(self) -> str:
f"linode-api-docs/{self.spec_version} "
f"python/{version_info[0]}.{version_info[1]}.{version_info[2]}"
)

@staticmethod
def _load_openapi_spec(spec_location: str) -> OpenAPI:
"""
Attempts to load the raw OpenAPI spec (YAML or JSON) at the given location.

:param spec_location: The location of the OpenAPI spec.
This can be a local path or a URL.

:returns: A tuple containing the loaded OpenAPI object and the parsed spec in
dict format.
"""

with CLI._get_spec_file_reader(spec_location) as f:
parsed = CLI._parse_spec_file(f)

return OpenAPI(parsed)

@staticmethod
@contextlib.contextmanager
def _get_spec_file_reader(
spec_location: str,
) -> ContextManager[IO]:
"""
Returns a reader for an OpenAPI spec file from the given location.

:param spec_location: The location of the OpenAPI spec.
This can be a local path or a URL.

:returns: A context manager yielding the spec file's reader.
"""

# Case for local file
local_path = os.path.expanduser(spec_location)
if os.path.exists(local_path):
f = open(local_path, "r", encoding="utf-8")

try:
yield f
finally:
f.close()

return

# Case for remote file
resp = requests.get(spec_location, stream=True, timeout=120)
if resp.status_code != 200:
raise RuntimeError(f"Failed to GET {spec_location}")

# We need to access the underlying urllib
# response here so we can return a reader
# usable in yaml.safe_load(...) and json.load(...)
resp.raw.decode_content = True

try:
yield resp.raw
finally:
resp.close()

@staticmethod
def _parse_spec_file(reader: IO) -> Dict[str, Any]:
"""
Parses the given file reader into a dict and returns a dict.

:param reader: A reader for a YAML or JSON file.

:returns: The parsed file.
"""

errors = []

try:
return yaml.safe_load(reader)
except yaml.YAMLError as err:
errors.append(str(err))

try:
return json.load(reader)
except JSONDecodeError as err:
errors.append(str(err))

raise ValueError(f"Failed to parse spec file: {'; '.join(errors)}")
Loading
Loading