Documentation
¶
Overview ¶
Package client is a thin wrapper over the Hamravesh Darkube REST API.
Authentication is a two-part scheme discovered against the live API:
Authorization: Api-key <account-token> X-Organization: <tenant-slug>
The account token identifies the user; X-Organization scopes every request to one tenant (organization). Requests without a valid X-Organization are rejected with 403 permission_denied even though the token itself is valid.
Index ¶
- Constants
- Variables
- func ErrorCode(err error) string
- func ExternalHosts(app map[string]any) []string
- func IsNotFound(err error) bool
- func IsTransient(err error) bool
- func SecretEnvNames(app map[string]any) []string
- func SetEnvVars(app map[string]any, envs []EnvVar)
- func SetExternalHosts(app map[string]any, hosts []string)
- type APIError
- type Alert
- type App
- type Auth
- type Certificate
- type Client
- func (c *Client) Alerts(ctx context.Context) ([]Alert, error)
- func (c *Client) AppLogs(ctx context.Context, appID string, opts LogOptions) ([]LogEntry, int, error)
- func (c *Client) Close() error
- func (c *Client) CreateApp(ctx context.Context, in CreateAppInput) (map[string]any, error)
- func (c *Client) DeleteApp(ctx context.Context, id string) error
- func (c *Client) DeployToken(ctx context.Context, id string) (string, error)
- func (c *Client) GetApp(ctx context.Context, id string) (map[string]any, error)
- func (c *Client) GetAppTyped(ctx context.Context, id string) (*App, error)
- func (c *Client) ListApps(ctx context.Context) ([]App, error)
- func (c *Client) ListCertificates(ctx context.Context) ([]Certificate, error)
- func (c *Client) ListNamespaces(ctx context.Context) ([]Namespace, error)
- func (c *Client) ListPlans(ctx context.Context) ([]Plan, error)
- func (c *Client) Namespaces(ctx context.Context) ([]Namespace, error)
- func (c *Client) NamespacesFromApps(ctx context.Context) ([]Namespace, error)
- func (c *Client) Notifications(ctx context.Context, limit int) ([]Notification, error)
- func (c *Client) Organization(ctx context.Context) (*Organization, error)
- func (c *Client) OrganizationID(ctx context.Context) (int, error)
- func (c *Client) PrepareAppUpdate(ctx context.Context, id string, mutate func(app map[string]any) error) (map[string]any, map[string]any, error)
- func (c *Client) Profile(ctx context.Context) (*Profile, error)
- func (c *Client) ResolveApp(ctx context.Context, nameOrID string) (*App, error)
- func (c *Client) UpdateApp(ctx context.Context, id string, mutate func(app map[string]any) error) (map[string]any, error)
- type Cluster
- type CreateAppInput
- type Disk
- type EnvVar
- type GitSource
- type LogEntry
- type LogOptions
- type Namespace
- type Notification
- type Organization
- type Partition
- type Plan
- type PlanDetail
- type Port
- type Profile
- type State
Constants ¶
const ( // CodeSameHelmReleaseName means the name is taken by a Helm release in the // target namespace. Deleting an app drops its record immediately but can // leave the release behind, so this also fires for names with no app. CodeSameHelmReleaseName = "SameHelmReleaseNameExists" // CodeTerminatingApp means an app of this name is still being deleted. CodeTerminatingApp = "TerminatingAppException" // CodeDuplicateReleaseAndNamespace means a live app of this name already // exists in the namespace. The API distinguishes this from // CodeSameHelmReleaseName, which is the orphaned-release case. CodeDuplicateReleaseAndNamespace = "DuplicateReleaseAndNamespaceException" // CodeGithubAuth and CodeGitlabAuth mean the git provider has not been // connected to this Hamravesh account, so Darkube cannot read the repository // it is being asked to build. Creating a git-backed app has an account-level // prerequisite that creating an image-backed one does not. CodeGithubAuth = "GithubAuthException" CodeGitlabAuth = "GitlabAuthException" )
API error codes worth handling by name. The `detail` that accompanies them is Persian prose, so matching the code is both more robust and more readable.
const ( CreationMethodDockerImage = "docker_image" CreationMethodGitRepoURL = "git_repo_url" ProviderGithub = "Github" ProviderGitlab = "Gitlab" BuilderDockerfile = "dockerfile" BuilderHerokuBuildpack = "heroku_buildpacks" BuildMethodGitlabCI = "gitlabci" BuildMethodWebhook = "webhook" )
Valid values for the enumerated build fields, as advertised by OPTIONS on /api/v1/darkube/apps/ (confirmed 2026-08-28).
const DefaultBaseURL = "https://api.hamravesh.com"
DefaultBaseURL is the public Hamravesh API host.
Variables ¶
var ( ErrAppNotFound = errors.New("no app named or with that id") ErrAppAmbiguous = errors.New("app name is ambiguous") )
Sentinel errors returned by ResolveApp, comparable with errors.Is.
var ErrNoOrganizationID = errors.New("could not determine the numeric organization id")
ErrNoOrganizationID is returned when the numeric org id can't be derived.
var ErrNoSuchEnv = errors.New("no such environment variable")
ErrNoSuchEnv is returned when removing an environment variable that is absent.
var ErrNoSuchHost = errors.New("no such domain")
ErrNoSuchHost is returned when removing a domain the app does not serve.
Functions ¶
func ErrorCode ¶ added in v0.2.0
ErrorCode returns the API error code carried by err, or "" if err is not an *APIError.
func ExternalHosts ¶ added in v0.4.0
ExternalHosts reads the domains routed to an app. This is where custom domains actually live; custom_domain_address is a separate, usually empty field and is not the ingress host list.
func IsNotFound ¶ added in v0.5.0
IsNotFound reports whether err is an API 404. This is the distinction `wait --for deleted` turns on: "the app is gone" versus "the request failed".
func IsTransient ¶ added in v0.5.0
IsTransient reports whether err is worth retrying: a transport failure, or a 5xx from the API. Polling loops use it so that a flaky minute — which this API does produce — does not abort a wait that would otherwise have succeeded.
func SecretEnvNames ¶ added in v0.4.0
SecretEnvNames reads the names of an app's secret environment variables.
Only names are ever available: the API stores the values in a vault and returns them empty on every read, so there is nothing to unmask.
func SetEnvVars ¶ added in v0.4.0
SetEnvVars replaces an app's plain environment variables.
func SetExternalHosts ¶ added in v0.4.0
SetExternalHosts replaces the domains routed to an app.
Types ¶
type APIError ¶
type APIError struct {
StatusCode int `json:"-"`
Detail string `json:"detail"`
Code string `json:"code"`
}
APIError is a structured error returned by the API's DRF backend.
type Alert ¶ added in v0.4.0
type Alert struct {
ID string `json:"id"`
AlertName string `json:"alertname"`
Status string `json:"status"`
Severity string `json:"severity"`
Instance string `json:"instance"`
ServiceOwner string `json:"service_owner"`
Condition string `json:"condition"`
AlertDescription string `json:"alert_description"`
DescriptionFA string `json:"description_fa"`
TimeWindow string `json:"timewindow"`
StartsAt string `json:"starts_at"`
EndsAt string `json:"ends_at"`
ActionLink string `json:"action_link"`
ForMe bool `json:"for_me"`
}
Alert is one monitoring alert raised against the tenant's resources.
type App ¶
type App struct {
ID string `json:"id"`
Name string `json:"name"`
Namespace Namespace `json:"namespace"`
State State `json:"state"`
Plan *Plan `json:"plan"`
Replicas int `json:"replicas"`
IsEnabled bool `json:"is_enabled"`
IsDeployable bool `json:"is_deployable"`
IsHPAEnabled bool `json:"is_hpa_enabled"`
RAMLimit string `json:"ram_limit"`
CPURequest string `json:"cpu_request"`
CustomDomainAddress string `json:"custom_domain_address"`
EnableSSL bool `json:"enable_SSL"`
ImageRepo string `json:"image_repo"`
ImageTag string `json:"image_tag"`
CreationTime string `json:"creation_time"`
UpdatedAt string `json:"updated_at"`
// CreationMethod is how the app came to exist, and it is the field that
// separates a plain workload from a managed service. There is no separate
// "managed services" API: a oneclick Redis, Postgres, Grafana or Prometheus is
// an ordinary app in this same list, carrying creation_method "redisnew",
// "postgresqlnew", "grafana", "prometheus" and so on. Without surfacing it
// there is no way to tell the two apart, which is why `get apps -o wide`
// prints it and `--type` filters on it.
CreationMethod string `json:"creation_method"`
}
App is a Darkube application (maps to a Kubernetes workload).
Only the commonly used fields are typed; `darkubectl describe`/`-o json` read the raw object so no data is lost to this partial view.
func (App) Image ¶ added in v0.5.0
Image is the container image the app runs, as repo:tag. The v2 list route returns the whole object, so this costs no extra request — which is what makes "which build is each app on?" answerable for a whole namespace at once.
func (App) IsManagedService ¶ added in v0.5.0
IsManagedService reports whether the app is a marketplace/oneclick service (Redis, Postgres, Grafana, …) rather than a workload someone built.
type Auth ¶
type Auth string
Auth is an HTTP Authorization header value. The Darkube API accepts two schemes: an account Api-key, or a Console JWT (Bearer) obtained from login.
func BearerToken ¶
BearerToken builds Bearer-scheme (JWT) authorization from a login access token.
type Certificate ¶
type Certificate struct {
ID string `json:"id"`
Name string `json:"name"`
CommonName string `json:"common_name"`
State string `json:"state"`
Domain string `json:"domain"`
}
Certificate is a TLS certificate entry. The certificates endpoint uses a different envelope than the paginated ones: {"data":{"items":[...]}}.
type Client ¶
Client talks to the Darkube API for a single tenant.
func (*Client) Alerts ¶ added in v0.4.0
Alerts returns the tenant's monitoring alerts, both firing and resolved.
func (*Client) AppLogs ¶ added in v0.2.0
func (c *Client) AppLogs(ctx context.Context, appID string, opts LogOptions) ([]LogEntry, int, error)
AppLogs returns the tail of one container's log, oldest entry first.
The endpoint is index-based rather than time-based: from_index/to_index bound a window and reference_index anchors it. Reading the tail means asking for a window past the end and letting the server clamp, which is what the console does.
func (*Client) CreateApp ¶
CreateApp creates a Docker-image app and returns the created object. The payload mirrors the console's confirmed POST /api/v1/darkube/apps/ request.
func (*Client) DeployToken ¶ added in v0.3.0
DeployToken returns an app's CI trigger deploy token.
This is the credential `darkube deploy --token` wants in a pipeline, paired with the app's own id as --app-id. The console shows it on the app's CI/CD page and it is stored nowhere in the cluster, so the API is the only way to wire up CI without the web UI.
It is deliberately not a field on App: `get apps -o json` would then print every app's deploy token, which is not what asking for a list of apps means.
func (*Client) GetApp ¶
GetApp returns the full raw app object by UUID. The result is a generic map so every field is preserved for `describe` / `-o json|yaml`.
func (*Client) GetAppTyped ¶
GetAppTyped returns a single app decoded into the typed App struct.
func (*Client) ListCertificates ¶
func (c *Client) ListCertificates(ctx context.Context) ([]Certificate, error)
ListCertificates returns TLS certificates for the current tenant.
func (*Client) ListNamespaces ¶ added in v0.2.0
ListNamespaces returns every namespace (project) in the current tenant, including ones that hold no apps yet. Requires a JWT.
func (*Client) Namespaces ¶ added in v0.2.0
Namespaces returns the tenant's namespaces, preferring the dedicated endpoint and falling back to deriving them from apps.
The distinction matters: the derived list can only contain namespaces that already hold at least one app, so a freshly created, still-empty project is invisible to it — which is exactly when you need to look one up in order to create the first app in it.
func (*Client) NamespacesFromApps ¶
NamespacesFromApps derives the set of namespaces (projects) visible in the current tenant from the app list. This is the fallback for credentials that cannot reach the dedicated endpoint; it omits namespaces with no apps.
func (*Client) Notifications ¶ added in v0.4.0
Notifications returns the most recent notifications, newest first, following pagination up to limit entries.
func (*Client) Organization ¶ added in v0.4.0
func (c *Client) Organization(ctx context.Context) (*Organization, error)
Organization returns the current tenant's entry from the user's profile, matching on the slug carried in the X-Organization header.
func (*Client) OrganizationID ¶
OrganizationID returns the current tenant's numeric organization id, which the create payload requires.
The user profile is the authoritative source and, crucially, works for a tenant that holds no apps yet — the case the app-detail fallback below cannot serve, which used to make it impossible to create the *first* app in a new organization.
func (*Client) PrepareAppUpdate ¶ added in v0.5.0
func (c *Client) PrepareAppUpdate( ctx context.Context, id string, mutate func(app map[string]any) error, ) (map[string]any, map[string]any, error)
PrepareAppUpdate performs every step of UpdateApp except the write, returning the normalized object as it stands now and as it would be sent.
This is what makes a dry run possible, and it matters more here than it would on an API with a real partial update: every write is a full-object PUT rebuilt from a read, so a mistake in the mutate step can rewrite fields nobody meant to touch, and the request body alone does not show which of its seventy fields actually changed.
func (*Client) Profile ¶ added in v0.4.0
Profile returns the signed-in user and the tenants they belong to.
func (*Client) ResolveApp ¶
ResolveApp finds an app by UUID or by exact name within the current tenant. Names are not guaranteed unique across namespaces; an ambiguous name is an error.
func (*Client) UpdateApp ¶ added in v0.4.0
func (c *Client) UpdateApp(ctx context.Context, id string, mutate func(app map[string]any) error) (map[string]any, error)
UpdateApp applies mutate to an app's current state and writes it back.
The API has no partial update, so this is a read-modify-write: the current object is fetched, normalized into the shape the write serializer accepts, handed to mutate, and PUT in full. Callers therefore change one field without having to reconstruct the other seventy.
type Cluster ¶
type Cluster struct {
ID int `json:"id"`
Name string `json:"name"`
LocationCountry string `json:"location_country"`
IsOnPremise bool `json:"is_on_premise"`
}
Cluster is the physical cluster an app's namespace lives on.
type CreateAppInput ¶
type CreateAppInput struct {
Name string
NamespaceID int
OrganizationID int
PlanID string
ImageRepo string
ImageTag string
Command string
Args string
Replicas int
SvcType string
Ports map[string]Port
Disk *Disk
Envs []EnvVar
SecretEnvs []EnvVar
// Git, when non-nil, switches the app from pulling a prebuilt image to being
// built by Darkube from a repository. ImageRepo/ImageTag are then chosen by
// the platform (it pushes to registry.hamdocker.ir/<tenant>/<app>) and must
// be left empty.
Git *GitSource
}
CreateAppInput describes a Docker-image app to create. Names have already been resolved to ids by the caller.
Everything below Replicas is optional. It can also be changed afterwards through UpdateApp, which PUTs the whole object back — the API has no partial update, but it is not read-only either.
type Disk ¶ added in v0.2.0
type Disk struct {
Partitions []Partition `json:"partitions" yaml:"partitions"`
SizeInGi int `json:"size_in_Gi" yaml:"sizeInGi"`
StorageClassName string `json:"storage_class_name,omitempty" yaml:"storageClassName"`
SetFSGroup bool `json:"set_fsgroup" yaml:"setFsGroup"`
}
Disk is the app's persistent volume. SizeInGi of 0 means "no disk".
type EnvVar ¶ added in v0.2.0
type EnvVar struct {
Name string `json:"name" yaml:"name"`
Value string `json:"value" yaml:"value"`
}
EnvVar is one entry of envs or secret_envs. The API calls the key "name".
type GitSource ¶ added in v0.5.0
type GitSource struct {
RepoURL string
Branch string
Provider string // Github | Gitlab
Dockerfile string
Context string
Workdir string
Builder string // dockerfile | heroku_buildpacks
// BuildMethod is gitlabci or webhook. Autodeploy has a pointer type so an
// explicit false is distinguishable from "unset".
BuildMethod string
Autodeploy *bool
}
GitSource describes a repository Darkube builds the app from, producing creation_method "git_repo_url" rather than "docker_image".
Every field maps to one the API returns on such an app; the zero values are filled from the defaults the console uses, so RepoURL alone is enough.
type LogEntry ¶ added in v0.2.0
type LogEntry struct {
Timestamp string // RFC3339 with nanoseconds, as sent by the API
Text string
}
LogEntry is one log record: the container's stdout/stderr at a point in time. The API returns a timestamp-keyed object, so entries carry their own key.
type LogOptions ¶ added in v0.2.0
type LogOptions struct {
PodName string
Container string
Tail int // number of lines back from the newest
Previous bool // the previous container instance, i.e. what a crashloop left behind
}
LogOptions selects which slice of an app's log to read.
type Namespace ¶
type Namespace struct {
ID int `json:"id"`
Name string `json:"name"`
Cluster Cluster `json:"cluster"`
}
Namespace maps to a Darkube "project" (a Kubernetes namespace).
type Notification ¶ added in v0.4.0
type Notification struct {
Slug int `json:"slug"`
Title string `json:"title"`
Description string `json:"description"`
Timestamp string `json:"timestamp"`
TargetType string `json:"target_type"`
ActionURL string `json:"action_url"`
ActionLabel string `json:"action_label"`
Unread bool `json:"unread"`
Public bool `json:"public"`
}
Notification is one entry of the account's notification feed. Titles and descriptions are Persian prose, and the description carries HTML.
type Organization ¶ added in v0.4.0
type Organization struct {
ID int `json:"id"`
Name string `json:"name"`
Roles []string `json:"current_user_roles"`
}
Organization is a tenant the signed-in user belongs to.
ID is the numeric primary key the app create/update payload calls "organization"; Name is the slug sent as the X-Organization header.
type Partition ¶ added in v0.2.0
type Partition struct {
DisplayName string `json:"display_name" yaml:"name"`
MountPath string `json:"mount_path" yaml:"mountPath"`
SubPath string `json:"sub_path" yaml:"subPath"`
}
Partition is one mount inside a Disk.
type Plan ¶
type Plan struct {
ID string `json:"id"`
Name string `json:"name"`
CodeName string `json:"code_name"`
PlanType string `json:"plan_type"`
CostType string `json:"cost_type"`
ShowInCreateApp bool `json:"show_in_create_app"`
Detail PlanDetail `json:"detail"`
Cluster *Cluster `json:"cluster"`
}
Plan is a resource/pricing plan.
func (Plan) IsCreatable ¶
IsCreatable reports whether a plan can be picked when creating an app.
type PlanDetail ¶
PlanDetail holds an app plan's resource sizing (megabytes / millicores).
type Port ¶ added in v0.2.0
type Port struct {
ContainerPort int `json:"containerPort" yaml:"containerPort"`
ServicePort int `json:"servicePort" yaml:"servicePort"`
Protocol string `json:"protocol" yaml:"protocol"`
}
Port is one entry of svc.ports, keyed by name in the API ("main", "amqp", …).