This commit is contained in:
dayterr
2025-09-11 15:56:44 +03:00
parent 825b1a0a00
commit abd35f858c
87 changed files with 930 additions and 571 deletions

View File

@@ -0,0 +1,46 @@
package secgroup
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type CreateRequest struct {
// Account ID that owns security group
// Required: true
AccountID uint64 `url:"account_id" json:"account_id" validate:"required"`
// Security group name
// Required: true
Name string `url:"name" json:"name" validate:"required"`
// Security group description
// Required: false
Description string `url:"description,omitempty" json:"description,omitempty"`
}
func (sg SecurityGroup) Create(ctx context.Context, req CreateRequest) (uint64, error) {
err := validators.ValidateRequest(req)
if err != nil {
return 0, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/create"
res, err := sg.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
if err != nil {
return 0, err
}
result, err := strconv.ParseUint(string(res), 10, 64)
if err != nil {
return 0, err
}
return result, nil
}

View File

@@ -0,0 +1,63 @@
package secgroup
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type CreateRuleRequest struct {
// Security group ID
// Required: true
SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"`
// Traffic direction (inbound/outbound)
// Required: true
Direction string `url:"direction" json:"direction" validate:"required,securityGroupDirection"`
// IP protocol version
// Default: IPv4
// Required: false
Ethertype string `url:"ethertype,omitempty" json:"ethertype,omitempty" validate:"omitempty,securityGroupEthertype"`
// Network protocol, available values : icmp, tcp, udp
// Required: false
Protocol string `url:"protocol,omitempty" json:"protocol,omitempty" validate:"omitempty,securityGroupProtocol"`
// Start port number (for TCP/UDP)
// Required: false
PortRangeMin uint64 `url:"port_range_min,omitempty" json:"port_range_min,omitempty"`
// End port number (for TCP/UDP)
// Required: false
PortRangeMax uint64 `url:"port_range_max,omitempty" json:"port_range_max,omitempty"`
// Remote IP prefix in CIDR notation
// Required: false
RemoteIPPrefix string `url:"remote_ip_prefix,omitempty" json:"remote_ip_prefix,omitempty"`
}
func (sg SecurityGroup) CreateRule(ctx context.Context, req CreateRuleRequest) (uint64, error) {
err := validators.ValidateRequest(req)
if err != nil {
return 0, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/create_rule"
res, err := sg.client.DecortApiCallCtype(ctx, http.MethodPost, url, constants.MIMEJSON, req)
if err != nil {
return 0, err
}
result, err := strconv.ParseUint(string(res), 10, 64)
if err != nil {
return 0, err
}
return result, nil
}

View File

@@ -0,0 +1,36 @@
package secgroup
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type DeleteRequest struct {
// Security group ID
// Required: true
SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"`
}
func (sg SecurityGroup) Delete(ctx context.Context, req DeleteRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/delete"
res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,40 @@
package secgroup
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type DeleteRuleRequest struct {
// Security group ID
// Required: true
SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"`
// Rule ID
// Required: true
RuleID uint64 `url:"rule_id" json:"rule_id" validate:"required"`
}
func (sg SecurityGroup) DeleteRule(ctx context.Context, req DeleteRuleRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/delete_rule"
res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,80 @@
package secgroup
// FilterByID returns ListSecurityGroups with specified ID.
func (lsg ListSecurityGroups) FilterByID(id uint64) ListSecurityGroups {
predicate := func(isg ItemSecurityGroup) bool {
return isg.ID == id
}
return lsg.FilterFunc(predicate)
}
// FilterByID returns ListSecurityGroups with specified Name.
func (lsg ListSecurityGroups) FilterByName(name string) ListSecurityGroups {
predicate := func(isg ItemSecurityGroup) bool {
return isg.Name == name
}
return lsg.FilterFunc(predicate)
}
// FilterByCreatedBy returns ListSecurityGroups with specified CreatedBy.
func (lsg ListSecurityGroups) FilterByCreatedBy(createdBy string) ListSecurityGroups {
predicate := func(isg ItemSecurityGroup) bool {
return isg.CreatedBy == createdBy
}
return lsg.FilterFunc(predicate)
}
// FilterByDescription returns ListSecurityGroups with specified Description.
func (lsg ListSecurityGroups) FilterByDescription(description string) ListSecurityGroups {
predicate := func(isg ItemSecurityGroup) bool {
return isg.Description == description
}
return lsg.FilterFunc(predicate)
}
// FilterByUpdatedBy returns ListSecurityGroups with specified UpdatedBy.
func (lsg ListSecurityGroups) FilterByUpdatedBy(updatedBy string) ListSecurityGroups {
predicate := func(isg ItemSecurityGroup) bool {
return isg.UpdatedBy == updatedBy
}
return lsg.FilterFunc(predicate)
}
// FilterByAccountID returns ListSecurityGroups with specified AccountID.
func (lsg ListSecurityGroups) FilterByAccountID(accountID uint64) ListSecurityGroups {
predicate := func(isg ItemSecurityGroup) bool {
return isg.AccountID == accountID
}
return lsg.FilterFunc(predicate)
}
// FilterFunc allows filtering ListSecurityGroups based on a user-specified predicate.
func (lsg ListSecurityGroups) FilterFunc(predicate func(ItemSecurityGroup) bool) ListSecurityGroups {
var result ListSecurityGroups
for _, item := range lsg.Data {
if predicate(item) {
result.Data = append(result.Data, item)
}
}
result.EntryCount = uint64(len(result.Data))
return result
}
// FindOne returns first found ItemSecurityGroup
// If none was found, returns an empty struct.
func (lsg ListSecurityGroups) FindOne() ItemSecurityGroup {
if len(lsg.Data) == 0 {
return ItemSecurityGroup{}
}
return lsg.Data[0]
}

View File

@@ -0,0 +1,87 @@
package secgroup
import "testing"
var securityGroups = ListSecurityGroups{
Data: []ItemSecurityGroup{
{
ID: 1,
AccountID: 1,
Name: "sg1",
Description: "some desc",
CreatedBy: "user",
},
{
ID: 3,
AccountID: 3,
Name: "sg3",
Description: "some desc",
CreatedBy: "anotheruser",
},
{
ID: 5,
AccountID: 3,
Name: "sg5",
Description: "some other desc",
CreatedBy: "anotheruser",
UpdatedBy: "user",
},
},
EntryCount: 3,
}
func TestFilterByID(t *testing.T) {
actual := securityGroups.FilterByID(1).FindOne()
if actual.ID != 1 {
t.Fatal("expected ID 1, found: ", actual.ID)
}
}
func TestFilterByName(t *testing.T) {
actual := securityGroups.FilterByName("sg3").FindOne()
if actual.Name != "sg3" {
t.Fatal("expected Name sg3, found: ", actual.Name)
}
}
func TestFilterByDescription(t *testing.T) {
actual := securityGroups.FilterByDescription("some desc")
if len(actual.Data) != 2 {
t.Fatal("expected 2 found, actual: ", len(actual.Data))
}
for _, item := range actual.Data {
if item.Description != "some desc" {
t.Fatal("expected Description 'some desc', found: ", item.Description)
}
}
}
func TestFilterByAccountID(t *testing.T) {
actual := securityGroups.FilterByAccountID(1).FindOne()
if actual.AccountID != 1 {
t.Fatal("expected AccountID 1, found: ", actual.AccountID)
}
}
func TestFilterByCreatedBy(t *testing.T) {
actual := securityGroups.FilterByCreatedBy("anotheruser")
if len(actual.Data) != 2 {
t.Fatal("expected 2 found, actual: ", len(actual.Data))
}
for _, item := range actual.Data {
if item.CreatedBy != "anotheruser" {
t.Fatal("expected CreatedBy 'anotheruser', found: ", item.CreatedBy)
}
}
}
func TestFilterByUpdatedBy(t *testing.T) {
actual := securityGroups.FilterByUpdatedBy("user").FindOne()
if actual.UpdatedBy != "user" {
t.Fatal("expected UpdatedBy 'user', found: ", actual.UpdatedBy)
}
}

View File

@@ -0,0 +1,43 @@
package secgroup
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type GetRequest struct {
// ID of security group
// Required: true
SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"`
}
func (sg SecurityGroup) Get(ctx context.Context, req GetRequest) (*RecordSecurityGroup, error) {
res, err := sg.GetRaw(ctx, req)
if err != nil {
return nil, err
}
info := RecordSecurityGroup{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (sg SecurityGroup) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/get"
res, err := sg.client.DecortApiCall(ctx, http.MethodGet, url, req)
return res, err
}

View File

@@ -0,0 +1,86 @@
package secgroup
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type ListRequest struct {
// Search by security group id
// Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Search by account id
// Required: false
AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"`
// Search by security group name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Search by security group description
// Required: false
Description string `url:"description,omitempty" json:"description,omitempty"`
// Search by created after time (unix timestamp)
// Required: false
CreatedMin uint64 `url:"created_min,omitempty" json:"created_min,omitempty"`
// Search by created before time (unix timestamp)
// Required: false
CreatedMax uint64 `url:"created_max,omitempty" json:"created_max,omitempty"`
// Search by updated after time (unix timestamp)
// Required: false
UpdatedMin uint64 `url:"updated_min,omitempty" json:"updated_min,omitempty"`
// Search by updated before time (unix timestamp)
// Required: false
UpdatedMax uint64 `url:"updated_max,omitempty" json:"updated_max,omitempty"`
// Sort by one of supported fields, format ±<field>
// Required: false
SortBy string `url:"sort_by,omitempty" json:"sort_by,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
}
// List gets list of security groups as a ListSecurityGroups struct
func (sg SecurityGroup) List(ctx context.Context, req ListRequest) (*ListSecurityGroups, error) {
res, err := sg.ListRaw(ctx, req)
if err != nil {
return nil, err
}
list := ListSecurityGroups{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}
// ListRaw gets list of security groups as an array of bytes
func (sg SecurityGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/list"
res, err := sg.client.DecortApiCall(ctx, http.MethodGet, url, req)
return res, err
}

View File

@@ -0,0 +1,94 @@
package secgroup
type ListSecurityGroups struct {
// List
Data []ItemSecurityGroup `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
type ItemSecurityGroup struct {
// ID of the security group
ID uint64 `json:"id"`
// Account ID that owns the security group
AccountID uint64 `json:"account_id"`
// Name of the security group
Name string `json:"name"`
// Description of the security group
Description string `json:"description"`
// List of rules
Rules Rules `json:"rules"`
// Created at
CreatedAt uint64 `json:"created_at"`
// Updated at
UpdatedAt uint64 `json:"updated_at"`
// Created by
CreatedBy string `json:"created_by"`
// Updated by
UpdatedBy string `json:"updated_by"`
}
type RecordSecurityGroup struct {
// ID of the security group
ID uint64 `json:"id"`
// Account ID that owns the security group
AccountID uint64 `json:"account_id"`
// Name of the security group
Name string `json:"name"`
// Description of the security group
Description string `json:"description"`
// List of rules
Rules Rules `json:"rules"`
// Created at
CreatedAt uint64 `json:"created_at"`
// Updated at
UpdatedAt uint64 `json:"updated_at"`
// Created by
CreatedBy string `json:"created_by"`
// Updated by
UpdatedBy string `json:"updated_by"`
}
type Rules []Rule
type Rule struct {
// ID of the rule
ID uint64 `json:"id"`
// Traffic direction (inbound/outbound)
Direction string `json:"direction"`
// IP protocol version
Ethertype string `json:"ethertype"`
// Network protocol
Protocol string `json:"protocol"`
// Start port number (for TCP/UDP)
PortRangeMin uint64 `json:"port_range_min"`
// End port number (for TCP/UDP)
PortRangeMax uint64 `json:"port_range_max"`
// Remote IP prefix in CIDR notation
RemoteIPPrefix string `json:"remote_ip_prefix"`
RemoteGroupID uint64 `json:"remote_group_id"`
}

View File

@@ -0,0 +1,15 @@
package secgroup
import "repository.basistech.ru/BASIS/decort-golang-sdk/interfaces"
// Structure for creating request to storage policy
type SecurityGroup struct {
client interfaces.Caller
}
// Builder for stack endpoint
func New(client interfaces.Caller) *SecurityGroup {
return &SecurityGroup{
client: client,
}
}

View File

@@ -0,0 +1,41 @@
package secgroup
import "sort"
// SortByCreatedAt sorts ListSecurityGroups by the CreatedAt field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (lsg ListSecurityGroups) SortByCreatedAt(inverse bool) ListSecurityGroups {
if len(lsg.Data) < 2 {
return lsg
}
sort.Slice(lsg.Data, func(i, j int) bool {
if inverse {
return lsg.Data[i].CreatedAt > lsg.Data[j].CreatedAt
}
return lsg.Data[i].CreatedAt < lsg.Data[j].CreatedAt
})
return lsg
}
// SortByUpdatedAt sorts ListSecurityGroups by the UpdatedAt field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (lsg ListSecurityGroups) SortByUpdatedAt(inverse bool) ListSecurityGroups {
if len(lsg.Data) < 2 {
return lsg
}
sort.Slice(lsg.Data, func(i, j int) bool {
if inverse {
return lsg.Data[i].UpdatedAt > lsg.Data[j].UpdatedAt
}
return lsg.Data[i].UpdatedAt < lsg.Data[j].UpdatedAt
})
return lsg
}

View File

@@ -0,0 +1,51 @@
package secgroup
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
type UpdateRequest struct {
// Security group ID
// Required: true
SecurityGroupID uint64 `url:"security_group_id" json:"security_group_id" validate:"required"`
// New security group name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// New security group description
// Required: false
Description string `url:"description,omitempty" json:"description,omitempty"`
}
func (sg SecurityGroup) Update(ctx context.Context, req UpdateRequest) (*RecordSecurityGroup, error) {
res, err := sg.UpdateRaw(ctx, req)
if err != nil {
return nil, err
}
info := RecordSecurityGroup{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (sg SecurityGroup) UpdateRaw(ctx context.Context, req UpdateRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/security_group/update"
res, err := sg.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}