digitalhub-cli-sdk

module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: Apache-2.0

README ΒΆ

digitalhub-cli-sdk

A Go SDK for interacting programmatically with DigitalHub Core API.

It provides programmatic access to the same capabilities offered by the DigitalHub CLI (dhcli), but is completely standalone and can be imported into any Go application or microservice.


✨ Highlights

  • Official Core API v1 support
  • CRUD operations on all core resources (projects, artifacts, functions, runs, tasks, etc.)
  • Function execution (RunService)
  • Stop / Resume for runnable resources
  • Logs and Metrics retrieval (same semantics as dhcli)
  • S3-compatible transfer (Upload/Download via MinIO / AWS S3)
  • Task auto-creation for Run (e.g. python+job β†’ python+job:run)
  • Backwards compatible with dhcli logic
  • No CLI dependency
  • Usable standalone inside external Go applications

πŸ“¦ Installation

go get github.com/scc-digitalhub/digitalhub-cli-sdk

πŸ”§ Configuration

The SDK uses the same configuration model as the DigitalHub CLI. Configuration can be provided via environment variables (recommended for external programs).

package main

import (
	"os"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:      os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:   os.Getenv("DHCORE_API_VERSION"),
			AccessToken:  os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
	}
	_ = cfg
}
Required environment variables
Variable Example
DHCORE_ENDPOINT https://core.dev.atlas.fbk.eu
DHCORE_API_VERSION v1
DHCORE_ACCESS_TOKEN eyJhbGciOi...
Optional S3/MinIO variables (TransferService)

These are used when you call transfer.Upload(...) / transfer.Download(...).

Variable Example
AWS_ACCESS_KEY_ID A68K...
AWS_SECRET_ACCESS_KEY ...
AWS_SESSION_TOKEN ...
AWS_REGION us-east-1
AWS_ENDPOINT_URL https://minio-api.dev.atlas.fbk.eu
S3_BUCKET datalake

Note: The SDK config struct uses config.S3Config{...}; you can map env vars however you prefer in your app.


πŸš€ Usage Examples

In the examples below, endpoint is the Core resource endpoint (e.g. "artifacts", "functions", "runs"). If you start from CLI-like resource names, you can reuse the same mapping used by the CLI:

endpoint := utils.TranslateEndpoint(resource) // e.g. "runs" -> "run"

CRUD: list resources (all pages)
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/services/crud"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:     os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:  os.Getenv("DHCORE_API_VERSION"),
			AccessToken: os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
	}

	ctx := context.Background()

	svc, err := crud.NewCrudService(ctx, cfg)
	if err != nil {
		panic(err)
	}

	items, _, err := svc.ListAllPages(ctx, crud.ListRequest{
		ResourceRequest: crud.ResourceRequest{
			Project:  "project-name",
			Resource: "artifacts",
		},
		Params: map[string]string{
			"size": "200",
			"sort": "updated,asc",
		},
	})
	if err != nil {
		panic(err)
	}

	out, _ := json.MarshalIndent(items, "", "  ")
	fmt.Println(string(out))
}

CRUD: get a resource
Get by ID
body, _, err := svc.Get(ctx, crud.GetRequest{
	ResourceRequest: crud.ResourceRequest{
		Project:  "project-name",
		Resource: "artifacts",
	},
	ID: "artifact-id",
})
_ = body
_ = err
Get latest version by name (CLI-compatible semantics)
body, _, err := svc.Get(ctx, crud.GetRequest{
	ResourceRequest: crud.ResourceRequest{
		Project:  "project-name",
		Resource: "artifacts",
	},
	// When ID is empty, Get uses:
	// ?name=<name>&versions=latest
	Name: "my-artifact",
})
_ = body
_ = err

CRUD: create a resource (from file or name)
package main

import (
	"context"
	"os"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/services/crud"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:     os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:  os.Getenv("DHCORE_API_VERSION"),
			AccessToken: os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
	}

	ctx := context.Background()
	svc, err := crud.NewCrudService(ctx, cfg)
	if err != nil {
		panic(err)
	}

	err = svc.Create(ctx, crud.CreateRequest{
		ResourceRequest: crud.ResourceRequest{
			Project:  "project-name",
			Resource: "artifacts",
		},
		// One of:
		// - FilePath: "/path/to/resource.yaml"
		// - Name:     "my-resource"
		FilePath: "/path/to/artifact.yaml",
		ResetID:  false,
	})
	if err != nil {
		panic(err)
	}
}

CRUD: update a resource (raw JSON body)
body := []byte(`{"project":"project-name","kind":"...","spec":{"foo":"bar"}}`)

err := svc.Update(ctx, crud.UpdateRequest{
	ResourceRequest: crud.ResourceRequest{
		Project:  "project-name",
		Resource: "artifacts",
	},
	ID:   "artifact-id",
	Body: body,
})
if err != nil {
	panic(err)
}

CRUD: delete a resource (by ID or by name)
// Delete by ID
err := svc.Delete(ctx, crud.DeleteRequest{
	ResourceRequest: crud.ResourceRequest{
		Project:  "project-name",
		Resource: "artifacts",
	},
	ID:      "artifact-id",
	Cascade: false,
})
if err != nil {
	panic(err)
}

// Delete all versions by name
err = svc.Delete(ctx, crud.DeleteRequest{
	ResourceRequest: crud.ResourceRequest{
		Project:  "project-name",
		Resource: "artifacts",
	},
	Name:    "my-artifact",
	Cascade: false,
})
if err != nil {
	panic(err)
}

▢️ Run / Stop / Resume (RunService)

The run service replicates the CLI behavior:

  • resolves functions by name or ID
  • finds or creates tasks
  • creates runs with the correct kind (e.g. python+job β†’ python+job:run)
package main

import (
	"context"
	"os"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/services/run"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:     os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:  os.Getenv("DHCORE_API_VERSION"),
			AccessToken: os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
	}

	ctx := context.Background()

	runSvc, err := run.NewRunService(ctx, cfg)
	if err != nil {
		panic(err)
	}

	// Create a run (kind is normalized to "<taskKind>:run")
	req := run.RunRequest{
		Project:      "project-name",
		TaskKind:     "python+job",   // normalized to "python+job:run"
		FunctionName: "test-run",     // or FunctionID
		// FunctionID: "....",
		InputSpec: map[string]any{
			"input": 123,
		},
	}
	if err := runSvc.Run(ctx, req); err != nil {
		panic(err)
	}

	// Stop / Resume a runnable resource (e.g. runs)
	_, _, _ = runSvc.Stop(ctx, run.StopRequest{
		RunResourceRequest: run.RunResourceRequest{
			Project:  "project-name",
			Resource: "runs",
			ID:       "run-id",
		},
	})

	_, _, _ = runSvc.Resume(ctx, run.ResumeRequest{
		RunResourceRequest: run.RunResourceRequest{
			Project:  "project-name",
			Resource: "runs",
			ID:       "run-id",
		},
	})
}

The SDK automatically generates the correct run kind:

{
  "kind": "python+job:run"
}

πŸ“œ Logs (CLI-compatible semantics)

Logs are retrieved via the same /logs API used by the CLI, and the default container name is inferred from spec.task if you don’t provide one.

package main

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/services/run"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:     os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:  os.Getenv("DHCORE_API_VERSION"),
			AccessToken: os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
	}

	ctx := context.Background()
	svc, err := run.NewRunService(ctx, cfg)
	if err != nil {
		panic(err)
	}

	// 1) Get logs list
	logBody, _, err := svc.GetLogs(ctx, run.LogRequest{
		RunResourceRequest: run.RunResourceRequest{
			Project:  "project-name",
			Resource: "runs",
			ID:       "run-id",
		},
	})
	if err != nil {
		panic(err)
	}

	// 2) Parse logs and print the main container (same as CLI)
	var logs []interface{}
	if err := json.Unmarshal(logBody, &logs); err != nil {
		panic(err)
	}

	// Compute default container like the CLI:
	// - GET resource to read spec.task
	// - container name is: c-<taskFormatted>-<id>
	resBody, _, err := svc.GetResource(ctx, run.LogRequest{
		RunResourceRequest: run.RunResourceRequest{
			Project:  "project-name",
			Resource: "runs",
			ID:       "run-id",
		},
	})
	if err != nil {
		panic(err)
	}
	var m map[string]interface{}
	_ = json.Unmarshal(resBody, &m)

	spec := m["spec"].(map[string]interface{})
	task := spec["task"].(string)
	taskFormatted := strings.ReplaceAll(task[:strings.Index(task, ":")], "+", "")
	mainContainer := fmt.Sprintf("c-%s-%s", taskFormatted, "run-id")

	// Find matching entry and print base64 decoded content
	for _, e := range logs {
		em := e.(map[string]interface{})
		st := em["status"].(map[string]interface{})
		if st["container"] == mainContainer {
			contentB64 := em["content"].(string)
			raw, _ := base64.StdEncoding.DecodeString(contentB64)
			fmt.Println(string(raw))
			break
		}
	}
}

Tip: if you already know the container name, you can directly filter the logs list by status.container.


πŸ“ˆ Metrics (CLI-compatible semantics)

Metrics are read from the selected container log entry (status.metrics) exactly like the CLI.

package main

import (
	"context"
	"os"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/services/run"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:     os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:  os.Getenv("DHCORE_API_VERSION"),
			AccessToken: os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
	}

	ctx := context.Background()
	svc, err := run.NewRunService(ctx, cfg)
	if err != nil {
		panic(err)
	}

	// Prints pretty JSON or "No metrics for this run."
	if err := svc.PrintMetrics(ctx, run.MetricsRequest{
		RunResourceRequest: run.RunResourceRequest{
			Project:  "project-name",
			Resource: "runs",
			ID:       "run-id",
		},
		// Container: "" // optional; if empty, infer main container like CLI
	}); err != nil {
		panic(err)
	}
}

⬆️⬇️ Upload / Download (S3 / MinIO)

The transfer service uses:

  • Core API to discover/download/upload resources
  • S3-compatible object storage for the actual file transfer
package main

import (
	"context"
	"os"

	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/config"
	"github.com/scc-digitalhub/digitalhub-cli-sdk/sdk/services/transfer"
)

func main() {
	cfg := config.Config{
		Core: config.CoreConfig{
			BaseURL:     os.Getenv("DHCORE_ENDPOINT"),
			APIVersion:  os.Getenv("DHCORE_API_VERSION"),
			AccessToken: os.Getenv("DHCORE_ACCESS_TOKEN"),
		},
		S3: config.S3Config{
			AccessKey:   os.Getenv("AWS_ACCESS_KEY_ID"),
			SecretKey:   os.Getenv("AWS_SECRET_ACCESS_KEY"),
			AccessToken: os.Getenv("AWS_SESSION_TOKEN"),
			Region:      os.Getenv("AWS_REGION"),
			EndpointURL: os.Getenv("AWS_ENDPOINT_URL"),
		},
	}

	ctx := context.Background()

	tr, err := transfer.NewTransferService(ctx, cfg)
	if err != nil {
		panic(err)
	}

	// Upload (bucket is optional; your CLI uses "datalake" as default)
	_, err = tr.Upload(ctx, "artifacts", transfer.UploadRequest{
		Project:  "project-name",
		Resource: "artifacts",
		Name:     "my-artifact",
		Input:    "/tmp/data.bin",
		Verbose:  true,
		Bucket:   os.Getenv("S3_BUCKET"),
	})
	if err != nil {
		panic(err)
	}

	// Download
	_, err = tr.Download(ctx, "artifacts", transfer.DownloadRequest{
		Project:     "project-name",
		Resource:    "artifacts",
		Name:        "my-artifact",
		Destination: "/tmp/out",
		Verbose:     true,
	})
	if err != nil {
		panic(err)
	}
}

πŸ§ͺ Running integration tests

export DHCORE_ENDPOINT=...
export DHCORE_API_VERSION=v1
export DHCORE_ACCESS_TOKEN=...

go test ./...

πŸ“ Repository layout

sdk/
  config/
  services/
    crud/
    run/
    transfer/
  utils/

The SDK is fully self-contained under /sdk and can be imported independently of the CLI.


πŸͺͺ License

Apache-2.0

Directories ΒΆ

Path Synopsis
sdk

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL