Skip to content
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Release (2026-MM-DD)

- `functions`:
- [v0.1.0](services/functions/CHANGELOG.md#v010)
- `v1alphaapi`:
- **Feature**: initial release
- `telemetrylink`:
- [v0.6.0](services/telemetrylink/CHANGELOG.md#v060)
- `v1api`:
Expand Down Expand Up @@ -4840,4 +4844,4 @@ List of modules:
- `ske`: [v0.7.0](services/ske/CHANGELOG.md#v070-2023-12-05)
- Manage your STACKIT Kubernetes Engine resources: `Project`, `Cluster`, `Credentials`, `Options`
- Waiters for async operations: `CreateOrUpdateClusterWaitHandler`, `DeleteClusterWaitHandler`, `CreateProjectWaitHandler`, `DeleteProjectWaitHandler`, `RotateCredentialsWaitHandler`
- [Usage example](https://github.com/stackitcloud/stackit-sdk-go/tree/main/examples/ske)
- [Usage example](https://github.com/stackitcloud/stackit-sdk-go/tree/main/examples/ske)
108 changes: 108 additions & 0 deletions examples/functions/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package main

import (
"context"
"fmt"
"os"

functions "github.com/stackitcloud/stackit-sdk-go/services/functions/v1alphaapi"
wait "github.com/stackitcloud/stackit-sdk-go/services/functions/v1alphaapi/wait"
)

func main() {
projectId := "PROJECT_ID" // the uuid of your STACKIT project

// Create a new API client, that uses default authentication and configuration
functionsClient, err := functions.NewAPIClient()
if err != nil {
fmt.Fprintf(os.Stderr, "Creating API client: %v\n", err)
os.Exit(1)
}

// List functions
functionsList, err := functionsClient.DefaultAPI.ListFunctions(context.Background(), projectId).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error listing functions: %v\n", err)
os.Exit(1)
}
_, err = fmt.Fprintf(os.Stdout, "Function count: %d\n", len(functionsList.Functions))
if err != nil {
fmt.Fprintf(os.Stderr, "Error when printing out function count: %v\n", err)
os.Exit(1)
}

// List pull-credentials
pull_credentials, err := functionsClient.DefaultAPI.ListPullCredentials(context.Background(), projectId).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when listing pull-credentials count: %v\n", err)
os.Exit(1)
}
_, err = fmt.Fprintf(os.Stdout, "Pull-credentials count: %d\n", len(pull_credentials.PullCredentials))
if err != nil {
fmt.Fprintf(os.Stderr, "Error when printing out pull-credentials count: %v\n", err)
os.Exit(1)
}

// Create pull-credential
payload := *functions.NewCreatePullCredentialPayload("ghcr.io", "MY_TOKEN", "MY_USERNAME")
pull_credential, err := functionsClient.DefaultAPI.CreatePullCredential(context.Background(), projectId).CreatePullCredentialPayload(payload).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "erorr creating pull-credential: %v\n", err)
os.Exit(1)
}
_, err = fmt.Fprintf(os.Stdout, "Created Pull-credentials address: %d\n", len(pull_credential.Address))
if err != nil {
fmt.Fprintf(os.Stderr, "can not print out function pull-credentials address: %v\n", err)
os.Exit(1)
}

// List pull-credentials
pull_credentials, err = functionsClient.DefaultAPI.ListPullCredentials(context.Background(), projectId).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `ListPullCredentials`: %v\n", err)
os.Exit(1)
}
_, err = fmt.Fprintf(os.Stdout, "Pull-credentials count: %d\n", len(pull_credentials.PullCredentials))
if err != nil {
fmt.Fprintf(os.Stderr, "Error when printing out pull-credentials count: %v\n", err)
os.Exit(1)
}

// Create function deployment
functionData := *functions.NewCreateFunctionPayload("FUNKY_FUNC")
function, err := functionsClient.DefaultAPI.CreateFunction(context.Background(), projectId).CreateFunctionPayload(functionData).Execute()
if err != nil || function.Id == nil {
fmt.Fprintf(os.Stderr, "Error when calling `CreateFunction`: %v\n", err)
os.Exit(1)
}
_, err = fmt.Printf("Function with id %s created\n", *function.Id)
if err != nil {
fmt.Fprintf(os.Stderr, "Error printing out function: %v\n", err)
os.Exit(1)
}

// Create function revision deployment
revisionData := *functions.NewCreateFunctionRevisionPayload(*functions.NewResourceLimits(100, "f2"), "ghcr.io/stackitcloud/example-image:0.1.0")
functionRevision, err := functionsClient.DefaultAPI.CreateFunctionRevision(context.Background(), projectId, *function.Id).CreateFunctionRevisionPayload(revisionData).Execute()
if err != nil || functionRevision.Id == nil {
fmt.Fprintf(os.Stderr, "Error when calling `CreateFunctionRevision`: %v\n", err)
os.Exit(1)
}
_, err = fmt.Printf("Function revision created at: %s\n", functionRevision.CreatedAt)
if err != nil {
fmt.Fprintf(os.Stderr, "Error when printing out function revision: %v\n", err)
os.Exit(1)
}

// Wait for creation of function revision
revision, err := wait.CreateRevisionWaitHandler(context.Background(), functionsClient.DefaultAPI, projectId, *function.Id, *functionRevision.Id).WaitWithContext(context.Background())
if err != nil || revision.Id == nil {
fmt.Fprintf(os.Stderr, "Error when waiting for revision creation: %v\n", err)
os.Exit(1)
}
_, err = fmt.Printf("Function revision %s has been successfully deployed.\n", *revision.Id)
if err != nil {
fmt.Fprintf(os.Stderr, "Error when printing out function revision: %v\n", err)
os.Exit(1)
}
}
14 changes: 14 additions & 0 deletions examples/functions/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/stackitcloud/stackit-sdk-go/examples/functions

go 1.25

// This is not needed in production. This is only here to point the golangci linter to the local version instead of the last release on GitHub.
replace github.com/stackitcloud/stackit-sdk-go/services/functions => ../../services/functions

require github.com/stackitcloud/stackit-sdk-go/services/functions v0.1.0

require (
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/stackitcloud/stackit-sdk-go/core v0.27.0 // indirect
)
8 changes: 8 additions & 0 deletions examples/functions/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/stackitcloud/stackit-sdk-go/core v0.27.0 h1:7lc6qStcFDFf8zHP4ORPa9joybw+Sa2LtB6BVjKQ+ek=
github.com/stackitcloud/stackit-sdk-go/core v0.27.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA=
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use (
./examples/edge
./examples/errorhandling
./examples/experimental
./examples/functions
./examples/iaas
./examples/intake
./examples/kms
Expand Down
3 changes: 3 additions & 0 deletions services/functions/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## v0.1.0
- Package `v1alphaapi`:
- **Feature**: initial release
2 changes: 2 additions & 0 deletions services/functions/NOTICE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
STACKIT Functions SDK for Go
Copyright 2024 - 2026 Schwarz IT KG
1 change: 1 addition & 0 deletions services/functions/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.1.0
1 change: 1 addition & 0 deletions services/functions/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ require github.com/stackitcloud/stackit-sdk-go/core v0.27.0

require (
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0 // indirect
)
37 changes: 37 additions & 0 deletions services/functions/v1alphaapi/wait/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package wait

import (
"context"
"fmt"
"net/http"
"time"

"github.com/stackitcloud/stackit-sdk-go/core/wait"
functions "github.com/stackitcloud/stackit-sdk-go/services/functions/v1alphaapi"
)

// CreateRevisionWaitHandler will wait for Revision creation
func CreateRevisionWaitHandler(ctx context.Context, client functions.DefaultAPI, projectId, functionId, revisionId string) *wait.AsyncActionHandler[functions.Revision] {
waitConfig := wait.WaiterHelper[functions.Revision, functions.RevisionState]{
FetchInstance: client.GetRevision(ctx, projectId, functionId, revisionId).Execute,
GetState: func(revision *functions.Revision) (functions.RevisionState, error) {
if revision.Id == nil || revision.State == nil {
return functions.REVISIONSTATE_UNKNOWN_DEFAULT_OPEN_API, fmt.Errorf("could not get revision id or state from response")
}
if *revision.Id == revisionId && *revision.State == functions.REVISIONSTATE_FAILED {
if revision.StateError != nil {
return *revision.State, fmt.Errorf("revision deployment failed: %s", *revision.StateError)
}

return *revision.State, fmt.Errorf("revision deployment failed")
}
return *revision.State, nil
},
ActiveState: []functions.RevisionState{functions.REVISIONSTATE_ACTIVE},
ErrorState: []functions.RevisionState{functions.REVISIONSTATE_FAILED, functions.REVISIONSTATE_CANCELLED}, //nolint:misspell // british wording
DeleteHttpErrorStatusCodes: []int{http.StatusNotFound},
}
handler := wait.New(waitConfig.Wait())
handler.SetTimeout(10 * time.Minute)
return handler
}
141 changes: 141 additions & 0 deletions services/functions/v1alphaapi/wait/wait_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package wait

import (
"context"
"testing"
"testing/synctest"
"time"

"github.com/google/go-cmp/cmp"

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/utils"

functions "github.com/stackitcloud/stackit-sdk-go/services/functions/v1alphaapi"
)

type mockSettings struct {
createFails bool
resourceStates []functions.RevisionState
}

func newAPIMock(settings mockSettings) functions.DefaultAPI {
return &functions.DefaultAPIServiceMock{
GetRevisionExecuteMock: utils.Ptr(
func(_ functions.ApiGetRevisionRequest) (*functions.Revision, error) {
if settings.createFails {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: 500,
}
}

state := settings.resourceStates[len(settings.resourceStates)-1]
return &functions.Revision{
Id: utils.Ptr("rid"),
FunctionId: utils.Ptr("fid"),
State: utils.Ptr(state),
}, nil
},
),
}
}

func TestCreateRevisionWaitHandler(t *testing.T) {
tests := []struct {
desc string
resourceStates []functions.RevisionState
wantErr bool
wantResp bool
wantState functions.RevisionState
}{
{
desc: "create_succeeded",
resourceStates: []functions.RevisionState{
functions.REVISIONSTATE_ACTIVE,
},
wantErr: false,
wantResp: true,
wantState: functions.REVISIONSTATE_ACTIVE,
},
{
desc: "revision_failed",
resourceStates: []functions.RevisionState{
functions.REVISIONSTATE_FAILED,
},
wantErr: true,
wantResp: false,
wantState: functions.REVISIONSTATE_FAILED,
},
{
desc: "creating_then_succeeded",
resourceStates: []functions.RevisionState{
functions.REVISIONSTATE_CREATING,
functions.REVISIONSTATE_ACTIVE,
},
wantErr: false,
wantResp: true,
wantState: functions.REVISIONSTATE_ACTIVE,
},
{
desc: "unknown",
resourceStates: []functions.RevisionState{
functions.REVISIONSTATE_UNKNOWN_DEFAULT_OPEN_API,
},
wantErr: true,
wantResp: false,
},
{
desc: "create_api_error",
wantErr: true,
wantResp: false,
},
}

for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
apiClient := newAPIMock(mockSettings{
createFails: tt.wantErr,
resourceStates: tt.resourceStates,
})

var wantRes *functions.Revision
if tt.wantResp {
wantRes = &functions.Revision{
Id: utils.Ptr("rid"),
FunctionId: utils.Ptr("fid"),
State: utils.Ptr(tt.wantState),
}
}

handler := CreateRevisionWaitHandler(
context.Background(),
apiClient,
"pid",
"fid",
"rid",
)

gotRes, err := handler.
SetTimeout(10 * time.Millisecond).
WaitWithContext(context.Background())

if (err != nil) != tt.wantErr {
t.Fatalf(
"handler error = %v, wantErr %v",
err,
tt.wantErr,
)
}

if !cmp.Equal(gotRes, wantRes) {
t.Fatalf(
"handler gotRes = %v, want %v",
gotRes,
wantRes,
)
}
})
})
}
}
Loading