-
Notifications
You must be signed in to change notification settings - Fork 38
Oas bot 32696593591/ufw #10628
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
base: oas-bot-32696593591/ufw
Are you sure you want to change the base?
Oas bot 32696593591/ufw #10628
Changes from all commits
9561658
018dfe8
194052f
d1ba973
5a6197d
f0bc912
c29479f
57c9c14
4409b04
2b83660
b720ca0
b15f27c
e8e99bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| module github.com/stackitcloud/stackit-sdk-go/examples/ufw | ||
|
|
||
| go 1.25.9 | ||
|
|
||
| replace github.com/stackitcloud/stackit-sdk-go/services/ufw => ../../services/ufw | ||
|
|
||
| require ( | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.26.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/ufw v0.0.0-00010101000000-000000000000 | ||
| ) | ||
|
|
||
| require ( | ||
| github.com/golang-jwt/jwt/v5 v5.3.1 // indirect | ||
| github.com/google/uuid v1.6.0 // indirect | ||
| ) |
| 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.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10cz4l0KM2L6hqYBH2QA= | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,240 @@ | ||||||
| package main | ||||||
|
|
||||||
| import ( | ||||||
| "context" | ||||||
| "fmt" | ||||||
| "os" | ||||||
| "reflect" | ||||||
| "strings" | ||||||
|
|
||||||
| "github.com/stackitcloud/stackit-sdk-go/core/config" | ||||||
| ufw "github.com/stackitcloud/stackit-sdk-go/services/ufw/v1api" | ||||||
| "github.com/stackitcloud/stackit-sdk-go/services/ufw/v1api/wait" | ||||||
| ) | ||||||
|
|
||||||
| func main() { | ||||||
| region := "eu01" // Region where the resources will be created | ||||||
| projectId := "PROJECT_ID" // UUID of your STACKIT project | ||||||
| instanceId := "INSTANCE_ID" // UUID of the instance to which the firewall rule will be attached | ||||||
| productType := "PRODUCT_TYPE" // Type of the instance to which the firewall rule will be attached (e.g. "redis", but you can get them from provider-options route) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the product type not limited to Edge Cloud for the beginning? If yes, I would prefer that edge is mentioned in the example instead of redis. |
||||||
| ufwRuleType := "ACL" // Type of the rule that you want to create (ACL, SecurityRule, PublicIp, but you can get them from provider-options route | ||||||
|
|
||||||
| ctx := context.Background() | ||||||
|
|
||||||
| token := "" | ||||||
| ufwClient, err := ufw.NewAPIClient(config.WithToken(token)) | ||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Creating API client: %v\n", err) | ||||||
| os.Exit(1) | ||||||
| } | ||||||
|
|
||||||
| // List all firewall rules | ||||||
| listUFWRules(ctx, ufwClient, projectId, region) | ||||||
|
|
||||||
| // Create a new firewall rule | ||||||
| description := "Created from SDK" | ||||||
| rulePayloadToCreate := ufw.CreateRulePayload{ | ||||||
| InstanceId: instanceId, | ||||||
| Product: productType, | ||||||
| SourceIP: "11.11.11.11/32", | ||||||
| Type: ufwRuleType, | ||||||
| Description: &description, | ||||||
| } | ||||||
|
|
||||||
| createdRuleResponse, err := createFirewallRule(ctx, ufwClient, projectId, region, &rulePayloadToCreate) | ||||||
|
|
||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error when creating firewall rule: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| fmt.Printf("Created firewall rule response: %+v\n", createdRuleResponse) | ||||||
|
|
||||||
| // Get the firewall rule | ||||||
| testGetRule, err := getFirewallRule(ctx, ufwClient, projectId, region, *createdRuleResponse.RefId) | ||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error when getting firewall rule: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| fmt.Printf("Firewall rule details: %+v\n", testGetRule) | ||||||
|
|
||||||
| if err := verifyPayloadMatch(testGetRule, rulePayloadToCreate); err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Verification failed after creation:\n%v\n", err) | ||||||
| return | ||||||
| } | ||||||
| fmt.Println("Created rule fields verified successfully.") | ||||||
|
|
||||||
| // Update the firewall rule | ||||||
| rulePayloadToUpdate := ufw.UpdateRulePayload{ | ||||||
| SourceIP: "22.22.22.22/32", | ||||||
| } | ||||||
|
|
||||||
| updatedRuleResponse, err := updateFirewallRule(ctx, ufwClient, projectId, region, *createdRuleResponse.RefId, rulePayloadToUpdate) | ||||||
|
|
||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error when updating firewall rule: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| fmt.Printf("Updated firewall rule details: %+v\n", updatedRuleResponse) | ||||||
|
|
||||||
| testGetRule, err = getFirewallRule(ctx, ufwClient, projectId, region, *updatedRuleResponse.RefId) | ||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error when getting updated firewall rule: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| if err := verifyPayloadMatch(testGetRule, rulePayloadToUpdate); err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Verification failed after update:\n%v\n", err) | ||||||
| return | ||||||
| } | ||||||
| fmt.Println("Updated rule fields verified successfully.") | ||||||
|
|
||||||
| // Delete the firewall rule | ||||||
| err = deleteFirewallRule(ctx, ufwClient, projectId, region, *updatedRuleResponse.RefId) | ||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error when deleting firewall rule: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| _, err = getFirewallRule(ctx, ufwClient, projectId, region, *updatedRuleResponse.RefId) | ||||||
| if !strings.Contains(err.Error(), "404") { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error while verifying deleted rule: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| fmt.Println("All firewall rules successfully tested and cleaned up.") | ||||||
| } | ||||||
|
|
||||||
| func listUFWRules(ctx context.Context, ufwClient *ufw.APIClient, projectId, region string) { | ||||||
| listRulesResponse, err := ufwClient.DefaultAPI.ListRules(ctx, projectId, region).Execute() | ||||||
| if err != nil { | ||||||
| fmt.Fprintf(os.Stderr, "[UFW] Error when listing firewall rules: %v\n", err) | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| fmt.Println("List of firewall rules:") | ||||||
| for i := range listRulesResponse.Rules { | ||||||
| fmt.Printf("%+v\n", listRulesResponse.Rules[i]) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| func getFirewallRule(ctx context.Context, ufwClient *ufw.APIClient, projectId, region, ruleId string) (*ufw.RuleResponse, error) { | ||||||
| return ufwClient.DefaultAPI.GetRule(ctx, projectId, region, ruleId).Execute() | ||||||
| } | ||||||
|
|
||||||
| func createFirewallRule(ctx context.Context, ufwClient *ufw.APIClient, projectId, region string, payload *ufw.CreateRulePayload) (*ufw.SecurityRuleSuccessfullyCreatedResponse, error) { | ||||||
| createdFirewallRule, err := ufwClient.DefaultAPI.CreateRule(ctx, projectId, region).CreateRulePayload(*payload).Execute() | ||||||
| if err != nil { | ||||||
| return nil, err | ||||||
| } | ||||||
|
|
||||||
| createdFirewallRuleId := createdFirewallRule.RefId | ||||||
| fmt.Printf("Created firewall rule with ID: %s\n", *createdFirewallRuleId) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This message is a bit misleading. Actually here the creation is triggered and waiter starts. |
||||||
|
|
||||||
| _, err = wait.CreateRuleWaitHandler(ctx, ufwClient.DefaultAPI, projectId, region, *createdFirewallRuleId).WaitWithContext(ctx) | ||||||
| if err != nil { | ||||||
| return nil, err | ||||||
| } | ||||||
|
|
||||||
| return createdFirewallRule, nil | ||||||
| } | ||||||
|
|
||||||
| func updateFirewallRule(ctx context.Context, ufwClient *ufw.APIClient, projectId, region, ruleId string, payload ufw.UpdateRulePayload) (*ufw.SecurityRuleSuccessfullyCreatedResponse, error) { | ||||||
| updatedFirewallRule, err := ufwClient.DefaultAPI.UpdateRule(ctx, projectId, region, ruleId).UpdateRulePayload(payload).Execute() | ||||||
| if err != nil { | ||||||
| return nil, err | ||||||
| } | ||||||
|
|
||||||
| fmt.Printf("Updated firewall rule with ID: %s\n", *updatedFirewallRule.RefId) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| _, err = wait.UpdateRuleWaitHandler(ctx, ufwClient.DefaultAPI, projectId, region, *updatedFirewallRule.RefId).WaitWithContext(ctx) | ||||||
| if err != nil { | ||||||
| return nil, err | ||||||
| } | ||||||
|
|
||||||
| return updatedFirewallRule, nil | ||||||
| } | ||||||
|
|
||||||
| func deleteFirewallRule(ctx context.Context, ufwClient *ufw.APIClient, projectId, region, ruleId string) error { | ||||||
| deleteRuleResponse, err := ufwClient.DefaultAPI.DeleteRule(ctx, projectId, region, ruleId).Execute() | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
|
|
||||||
| fmt.Printf("Deleted firewall rule with ID: %s\n", ruleId) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| fmt.Printf("Deleted firewall rule response: %+v\n", deleteRuleResponse) | ||||||
|
|
||||||
| _, err = wait.DeleteRuleWaitHandler(ctx, ufwClient.DefaultAPI, projectId, region, ruleId).WaitWithContext(ctx) | ||||||
| if err != nil { | ||||||
| return err | ||||||
| } | ||||||
|
|
||||||
| return nil | ||||||
| } | ||||||
|
|
||||||
| func verifyPayloadMatch(actual, expected any) error { | ||||||
| var mismatches []string | ||||||
|
|
||||||
| actualVal := reflect.ValueOf(actual) | ||||||
| if actualVal.Kind() == reflect.Pointer { | ||||||
| actualVal = actualVal.Elem() | ||||||
| } | ||||||
|
|
||||||
| expectedVal := reflect.ValueOf(expected) | ||||||
| if expectedVal.Kind() == reflect.Pointer { | ||||||
| expectedVal = expectedVal.Elem() | ||||||
| } | ||||||
|
|
||||||
| expectedType := expectedVal.Type() | ||||||
|
|
||||||
| for i := 0; i < expectedVal.NumField(); i++ { | ||||||
| fieldName := expectedType.Field(i).Name | ||||||
|
|
||||||
| if fieldName == "AdditionalProperties" { | ||||||
| continue // AdditionalProperties field is not part of the API response struct | ||||||
| } | ||||||
|
|
||||||
| if fieldName == "Description" { | ||||||
| continue // Description field is overridden by the API, will be fixed in the future | ||||||
| } | ||||||
|
|
||||||
| expectedField := expectedVal.Field(i) | ||||||
|
|
||||||
| // Look for a matching field in the API response struct | ||||||
| actualField := actualVal.FieldByName(fieldName) | ||||||
| if !actualField.IsValid() { | ||||||
| continue // Field exists in payload but not in response struct, safe to skip | ||||||
| } | ||||||
|
|
||||||
| // Dereference pointers cleanly to get string representations | ||||||
| expStr := formatReflectValue(expectedField) | ||||||
| actStr := formatReflectValue(actualField) | ||||||
|
|
||||||
| // Skip uninitialized/nil fields in the expected payload | ||||||
| // (e.g. fields omitted from an UpdateRulePayload) | ||||||
| if expStr == "" || expStr == "<nil>" { | ||||||
| continue | ||||||
| } | ||||||
|
|
||||||
| if expStr != actStr { | ||||||
| mismatches = append(mismatches, fmt.Sprintf("%s: expected %q, got %q", fieldName, expStr, actStr)) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if len(mismatches) > 0 { | ||||||
| return fmt.Errorf("field mismatches found:\n- %s", strings.Join(mismatches, "\n- ")) | ||||||
| } | ||||||
| return nil | ||||||
| } | ||||||
|
|
||||||
| func formatReflectValue(v reflect.Value) string { | ||||||
| if v.Kind() == reflect.Pointer { | ||||||
| if v.IsNil() { | ||||||
| return "<nil>" | ||||||
| } | ||||||
| v = v.Elem() | ||||||
| } | ||||||
| return fmt.Sprintf("%v", v.Interface()) | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,3 @@ | ||||||
| ## v1.0.0 | ||||||
| - **New:** STACKIT UFW service | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| - **Feature:** Add waiter and example methods for the API | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| STACKIT UFW SDK for Go | ||
| Copyright 2026 Schwarz IT KG |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| v1.0.0 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really want to start with v1? Other services mostly start with v0.1.0 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 127d9fd6bc674ed6ae757e63103645d95f7163a5 | ||
| 127d9fd6bc674ed6ae757e63103645d95f7163a5 |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||
| package wait | ||||
|
|
||||
| import ( | ||||
| "context" | ||||
| "errors" | ||||
| "net/http" | ||||
| "time" | ||||
|
|
||||
| "github.com/stackitcloud/stackit-sdk-go/core/wait" | ||||
| ufw "github.com/stackitcloud/stackit-sdk-go/services/ufw/v1api" | ||||
| ) | ||||
|
|
||||
| func CreateRuleWaitHandler(ctx context.Context, a ufw.DefaultAPI, projectId, region, ruleId string) *wait.AsyncActionHandler[ufw.RuleResponse] { | ||||
| return ruleWaitHandler(ctx, a, projectId, region, ruleId, []ufw.RuleResponseStatus{ufw.RULERESPONSESTATUS_ACTIVE}, nil) | ||||
| } | ||||
|
|
||||
| func UpdateRuleWaitHandler(ctx context.Context, a ufw.DefaultAPI, projectId, region, ruleId string) *wait.AsyncActionHandler[ufw.RuleResponse] { | ||||
| return ruleWaitHandler(ctx, a, projectId, region, ruleId, []ufw.RuleResponseStatus{ufw.RULERESPONSESTATUS_ACTIVE}, nil) | ||||
| } | ||||
|
|
||||
| func DeleteRuleWaitHandler(ctx context.Context, a ufw.DefaultAPI, projectId, region, ruleId string) *wait.AsyncActionHandler[ufw.RuleResponse] { | ||||
| return ruleWaitHandler(ctx, a, projectId, region, ruleId, nil, []int{http.StatusNotFound}) | ||||
| } | ||||
|
|
||||
| func ruleWaitHandler(ctx context.Context, a ufw.DefaultAPI, projectId, region, ruleId string, activeStates []ufw.RuleResponseStatus, deleteHttpErrorStatusCodes []int) *wait.AsyncActionHandler[ufw.RuleResponse] { | ||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you rename this function to sth like createOrUpdateRuleWaitHandler to keep the standard implementation of SDK. We try to follow this naming convention since some time.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
for example |
||||
| waitConfig := wait.WaiterHelper[ufw.RuleResponse, ufw.RuleResponseStatus]{ | ||||
| FetchInstance: a.GetRule(ctx, projectId, region, ruleId).Execute, | ||||
| GetState: func(ruleResp *ufw.RuleResponse) (ufw.RuleResponseStatus, error) { | ||||
| if ruleResp == nil { | ||||
| return "", errors.New("empty response") | ||||
| } | ||||
| return ruleResp.Status, nil | ||||
| }, | ||||
| ActiveState: activeStates, | ||||
| ErrorState: []ufw.RuleResponseStatus{ufw.RULERESPONSESTATUS_ERROR}, | ||||
| DeleteHttpErrorStatusCodes: deleteHttpErrorStatusCodes, | ||||
| } | ||||
|
|
||||
| handler := wait.New(waitConfig.Wait()) | ||||
| handler.SetTimeout(5 * time.Minute) | ||||
| return handler | ||||
| } | ||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.