Skip to main content

Naas Python SDK

Project description

🐍 Naas Python SDK

Overview

Naas Python SDK is a software development kit that provides interfaces and implementations for interacting with Naas services. The project follows a hexagonal architecture pattern with clear separation between domains, adaptors, and interfaces.

Key Features

  • Space Management: Deploy and manage cloud applications
  • Storage Operations: S3-compatible storage with credential management
  • Asset Management: Handle digital assets
  • Registry Support: Container registry operations
  • Secret Management: Secure credential handling
  • CI/CD Integration: Automated pipeline generation

Project Structure

The project is organized into several key components:

1. Core Domains

  • Asset Domain: Handles asset management operations
  • Space Domain: Manages space-related operations
  • Storage Domain: Handles storage operations
  • Secret Domain: Manages secrets and credentials
  • Registry Domain: Handles registry operations

Each domain follows a similar structure:

domains/
  ├── domain_name/
  │   ├── adaptors/
  │   │   ├── primary/
  │   │   └── secondary/
  │   ├── handlers/
  │   ├── models/
  │   ├── DomainSchema.py
  │   └── DomainDomain.py

2. Architecture

The project implements a hexagonal (ports and adapters) architecture with:

  • Primary Adapters: Handle incoming requests (CLI, SDK)
  • Secondary Adapters: Handle outgoing requests (API calls)
  • Domain Logic: Core business logic
  • Schemas: Interface definitions using abstract base classes

3. Key Components

CLI Interface

The project provides a CLI interface through Typer:

from naas_python.cli import app


def main():
    app()


if __name__ == "__main__":
    main()

Storage Provider Support

Includes S3 storage provider implementation with credential management:

    def save_naas_credentials(self, workspace_id:str, storage_name:str, credentials:dict)-> str:

        self.naas_bucket = urlparse(credentials['credentials']['s3']['endpoint_url']).netloc
        self.naas_workspace_id = urlparse(credentials['credentials']['s3']['endpoint_url']).path.split('/')[1]
        self.naas_storage = urlparse(credentials['credentials']['s3']['endpoint_url']).path.split('/')[2]
        
        s3_credentials = {
            "provider": "s3",
            "workspace_id": self.naas_workspace_id,
            "storage_name": self.naas_storage,
            "endpoint_url": f"s3.{credentials['credentials']['s3']['region_name']}.amazonaws.com",
            "bucket": f"{self.naas_bucket}",
            "region_name": credentials['credentials']['s3']['region_name'],
            "access_key_id": credentials['credentials']['s3']['access_key_id'],
            "secret_key": credentials['credentials']['s3']['secret_key'],
            "session_token": credentials['credentials']['s3']['session_token'],
            "expiration": credentials['credentials']['s3']['expiration']
        }

        # write the credentials to the file
        naas_credentials = os.path.expanduser(self.naas_credentials)
        existing_data = {}

        if os.path.exists(naas_credentials):
            with open(naas_credentials, 'r') as f:
                existing_data = json.load(f)
        # Ensure 'storage' key exists in existing_data
        if 'storage' not in existing_data:
            existing_data['storage'] = {}

        # Update the 'storage' key with new credentials
        existing_data['storage'].update({
            s3_credentials['workspace_id']: {
                    s3_credentials['storage_name']: {
                        s3_credentials["provider"]: {
                        "REGION_NAME": s3_credentials['region_name'],
                        "AWS_ACCESS_KEY_ID": s3_credentials['access_key_id'],
                        "AWS_SECRET_ACCESS_KEY": s3_credentials['secret_key'],
                        "AWS_SESSION_TOKEN": s3_credentials['session_token'],
                        "AWS_SESSION_EXPIRATION_TOKEN": s3_credentials['expiration']
                    }
                }
            }
        })
        with open(naas_credentials, 'w') as f:
            json.dump(existing_data, f)
        return ("generated s3 credentials.")

CI/CD Integration

Supports automatic CI/CD configuration generation:

        # Step 2.a: Build and push image to registry container if requested:
        if space_type == "docker":
            print(f"Building Docker Image for '{space_name}'...")
            os.system(
                f"docker build -t {registry.registry.uri}:latest -f {dockerfile_path} {docker_context}"
            )

            print("Pushing Docker Image...")
            os.system(f"docker push {registry.registry.uri}:latest")

        # Step 2.b: Create a new space on space.naas.ai
        print(f"Creating Naas Space '{space_name}'...")
        try:
            self.domain.create(
                name=space_name,
                domain=f"{space_name}.naas.ai",
                containers=[
                    {
                        "name": space_name,
                        "image": image if image else f"{registry.registry.uri}:latest",
                        "env": {},
                        "cpu": cpu,
                        "memory": memory,
                        "port": container_port,
                    }
                ],
            )
        except SpaceConflictError as e:
            print(
                f"A space with the name '{space_name}' already exists. Proceeding with existing space."
            )
            self.domain.get(name=space_name)
        # Step 3: Generate CI/CD configuration if requested
        if generate_ci:
            pipeline = Pipeline(name=f"ci-{space_name}")

            # Check for naas_python cli help command
            pipeline.add_job(
                "Validate that naas_python works",
                [
                    "name: Validate that naas_python works",
                    "run: |",
                    "  naas_python --help",
                ],
            )

            # Check Naas Space status
            pipeline.add_job(
                "Check Naas Space status",
                [
                    "name: Check Naas Space status",
                    "run: |",
                    f"  naas_python space get --name { space_name }",
                ],
            )

            # Check Naas Registry status
            pipeline.add_job(
                "Check Naas Registry status",
                [
                    "name: Check Naas Registry status",
                    "run: |",
                    f"  naas_python registry get --name { registry_name }",
                ],
            )

            # Add custom jobs for CI/CD configuration
            if space_type == "docker":
                # Retrieve credentials from registry and login into docker
                pipeline.add_job(
                    "Login to Docker Registry",
                    [
                        "name: Login to Docker Registry",
                        "run: |",
                        f"  naas_python registry docker-login --name { registry_name }",
                    ],
                )

                try:
                    _build_command = f"  docker build -t {registry.registry.uri}:latest -f {dockerfile_path} {docker_context}"
                except ValueError as e:
                    raise ValueError(
                        "When space_type is 'docker', dockerfile_path and docker_context must be provided. Please provide these values and try again"
                    ) from e

                docker_steps = [
                    "name: Build and Push Docker Image",
                    f'if: ${{ github.event_name == "push" }}',
                    "run: |",
                    _build_command,
                    f"  docker push { registry.registry.uri }:latest",
                ]
                pipeline.add_job("Build and Push Docker Image", docker_steps)
            # Render the CI/CD configuration
            pipeline.render()

            print("Generated CI/CD configuration.")

Installation

The project uses Poetry for dependency management:

[tool.poetry]
name = "naas-python"
version = "0.1.0"
description = "Naas Python SDK"
authors = ["Maxime Jublou <maxime@naas.ai>"]
license = "AGPL"
readme = "README.md"
packages = [{ include = "naas_python" }]

[tool.poetry.dependencies]
python = "^3.9"
typer = { extras = ["all"], version = "^0.9.0" }
requests = "^2.31.0"
cachetools = "^5.3.1"
jinja2 = "^3.0.1"
naas-models = "^1.11.2"
grpcio = "^1.60.0"
pydash = "^7.0.7"
boto3 = "^1.34.128"
pydantic = "<2.9"

[tool.poetry.group.dev.dependencies]
pytest = "^7.3.1"
requests = "^2.31.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
naas-python = "naas_python.main:main"

To install:

poetry install

Usage

As a Library

import naas_python as naas

# Space operations
naas.space.add(space_name="my-space")

# Storage operations
naas.storage.create(workspace_id="123", storage_name="my-storage")

# Asset operations
naas.asset.create(workspace_id="123", asset_creation=asset_data)

Command Line Interface

naas-python space add --name my-space
naas-python storage create --workspace-id 123 --name my-storage
naas-python asset create --workspace-id 123 --data asset_data.json

Development

Testing

Tests are written using pytest:

import pytest


def test_lib_add_import():
    import naas_python as naas

    # Test if ``naas.space.add`` is a valid method and callable

    assert callable(naas.space.add)


def test_missing_keys_call():
    import naas_python as naas

    # Test if ``naas.space.add`` is a valid method and callable

    with pytest.raises(TypeError):
        naas.space.add()

Run tests with:

make test

Git Hooks

The project includes pre-commit hooks for running tests:

#!/bin/sh

# Execute the make target
make test

# Capture the exit status of the make command
STATUS=$?

# If the make command fails, exit with the same status
if [ $STATUS -ne 0 ]; then
    echo "Pre-commit hook failed: make test failed with status $STATUS"
    exit $STATUS
fi

# If the make command succeeds, allow the commit to proceed
exit 0

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0):

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests (make test)
  5. Submit a pull request

The project uses semantic release for versioning:

name: Release
on:
  push:
    branches:
      - main
      - dev

jobs:
  release:
    name: Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 'lts/*'
      - name: Install dependencies
        run: |
          cd .github \
            && npm ci \
            && cd .. \
            && (rm package.json || true) \
            && (rm package-lock.json || true) \
            && ln -s .github/node_modules node_modules \
            && ln -s .github/package.json package.json \
            && ln -s .github/package-lock.json package-lock.json \
            && ln -s .github/.releaserc .releaserc
      - name: Release
        env:
#          GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # Use this if you need to trigger CI/CD based on new release being published.
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
        run: npx semantic-release

Error Handling

The project implements custom exceptions for different error scenarios:

class AssetNotFound(NaasException): pass
class AssetConflictError(NaasException): pass
class AssetRequestError(NaasException): pass

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

naas_python-1.4.4.tar.gz (47.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

naas_python-1.4.4-py3-none-any.whl (67.8 kB view details)

Uploaded Python 3

File details

Details for the file naas_python-1.4.4.tar.gz.

File metadata

  • Download URL: naas_python-1.4.4.tar.gz
  • Upload date:
  • Size: 47.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for naas_python-1.4.4.tar.gz
Algorithm Hash digest
SHA256 ee51c9a5022568a99fa9a66f3ea1505965a9adc84d57d3f88db4ba169a879c8b
MD5 dc2cb04c57c78ca86146c2aa4f55af1c
BLAKE2b-256 df6af393de09f9971ae72c9f9992f81046c4c13abeebc4543345a05096dfb259

See more details on using hashes here.

File details

Details for the file naas_python-1.4.4-py3-none-any.whl.

File metadata

  • Download URL: naas_python-1.4.4-py3-none-any.whl
  • Upload date:
  • Size: 67.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for naas_python-1.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 b751de5f43682b1047dab0c2ed8076ca117aaa3bf1fee01ee6fb4851250fcd1d
MD5 a70d444116a0693cf806cad564e9e912
BLAKE2b-256 801fc4333ab67a3128257ba4a760702a3ac12e776d7d7f2c429cc8c4bd0f3952

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page