feat: [CDE-637]: Changes to support hybrid's infra resources. (#3453)

* Linting
* Refactoring db query in infra resource store for pq support.
* Using a default value of infra config identifier if not present in the create gitspace request.
* feat: [CDE-637]: Decoupling infra resource mgmt from config. Adding soft delete support to infra resource table.
* Making infra provider resource unique to config and space.
jobatzil/login/xforwardedfor
Dhruv Dhruv 2025-02-19 06:45:24 +00:00 committed by Harness
parent 8732b64aa8
commit 31d15dc8eb
32 changed files with 847 additions and 605 deletions

View File

@ -13,4 +13,7 @@ GITNESS_SSH_HOST=localhost
GITNESS_SSH_PORT=2222 GITNESS_SSH_PORT=2222
GITNESS_REGISTRY_STORAGE_TYPE=filesystem GITNESS_REGISTRY_STORAGE_TYPE=filesystem
GITNESS_REGISTRY_FILESYSTEM_ROOT_DIRECTORY=/tmp GITNESS_REGISTRY_FILESYSTEM_ROOT_DIRECTORY=/tmp
#GITNESS_DATABASE_DRIVER=postgres
#GITNESS_DATABASE_DATASOURCE=postgres://postgres:postgres@localhost:5432/gitness?sslmode=disable

View File

@ -45,19 +45,20 @@ var (
// CreateInput is the input used for create operations. // CreateInput is the input used for create operations.
type CreateInput struct { type CreateInput struct {
Identifier string `json:"identifier"` Identifier string `json:"identifier"`
Name string `json:"name"` Name string `json:"name"`
SpaceRef string `json:"space_ref"` // Ref of the parent space SpaceRef string `json:"space_ref"` // Ref of the parent space
IDE enum.IDEType `json:"ide"` IDE enum.IDEType `json:"ide"`
ResourceIdentifier string `json:"resource_identifier"` InfraProviderConfigIdentifier string `json:"infra_provider_config_identifier"`
ResourceSpaceRef string `json:"resource_space_ref"` ResourceIdentifier string `json:"resource_identifier"`
CodeRepoURL string `json:"code_repo_url"` ResourceSpaceRef string `json:"resource_space_ref"`
CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"` CodeRepoURL string `json:"code_repo_url"`
CodeRepoRef *string `json:"code_repo_ref"` CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"`
Branch string `json:"branch"` CodeRepoRef *string `json:"code_repo_ref"`
DevcontainerPath *string `json:"devcontainer_path"` Branch string `json:"branch"`
Metadata map[string]string `json:"metadata"` DevcontainerPath *string `json:"devcontainer_path"`
SSHTokenIdentifier string `json:"ssh_token_identifier"` Metadata map[string]string `json:"metadata"`
SSHTokenIdentifier string `json:"ssh_token_identifier"`
} }
// Create creates a new gitspace. // Create creates a new gitspace.
@ -132,7 +133,15 @@ func (c *Controller) Create(
enum.PermissionInfraProviderAccess); err != nil { enum.PermissionInfraProviderAccess); err != nil {
return nil, err return nil, err
} }
infraProviderResource, err := c.createOrFindInfraProviderResource(ctx, resourceSpace, resourceIdentifier, now)
// TODO: Temp fix to ensure the gitspace creation doesnt fail. Once the FE starts sending this field in the
// request, remove this.
if in.InfraProviderConfigIdentifier == "" {
in.InfraProviderConfigIdentifier = defaultResourceIdentifier
}
infraProviderResource, err := c.createOrFindInfraProviderResource(ctx, resourceSpace, resourceIdentifier,
in.InfraProviderConfigIdentifier, now)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -183,13 +192,16 @@ func (c *Controller) createOrFindInfraProviderResource(
ctx context.Context, ctx context.Context,
resourceSpace *types.SpaceCore, resourceSpace *types.SpaceCore,
resourceIdentifier string, resourceIdentifier string,
infraProviderConfigIdentifier string,
now int64, now int64,
) (*types.InfraProviderResource, error) { ) (*types.InfraProviderResource, error) {
var resource *types.InfraProviderResource var resource *types.InfraProviderResource
var err error var err error
resource, err = c.infraProviderSvc.FindResourceByIdentifier(ctx, resourceSpace.ID, resourceIdentifier) resource, err = c.infraProviderSvc.FindResourceByConfigAndIdentifier(ctx, resourceSpace.ID,
if err != nil && errors.Is(err, store.ErrResourceNotFound) && resourceIdentifier == defaultResourceIdentifier { infraProviderConfigIdentifier, resourceIdentifier)
if ((err != nil && errors.Is(err, store.ErrResourceNotFound)) || resource == nil) &&
resourceIdentifier == defaultResourceIdentifier {
resource, err = c.autoCreateDefaultResource(ctx, resourceSpace, now) resource, err = c.autoCreateDefaultResource(ctx, resourceSpace, now)
if err != nil { if err != nil {
return nil, err return nil, err
@ -237,12 +249,13 @@ func (c *Controller) autoCreateDefaultResource(
} }
defaultDockerConfig.Resources = []types.InfraProviderResource{defaultResource} defaultDockerConfig.Resources = []types.InfraProviderResource{defaultResource}
err = c.infraProviderSvc.CreateInfraProvider(ctx, defaultDockerConfig) err = c.infraProviderSvc.CreateConfigAndResources(ctx, defaultDockerConfig)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not auto-create the infra provider: %w", err) return nil, fmt.Errorf("could not auto-create the infra provider: %w", err)
} }
resource, err := c.infraProviderSvc.FindResourceByIdentifier(ctx, rootSpace.ID, defaultResourceIdentifier) resource, err := c.infraProviderSvc.FindResourceByConfigAndIdentifier(ctx, rootSpace.ID,
defaultDockerConfig.Identifier, defaultResourceIdentifier)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not find infra provider resource : %q %w", defaultResourceIdentifier, err) return nil, fmt.Errorf("could not find infra provider resource : %q %w", defaultResourceIdentifier, err)
} }

View File

@ -18,8 +18,44 @@ import (
"github.com/harness/gitness/app/auth/authz" "github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/infraprovider" "github.com/harness/gitness/app/services/infraprovider"
"github.com/harness/gitness/app/services/refcache" "github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/types/enum"
) )
const NoResourceIdentifier = ""
type ConfigInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
SpaceRef string `json:"space_ref" yaml:"space_ref"`
Name string `json:"name" yaml:"name"`
Type enum.InfraProviderType `json:"type" yaml:"type"`
Metadata map[string]any `json:"metadata" yaml:"metadata"`
}
type ResourceInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
Name string `json:"name" yaml:"name"`
InfraProviderType enum.InfraProviderType `json:"infra_provider_type" yaml:"infra_provider_type"`
CPU *string `json:"cpu" yaml:"cpu"`
Memory *string `json:"memory" yaml:"memory"`
Disk *string `json:"disk" yaml:"disk"`
Network *string `json:"network" yaml:"network"`
Region []string `json:"region" yaml:"region"`
Metadata map[string]string `json:"metadata" yaml:"metadata"`
GatewayHost *string `json:"gateway_host" yaml:"gateway_host"`
GatewayPort *string `json:"gateway_port" yaml:"gateway_port"`
}
type AutoCreateInput struct {
Config ConfigInput `json:"config" yaml:"config"`
Resources []ResourceInput `json:"resources" yaml:"resources"`
}
type TemplateInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
Description string `json:"description" yaml:"description"`
Data string `json:"data" yaml:"data"`
}
type Controller struct { type Controller struct {
authorizer authz.Authorizer authorizer authz.Authorizer
spaceFinder refcache.SpaceFinder spaceFinder refcache.SpaceFinder

View File

@ -1,115 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
const NoResourceIdentifier = ""
type CreateInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
SpaceRef string `json:"space_ref" yaml:"space_ref"` // Ref of the parent space
Name string `json:"name" yaml:"name"`
Type enum.InfraProviderType `json:"type" yaml:"type"`
Metadata map[string]any `json:"metadata" yaml:"metadata"`
Resources []ResourceInput `json:"resources" yaml:"resources"`
}
type ResourceInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
Name string `json:"name" yaml:"name"`
InfraProviderType enum.InfraProviderType `json:"infra_provider_type" yaml:"infra_provider_type"`
CPU *string `json:"cpu" yaml:"cpu"`
Memory *string `json:"memory" yaml:"memory"`
Disk *string `json:"disk" yaml:"disk"`
Network *string `json:"network" yaml:"network"`
Region []string `json:"region" yaml:"region"`
Metadata map[string]string `json:"metadata" yaml:"metadata"`
GatewayHost *string `json:"gateway_host" yaml:"gateway_host"`
GatewayPort *string `json:"gateway_port" yaml:"gateway_port"`
}
type TemplateInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
Description string `json:"description" yaml:"description"`
Data string `json:"data" yaml:"data"`
}
// Create creates a new infra provider.
func (c *Controller) Create(
ctx context.Context,
session auth.Session,
in CreateInput,
) (*types.InfraProviderConfig, error) {
if err := c.sanitizeCreateInput(in); err != nil {
return nil, fmt.Errorf("invalid input: %w", err)
}
parentSpace, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref %q : %w", in.SpaceRef, err)
}
if err = apiauth.CheckInfraProvider(
ctx,
c.authorizer,
&session,
parentSpace.Path,
NoResourceIdentifier,
enum.PermissionInfraProviderEdit); err != nil {
return nil, err
}
now := time.Now().UnixMilli()
infraProviderConfig := c.MapToInfraProviderConfig(in, parentSpace, now)
err = c.infraproviderSvc.CreateInfraProvider(ctx, infraProviderConfig)
if err != nil {
return nil, fmt.Errorf("unable to create the infraprovider: %q %w", infraProviderConfig.Identifier, err)
}
return infraProviderConfig, nil
}
func (c *Controller) MapToInfraProviderConfig(
in CreateInput,
parentSpace *types.SpaceCore,
now int64,
) *types.InfraProviderConfig {
infraProviderConfig := &types.InfraProviderConfig{
Identifier: in.Identifier,
Name: in.Name,
SpaceID: parentSpace.ID,
SpacePath: parentSpace.Path,
Type: in.Type,
Created: now,
Updated: now,
Metadata: in.Metadata,
}
infraProviderConfig.Resources = mapToResourceEntity(in.Resources, parentSpace, now)
return infraProviderConfig
}
func (c *Controller) sanitizeCreateInput(in CreateInput) error {
if err := check.Identifier(in.Identifier); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,82 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
// CreateConfig creates a new infra provider config.
func (c *Controller) CreateConfig(
ctx context.Context,
session auth.Session,
in ConfigInput,
) (*types.InfraProviderConfig, error) {
if err := c.sanitizeCreateInput(in); err != nil {
return nil, fmt.Errorf("invalid input: %w", err)
}
parentSpace, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref %q : %w", in.SpaceRef, err)
}
if err = apiauth.CheckInfraProvider(
ctx,
c.authorizer,
&session,
parentSpace.Path,
NoResourceIdentifier,
enum.PermissionInfraProviderEdit); err != nil {
return nil, err
}
now := time.Now().UnixMilli()
infraProviderConfig := c.MapToInfraProviderConfig(in, parentSpace, now)
err = c.infraproviderSvc.CreateConfig(ctx, infraProviderConfig)
if err != nil {
return nil, fmt.Errorf("unable to create the infraprovider: %q %w", infraProviderConfig.Identifier, err)
}
return infraProviderConfig, nil
}
func (c *Controller) MapToInfraProviderConfig(
in ConfigInput,
space *types.SpaceCore,
now int64,
) *types.InfraProviderConfig {
return &types.InfraProviderConfig{
Identifier: in.Identifier,
Name: in.Name,
SpaceID: space.ID,
SpacePath: space.Path,
Type: in.Type,
Created: now,
Updated: now,
Metadata: in.Metadata,
}
}
func (c *Controller) sanitizeCreateInput(in ConfigInput) error {
if err := check.Identifier(in.Identifier); err != nil {
return err
}
return nil
}

View File

@ -83,7 +83,7 @@ func (c *Controller) CreateResources(
return nil, fmt.Errorf("invalid input: %w", err) return nil, fmt.Errorf("invalid input: %w", err)
} }
now := time.Now().UnixMilli() now := time.Now().UnixMilli()
parentSpace, err := c.spaceFinder.FindByRef(ctx, spaceRef) space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to find parent by ref: %w", err) return nil, fmt.Errorf("failed to find parent by ref: %w", err)
} }
@ -91,31 +91,35 @@ func (c *Controller) CreateResources(
ctx, ctx,
c.authorizer, c.authorizer,
&session, &session,
parentSpace.Path, space.Path,
NoResourceIdentifier, NoResourceIdentifier,
enum.PermissionInfraProviderEdit); err != nil { enum.PermissionInfraProviderEdit); err != nil {
return nil, err return nil, err
} }
infraProviderConfig, err := c.infraproviderSvc.Find(ctx, parentSpace, configIdentifier) infraProviderConfig, err := c.infraproviderSvc.Find(ctx, space, configIdentifier)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to find infraprovider config by ref: %q %w", infraProviderConfig.Identifier, err) return nil, fmt.Errorf("failed to find infraprovider config by ref: %q %w", configIdentifier, err)
} }
resources := mapToResourceEntity(in, parentSpace, now) resources := c.MapToResourceEntity(in, space, now)
err = c.infraproviderSvc.CreateResources(ctx, resources, infraProviderConfig.ID, infraProviderConfig.Identifier) err = c.infraproviderSvc.CreateResources(ctx, space.ID, resources, infraProviderConfig.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resources, nil return resources, nil
} }
func mapToResourceEntity(in []ResourceInput, parentSpace *types.SpaceCore, now int64) []types.InfraProviderResource { func (c *Controller) MapToResourceEntity(
in []ResourceInput,
space *types.SpaceCore,
now int64,
) []types.InfraProviderResource {
var resources []types.InfraProviderResource var resources []types.InfraProviderResource
for _, res := range in { for _, res := range in {
infraProviderResource := types.InfraProviderResource{ infraProviderResource := types.InfraProviderResource{
UID: res.Identifier, UID: res.Identifier,
InfraProviderType: res.InfraProviderType, InfraProviderType: res.InfraProviderType,
Name: res.Name, Name: res.Name,
SpaceID: parentSpace.ID, SpaceID: space.ID,
CPU: res.CPU, CPU: res.CPU,
Memory: res.Memory, Memory: res.Memory,
Disk: res.Disk, Disk: res.Disk,
@ -124,7 +128,7 @@ func mapToResourceEntity(in []ResourceInput, parentSpace *types.SpaceCore, now i
Metadata: res.Metadata, Metadata: res.Metadata,
Created: now, Created: now,
Updated: now, Updated: now,
SpacePath: parentSpace.Path, SpacePath: space.Path,
} }
resources = append(resources, infraProviderResource) resources = append(resources, infraProviderResource)
} }

View File

@ -29,14 +29,14 @@ func HandleCreateConfig(infraProviderCtrl *infraprovider.Controller) http.Handle
ctx := r.Context() ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx) session, _ := request.AuthSessionFrom(ctx)
in := new(infraprovider.CreateInput) in := new(infraprovider.ConfigInput)
err := json.NewDecoder(r.Body).Decode(in) err := json.NewDecoder(r.Body).Decode(in)
if err != nil { if err != nil {
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err) render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
return return
} }
infraProviderConfig, err := infraProviderCtrl.Create(ctx, *session, *in) infraProviderConfig, err := infraProviderCtrl.CreateConfig(ctx, *session, *in)
if err != nil { if err != nil {
render.TranslatedUserError(ctx, w, err) render.TranslatedUserError(ctx, w, err)
return return

View File

@ -25,7 +25,7 @@ import (
) )
type createInfraProviderConfigRequest struct { type createInfraProviderConfigRequest struct {
infraprovider.CreateInput infraprovider.ConfigInput
} }
type getInfraProviderRequest struct { type getInfraProviderRequest struct {

View File

@ -1,215 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"net/http"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/infraprovider"
"github.com/harness/gitness/types"
"github.com/rs/zerolog/log"
)
func (c *Service) CreateTemplate(
ctx context.Context,
template *types.InfraProviderTemplate,
) error {
return c.infraProviderTemplateStore.Create(ctx, template)
}
func (c *Service) CreateInfraProvider(
ctx context.Context,
infraProviderConfig *types.InfraProviderConfig,
) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
err := c.areNewConfigsAllowed(ctx, infraProviderConfig)
if err != nil {
return err
}
configID, err := c.createConfig(ctx, infraProviderConfig)
if err != nil {
return fmt.Errorf("could not create the config: %q %w", infraProviderConfig.Identifier, err)
}
err = c.createResources(ctx, infraProviderConfig.Resources, configID, infraProviderConfig.Identifier)
if err != nil {
return fmt.Errorf("could not create the resources: %v %w", infraProviderConfig.Resources, err)
}
return nil
})
if err != nil {
return fmt.Errorf("failed to complete txn for the infraprovider %w", err)
}
return nil
}
func (c *Service) validateConfigAndResources(infraProviderConfig *types.InfraProviderConfig) error {
infraProvider, err := c.infraProviderFactory.GetInfraProvider(infraProviderConfig.Type)
if err != nil {
return fmt.Errorf("failed to fetch infra provider for type %s: %w", infraProviderConfig.Type, err)
}
err = infraProvider.ValidateConfigAndResources(infraProviderConfig)
if err != nil {
return err
}
return nil
}
func (c *Service) areNewConfigsAllowed(ctx context.Context, infraProviderConfig *types.InfraProviderConfig) error {
existingConfigs, err := c.fetchExistingConfigs(ctx, infraProviderConfig)
if err != nil {
return err
}
if len(existingConfigs) > 0 {
return usererror.NewWithPayload(http.StatusForbidden, fmt.Sprintf(
"%d infra configs for provider %s exist for this account. Only 1 is allowed",
len(existingConfigs), infraProviderConfig.Type))
}
return nil
}
func (c *Service) fetchExistingConfigs(
ctx context.Context,
infraProviderConfig *types.InfraProviderConfig,
) ([]*types.InfraProviderConfig, error) {
existingConfigs, err := c.infraProviderConfigStore.FindByType(ctx, infraProviderConfig.SpaceID,
infraProviderConfig.Type)
if err != nil {
return nil, fmt.Errorf("failed to find existing infraprovider config for type %s & space %d: %w",
infraProviderConfig.Type, infraProviderConfig.SpaceID, err)
}
return existingConfigs, nil
}
func (c *Service) createConfig(ctx context.Context, infraProviderConfig *types.InfraProviderConfig) (int64, error) {
err := c.validateConfigAndResources(infraProviderConfig)
if err != nil {
return 0, err
}
err = c.infraProviderConfigStore.Create(ctx, infraProviderConfig)
if err != nil {
return 0, fmt.Errorf("failed to create infraprovider config for %s: %w", infraProviderConfig.Identifier, err)
}
newInfraProviderConfig, err := c.infraProviderConfigStore.FindByIdentifier(ctx, infraProviderConfig.SpaceID,
infraProviderConfig.Identifier)
if err != nil {
return 0, fmt.Errorf("failed to find newly created infraprovider config %s in space %d: %w",
infraProviderConfig.Identifier, infraProviderConfig.SpaceID, err)
}
return newInfraProviderConfig.ID, nil
}
func (c *Service) CreateResources(
ctx context.Context,
resources []types.InfraProviderResource,
configID int64,
infraProviderConfigIdentifier string,
) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
return c.createResources(ctx, resources, configID, infraProviderConfigIdentifier)
})
if err != nil {
return fmt.Errorf("failed to complete create txn for the infraprovider resource %w", err)
}
return nil
}
func (c *Service) createResources(
ctx context.Context,
resources []types.InfraProviderResource,
configID int64,
infraProviderConfigIdentifier string,
) error {
for idx := range resources {
resource := &resources[idx]
resource.InfraProviderConfigID = configID
resource.InfraProviderConfigIdentifier = infraProviderConfigIdentifier
err := c.validate(ctx, resource)
if err != nil {
return err
}
err = c.infraProviderResourceStore.Create(ctx, resource)
if err != nil {
return fmt.Errorf("failed to create infraprovider resource for : %q %w", resource.UID, err)
}
}
return nil
}
func (c *Service) validate(ctx context.Context, resource *types.InfraProviderResource) error {
infraProvider, err := c.infraProviderFactory.GetInfraProvider(resource.InfraProviderType)
if err != nil {
return fmt.Errorf("failed to fetch infra impl for type : %q %w", resource.InfraProviderType, err)
}
if len(infraProvider.TemplateParams()) > 0 {
err = c.validateTemplates(ctx, infraProvider, *resource)
if err != nil {
return err
}
}
err = c.validateResourceParams(infraProvider, *resource)
if err != nil {
return err
}
return err
}
func (c *Service) validateTemplates(
ctx context.Context,
infraProvider infraprovider.InfraProvider,
res types.InfraProviderResource,
) error {
templateParams := infraProvider.TemplateParams()
for _, param := range templateParams {
key := param.Name
if res.Metadata[key] != "" {
templateIdentifier := res.Metadata[key]
_, err := c.infraProviderTemplateStore.FindByIdentifier(
ctx, res.SpaceID, templateIdentifier)
if err != nil {
log.Warn().Msgf("unable to get template params for ID : %s",
res.Metadata[key])
}
}
}
return nil
}
func (c *Service) validateResourceParams(
infraProvider infraprovider.InfraProvider,
res types.InfraProviderResource,
) error {
infraResourceParams := make([]types.InfraProviderParameter, 0)
for key, value := range res.Metadata {
infraResourceParams = append(infraResourceParams, types.InfraProviderParameter{
Name: key,
Value: value,
})
}
return infraProvider.ValidateParams(infraResourceParams)
}

View File

@ -0,0 +1,107 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"net/http"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/types"
)
func (c *Service) CreateConfig(
ctx context.Context,
infraProviderConfig *types.InfraProviderConfig,
) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
err := c.areNewConfigsAllowed(ctx, infraProviderConfig)
if err != nil {
return err
}
_, err = c.createConfig(ctx, infraProviderConfig)
if err != nil {
return fmt.Errorf("could not create the config: %q %w", infraProviderConfig.Identifier, err)
}
return nil
})
if err != nil {
return fmt.Errorf("failed to complete txn for the infraprovider %w", err)
}
return nil
}
func (c *Service) areNewConfigsAllowed(ctx context.Context, infraProviderConfig *types.InfraProviderConfig) error {
existingConfigs, err := c.fetchExistingConfigs(ctx, infraProviderConfig)
if err != nil {
return err
}
if len(existingConfigs) > 0 {
return usererror.NewWithPayload(http.StatusForbidden, fmt.Sprintf(
"%d infra configs for provider %s exist for this account. Only 1 is allowed",
len(existingConfigs), infraProviderConfig.Type))
}
return nil
}
func (c *Service) fetchExistingConfigs(
ctx context.Context,
infraProviderConfig *types.InfraProviderConfig,
) ([]*types.InfraProviderConfig, error) {
existingConfigs, err := c.infraProviderConfigStore.FindByType(ctx, infraProviderConfig.SpaceID,
infraProviderConfig.Type)
if err != nil {
return nil, fmt.Errorf("failed to find existing infraprovider config for type %s & space %d: %w",
infraProviderConfig.Type, infraProviderConfig.SpaceID, err)
}
return existingConfigs, nil
}
func (c *Service) createConfig(ctx context.Context, infraProviderConfig *types.InfraProviderConfig) (int64, error) {
err := c.validateConfig(infraProviderConfig)
if err != nil {
return 0, err
}
err = c.infraProviderConfigStore.Create(ctx, infraProviderConfig)
if err != nil {
return 0, fmt.Errorf("failed to create infraprovider config for %s: %w", infraProviderConfig.Identifier, err)
}
newInfraProviderConfig, err := c.infraProviderConfigStore.FindByIdentifier(ctx, infraProviderConfig.SpaceID,
infraProviderConfig.Identifier)
if err != nil {
return 0, fmt.Errorf("failed to find newly created infraprovider config %s in space %d: %w",
infraProviderConfig.Identifier, infraProviderConfig.SpaceID, err)
}
return newInfraProviderConfig.ID, nil
}
func (c *Service) validateConfig(infraProviderConfig *types.InfraProviderConfig) error {
infraProvider, err := c.infraProviderFactory.GetInfraProvider(infraProviderConfig.Type)
if err != nil {
return fmt.Errorf("failed to fetch infra provider for type %s: %w", infraProviderConfig.Type, err)
}
err = infraProvider.ValidateConfig(infraProviderConfig)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,48 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"github.com/harness/gitness/types"
)
func (c *Service) CreateConfigAndResources(
ctx context.Context,
infraProviderConfig *types.InfraProviderConfig,
) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
err := c.areNewConfigsAllowed(ctx, infraProviderConfig)
if err != nil {
return err
}
configID, err := c.createConfig(ctx, infraProviderConfig)
if err != nil {
return fmt.Errorf("could not create the config: %q %w", infraProviderConfig.Identifier, err)
}
err = c.createMissingResources(ctx, infraProviderConfig.Resources, configID, infraProviderConfig.SpaceID)
if err != nil {
return fmt.Errorf("could not create the resources: %v %w", infraProviderConfig.Resources, err)
}
return nil
})
if err != nil {
return fmt.Errorf("failed to complete txn for the infraprovider %w", err)
}
return nil
}

View File

@ -0,0 +1,102 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"errors"
"fmt"
"github.com/harness/gitness/infraprovider"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
"github.com/rs/zerolog/log"
)
func (c *Service) CreateResources(
ctx context.Context,
spaceID int64,
resources []types.InfraProviderResource,
configID int64,
) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
return c.createMissingResources(ctx, resources, configID, spaceID)
})
if err != nil {
return fmt.Errorf("failed to complete create txn for the infraprovider resource %w", err)
}
return nil
}
func (c *Service) createMissingResources(
ctx context.Context,
resources []types.InfraProviderResource,
configID int64,
spaceID int64,
) error {
for idx := range resources {
resource := &resources[idx]
resource.InfraProviderConfigID = configID
resource.SpaceID = spaceID
if err := c.validateResource(ctx, resource); err != nil {
return err
}
existingResource, err := c.infraProviderResourceStore.FindByConfigAndIdentifier(ctx, resource.SpaceID,
configID, resource.UID)
if (err != nil && errors.Is(err, store.ErrResourceNotFound)) || existingResource == nil {
if err = c.infraProviderResourceStore.Create(ctx, resource); err != nil {
return fmt.Errorf("failed to create infraprovider resource for %s: %w", resource.UID, err)
}
log.Info().Msgf("created new resource %s/%s", resource.InfraProviderConfigIdentifier, resource.UID)
}
}
return nil
}
func (c *Service) validateResource(ctx context.Context, resource *types.InfraProviderResource) error {
infraProvider, err := c.infraProviderFactory.GetInfraProvider(resource.InfraProviderType)
if err != nil {
return fmt.Errorf("failed to fetch infra impl for type : %q %w", resource.InfraProviderType, err)
}
if len(infraProvider.TemplateParams()) > 0 {
err = c.validateTemplates(ctx, infraProvider, *resource)
if err != nil {
return err
}
}
err = c.validateResourceParams(infraProvider, *resource)
if err != nil {
return err
}
return err
}
func (c *Service) validateResourceParams(
infraProvider infraprovider.InfraProvider,
res types.InfraProviderResource,
) error {
infraResourceParams := make([]types.InfraProviderParameter, 0)
for key, value := range res.Metadata {
infraResourceParams = append(infraResourceParams, types.InfraProviderParameter{
Name: key,
Value: value,
})
}
return infraProvider.ValidateParams(infraResourceParams)
}

View File

@ -0,0 +1,52 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"github.com/harness/gitness/infraprovider"
"github.com/harness/gitness/types"
"github.com/rs/zerolog/log"
)
func (c *Service) CreateTemplate(
ctx context.Context,
template *types.InfraProviderTemplate,
) error {
return c.infraProviderTemplateStore.Create(ctx, template)
}
func (c *Service) validateTemplates(
ctx context.Context,
infraProvider infraprovider.InfraProvider,
res types.InfraProviderResource,
) error {
templateParams := infraProvider.TemplateParams()
for _, param := range templateParams {
key := param.Name
if res.Metadata[key] != "" {
templateIdentifier := res.Metadata[key]
_, err := c.infraProviderTemplateStore.FindByIdentifier(
ctx, res.SpaceID, templateIdentifier)
if err != nil {
log.Warn().Msgf("unable to get template params for ID : %s",
res.Metadata[key])
}
}
}
return nil
}

View File

@ -0,0 +1,66 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"net/http"
"github.com/harness/gitness/app/api/usererror"
)
func (c *Service) DeleteResource(
ctx context.Context,
spaceID int64,
infraProviderConfigIdentifier string,
identifier string,
) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
infraProviderConfig, err := c.infraProviderConfigStore.FindByIdentifier(ctx, spaceID,
infraProviderConfigIdentifier)
if err != nil {
return fmt.Errorf("failed to find infra config %s for deleting resource: %w",
infraProviderConfigIdentifier, err)
}
infraProviderResource, err := c.infraProviderResourceStore.FindByConfigAndIdentifier(ctx, spaceID,
infraProviderConfig.ID, identifier)
if err != nil {
return fmt.Errorf("failed to find infra resource %s with config %s for deleting resource: %w",
identifier, infraProviderConfigIdentifier, err)
}
activeGitspaces, err := c.gitspaceConfigStore.ListActiveConfigsForInfraProviderResource(ctx,
infraProviderResource.ID)
if err != nil {
return fmt.Errorf("failed to list active configs for infra resource %s for deleting resource: %w",
identifier, err)
}
if len(activeGitspaces) > 0 {
return usererror.NewWithPayload(http.StatusForbidden, fmt.Sprintf("There are %d active configs for "+
"infra resource %s, expected 0", len(activeGitspaces), identifier))
}
return c.infraProviderResourceStore.Delete(ctx, infraProviderResource.ID)
})
if err != nil {
return fmt.Errorf("failed to complete txn for deleting the infra resource %s: %w", identifier, err)
}
return nil
}

View File

@ -0,0 +1,81 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"slices"
"github.com/harness/gitness/types"
)
func (c *Service) Find(
ctx context.Context,
space *types.SpaceCore,
identifier string,
) (*types.InfraProviderConfig, error) {
infraProviderConfig, err := c.infraProviderConfigStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider config: %q %w", identifier, err)
}
resources, err := c.infraProviderResourceStore.List(ctx, infraProviderConfig.ID, types.ListQueryFilter{})
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider resources for config: %q %w",
infraProviderConfig.Identifier, err)
}
infraProviderConfig.SpacePath = space.Path
if len(resources) > 0 {
providerResources := make([]types.InfraProviderResource, len(resources))
for i, resource := range resources {
if resource != nil {
providerResources[i] = *resource
providerResources[i].SpacePath = space.Path
}
}
slices.SortFunc(providerResources, types.CompareInfraProviderResource)
infraProviderConfig.Resources = providerResources
}
return infraProviderConfig, nil
}
func (c *Service) FindTemplate(
ctx context.Context,
space *types.SpaceCore,
identifier string,
) (*types.InfraProviderTemplate, error) {
infraProviderTemplate, err := c.infraProviderTemplateStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider template: %q %w", identifier, err)
}
return infraProviderTemplate, nil
}
func (c *Service) FindResourceByConfigAndIdentifier(
ctx context.Context,
spaceID int64,
infraProviderConfigIdentifier string,
identifier string,
) (*types.InfraProviderResource, error) {
infraProviderConfig, err := c.infraProviderConfigStore.FindByIdentifier(ctx, spaceID, infraProviderConfigIdentifier)
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider config %s: %w", infraProviderConfigIdentifier, err)
}
return c.infraProviderResourceStore.FindByConfigAndIdentifier(ctx, spaceID, infraProviderConfig.ID, identifier)
}
func (c *Service) FindResource(ctx context.Context, id int64) (*types.InfraProviderResource, error) {
return c.infraProviderResourceStore.Find(ctx, id)
}

View File

@ -15,19 +15,15 @@
package infraprovider package infraprovider
import ( import (
"context"
"fmt"
"slices"
"github.com/harness/gitness/app/services/refcache" "github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store" "github.com/harness/gitness/app/store"
"github.com/harness/gitness/infraprovider" "github.com/harness/gitness/infraprovider"
"github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
) )
func NewService( func NewService(
tx dbtx.Transactor, tx dbtx.Transactor,
gitspaceConfigStore store.GitspaceConfigStore,
resourceStore store.InfraProviderResourceStore, resourceStore store.InfraProviderResourceStore,
configStore store.InfraProviderConfigStore, configStore store.InfraProviderConfigStore,
templateStore store.InfraProviderTemplateStore, templateStore store.InfraProviderTemplateStore,
@ -41,66 +37,16 @@ func NewService(
infraProviderTemplateStore: templateStore, infraProviderTemplateStore: templateStore,
infraProviderFactory: factory, infraProviderFactory: factory,
spaceFinder: spaceFinder, spaceFinder: spaceFinder,
gitspaceConfigStore: gitspaceConfigStore,
} }
} }
type Service struct { type Service struct {
tx dbtx.Transactor tx dbtx.Transactor
gitspaceConfigStore store.GitspaceConfigStore
infraProviderResourceStore store.InfraProviderResourceStore infraProviderResourceStore store.InfraProviderResourceStore
infraProviderConfigStore store.InfraProviderConfigStore infraProviderConfigStore store.InfraProviderConfigStore
infraProviderTemplateStore store.InfraProviderTemplateStore infraProviderTemplateStore store.InfraProviderTemplateStore
infraProviderFactory infraprovider.Factory infraProviderFactory infraprovider.Factory
spaceFinder refcache.SpaceFinder spaceFinder refcache.SpaceFinder
} }
func (c *Service) Find(
ctx context.Context,
space *types.SpaceCore,
identifier string,
) (*types.InfraProviderConfig, error) {
infraProviderConfig, err := c.infraProviderConfigStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider config: %q %w", identifier, err)
}
resources, err := c.infraProviderResourceStore.List(ctx, infraProviderConfig.ID, types.ListQueryFilter{})
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider resources for config: %q %w",
infraProviderConfig.Identifier, err)
}
infraProviderConfig.SpacePath = space.Path
if len(resources) > 0 {
providerResources := make([]types.InfraProviderResource, len(resources))
for i, resource := range resources {
if resource != nil {
providerResources[i] = *resource
providerResources[i].SpacePath = space.Path
}
}
slices.SortFunc(providerResources, types.CompareInfraProviderResource)
infraProviderConfig.Resources = providerResources
}
return infraProviderConfig, nil
}
func (c *Service) FindTemplate(
ctx context.Context,
space *types.SpaceCore,
identifier string,
) (*types.InfraProviderTemplate, error) {
infraProviderTemplate, err := c.infraProviderTemplateStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider template: %q %w", identifier, err)
}
return infraProviderTemplate, nil
}
func (c *Service) FindResourceByIdentifier(
ctx context.Context,
spaceID int64,
identifier string) (*types.InfraProviderResource, error) {
return c.infraProviderResourceStore.FindByIdentifier(ctx, spaceID, identifier)
}
func (c *Service) FindResource(ctx context.Context, id int64) (*types.InfraProviderResource, error) {
return c.infraProviderResourceStore.Find(ctx, id)
}

View File

@ -0,0 +1,38 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"time"
"github.com/harness/gitness/types"
)
func (c *Service) UpdateConfig(ctx context.Context, infraProviderConfig *types.InfraProviderConfig) error {
err := c.validateConfig(infraProviderConfig)
if err != nil {
return err
}
infraProviderConfig.Updated = time.Now().UnixMilli()
err = c.infraProviderConfigStore.Update(ctx, infraProviderConfig)
if err != nil {
return fmt.Errorf("failed to update infraprovider config for %s: %w", infraProviderConfig.Identifier, err)
}
return nil
}

View File

@ -17,49 +17,10 @@ package infraprovider
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/harness/gitness/types" "github.com/harness/gitness/types"
) )
func (c *Service) updateConfig(ctx context.Context, infraProviderConfig *types.InfraProviderConfig) error {
err := c.validateConfigAndResources(infraProviderConfig)
if err != nil {
return err
}
infraProviderConfig.Updated = time.Now().UnixMilli()
err = c.infraProviderConfigStore.Update(ctx, infraProviderConfig)
if err != nil {
return fmt.Errorf("failed to update infraprovider config for %s: %w", infraProviderConfig.Identifier, err)
}
return nil
}
func (c *Service) UpdateResource(ctx context.Context, resource types.InfraProviderResource) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error {
space, err := c.spaceFinder.FindByRef(ctx, resource.SpacePath)
if err != nil {
return err
}
infraProviderResource, err := c.FindResourceByIdentifier(ctx, space.ID, resource.UID)
if err != nil {
return err
}
resource.ID = infraProviderResource.ID
resource.Updated = time.Now().UnixMilli()
if err = c.infraProviderResourceStore.Update(ctx, &resource); err != nil {
return err
}
return nil
})
if err != nil {
return fmt.Errorf("failed to complete update txn for the infraprovider resource %w", err)
}
return nil
}
func (c *Service) UpdateTemplate(ctx context.Context, template types.InfraProviderTemplate) error { func (c *Service) UpdateTemplate(ctx context.Context, template types.InfraProviderTemplate) error {
err := c.tx.WithTx(ctx, func(ctx context.Context) error { err := c.tx.WithTx(ctx, func(ctx context.Context) error {
space, err := c.spaceFinder.FindByRef(ctx, template.SpacePath) space, err := c.spaceFinder.FindByRef(ctx, template.SpacePath)

View File

@ -25,9 +25,10 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func (c *Service) UpsertInfraProvider( func (c *Service) UpsertConfigAndResources(
ctx context.Context, ctx context.Context,
infraProviderConfig *types.InfraProviderConfig, infraProviderConfig *types.InfraProviderConfig,
infraProviderResources []types.InfraProviderResource,
) error { ) error {
space, err := c.spaceFinder.FindByRef(ctx, infraProviderConfig.SpacePath) space, err := c.spaceFinder.FindByRef(ctx, infraProviderConfig.SpacePath)
if err != nil { if err != nil {
@ -35,7 +36,7 @@ func (c *Service) UpsertInfraProvider(
} }
err = c.tx.WithTx(ctx, func(ctx context.Context) error { err = c.tx.WithTx(ctx, func(ctx context.Context) error {
return c.upsertConfig(ctx, space, infraProviderConfig) return c.upsertConfigAndResources(ctx, space, infraProviderConfig, infraProviderResources)
}) })
if err != nil { if err != nil {
return fmt.Errorf("failed to complete txn for the infraprovider: %w", err) return fmt.Errorf("failed to complete txn for the infraprovider: %w", err)
@ -43,59 +44,35 @@ func (c *Service) UpsertInfraProvider(
return nil return nil
} }
func (c *Service) upsertConfig( func (c *Service) upsertConfigAndResources(
ctx context.Context, ctx context.Context,
space *types.SpaceCore, space *types.SpaceCore,
infraProviderConfig *types.InfraProviderConfig, infraProviderConfig *types.InfraProviderConfig,
infraProviderResources []types.InfraProviderResource,
) error { ) error {
providerConfigInDB, err := c.Find(ctx, space, infraProviderConfig.Identifier) providerConfigInDB, err := c.Find(ctx, space, infraProviderConfig.Identifier)
var infraProviderConfigID int64 var infraProviderConfigID int64
if errors.Is(err, store.ErrResourceNotFound) { if errors.Is(err, store.ErrResourceNotFound) { // nolint:gocritic
if infraProviderConfigID, err = c.createConfig(ctx, infraProviderConfig); err != nil { configID, createErr := c.createConfig(ctx, infraProviderConfig)
if createErr != nil {
return fmt.Errorf("could not create the config: %q %w", infraProviderConfig.Identifier, err) return fmt.Errorf("could not create the config: %q %w", infraProviderConfig.Identifier, err)
} }
infraProviderConfigID = configID
log.Info().Msgf("created new infraconfig %s", infraProviderConfig.Identifier) log.Info().Msgf("created new infraconfig %s", infraProviderConfig.Identifier)
} else if err != nil { // todo: should this not be err == nil? } else if err != nil {
infraProviderConfig.ID = providerConfigInDB.ID
if err = c.updateConfig(ctx, infraProviderConfig); err != nil {
return fmt.Errorf("could not update the config %s: %w", infraProviderConfig.Identifier, err)
}
log.Info().Msgf("updated infraconfig %s", infraProviderConfig.Identifier)
}
if err != nil {
return err return err
} else {
infraProviderConfigID = providerConfigInDB.ID
} }
if err = c.UpsertResources(ctx, infraProviderConfig.Resources, infraProviderConfigID, space.ID); err != nil {
return err
}
return nil
}
func (c *Service) UpsertResources( infraProviderConfig.ID = infraProviderConfigID
ctx context.Context, if err = c.UpdateConfig(ctx, infraProviderConfig); err != nil {
resources []types.InfraProviderResource, return fmt.Errorf("could not update the config %s: %w", infraProviderConfig.Identifier, err)
configID int64, }
spaceID int64,
) error { log.Info().Msgf("updated infraconfig %s", infraProviderConfig.Identifier)
for idx := range resources { if err = c.createMissingResources(ctx, infraProviderResources, infraProviderConfigID, space.ID); err != nil {
resource := &resources[idx] return err
resource.InfraProviderConfigID = configID
resource.SpaceID = spaceID
if err := c.validate(ctx, resource); err != nil {
return err
}
_, err := c.infraProviderResourceStore.FindByIdentifier(ctx, resource.SpaceID, resource.UID)
if errors.Is(err, store.ErrResourceNotFound) {
if err = c.infraProviderResourceStore.Create(ctx, resource); err != nil {
return fmt.Errorf("failed to create infraprovider resource for %s: %w", resource.UID, err)
}
log.Info().Msgf("created new resource %s/%s", resource.InfraProviderConfigIdentifier, resource.UID)
} else {
if err = c.UpdateResource(ctx, *resource); err != nil {
log.Info().Msgf("updated resource %s/%s", resource.InfraProviderConfigIdentifier, resource.UID)
return fmt.Errorf("could not update the resources %s: %w", resource.UID, err)
}
}
} }
return nil return nil
} }

View File

@ -29,11 +29,13 @@ var WireSet = wire.NewSet(
func ProvideInfraProvider( func ProvideInfraProvider(
tx dbtx.Transactor, tx dbtx.Transactor,
gitspaceConfigStore store.GitspaceConfigStore,
resourceStore store.InfraProviderResourceStore, resourceStore store.InfraProviderResourceStore,
configStore store.InfraProviderConfigStore, configStore store.InfraProviderConfigStore,
templateStore store.InfraProviderTemplateStore, templateStore store.InfraProviderTemplateStore,
infraProviderFactory infraprovider.Factory, infraProviderFactory infraprovider.Factory,
spaceFinder refcache.SpaceFinder, spaceFinder refcache.SpaceFinder,
) *Service { ) *Service {
return NewService(tx, resourceStore, configStore, templateStore, infraProviderFactory, spaceFinder) return NewService(tx, gitspaceConfigStore, resourceStore, configStore, templateStore, infraProviderFactory,
spaceFinder)
} }

View File

@ -705,6 +705,12 @@ type (
// Count the number of gitspace configs in a space matching the given filter. // Count the number of gitspace configs in a space matching the given filter.
Count(ctx context.Context, filter *types.GitspaceFilter) (int64, error) Count(ctx context.Context, filter *types.GitspaceFilter) (int64, error)
// ListActiveConfigsForInfraProviderResource returns all active configs for the given infra resource.
ListActiveConfigsForInfraProviderResource(
ctx context.Context,
infraProviderResourceID int64,
) ([]*types.GitspaceConfig, error)
} }
GitspaceInstanceStore interface { GitspaceInstanceStore interface {
@ -768,15 +774,18 @@ type (
// Find returns a Infra provider resource given a ID from the datastore. // Find returns a Infra provider resource given a ID from the datastore.
Find(ctx context.Context, id int64) (*types.InfraProviderResource, error) Find(ctx context.Context, id int64) (*types.InfraProviderResource, error)
// FindByIdentifier returns a infra provider resource with a given UID in a space // FindByConfigAndIdentifier returns the most recent infra provider resource with a given identifier in
FindByIdentifier(ctx context.Context, spaceID int64, identifier string) (*types.InfraProviderResource, error) // a space for the given infra provider config.
FindByConfigAndIdentifier(
ctx context.Context,
spaceID int64,
infraProviderConfigID int64,
identifier string,
) (*types.InfraProviderResource, error)
// Create creates a new infra provider resource in the datastore. // Create creates a new infra provider resource in the datastore.
Create(ctx context.Context, infraProviderResource *types.InfraProviderResource) error Create(ctx context.Context, infraProviderResource *types.InfraProviderResource) error
// Update tries to update the infra provider resource in the datastore.
Update(ctx context.Context, infraProviderResource *types.InfraProviderResource) error
// List lists the infra provider resource present for the gitspace config in a parent space ID in the datastore. // List lists the infra provider resource present for the gitspace config in a parent space ID in the datastore.
List( List(
ctx context.Context, ctx context.Context,
@ -784,8 +793,8 @@ type (
filter types.ListQueryFilter, filter types.ListQueryFilter,
) ([]*types.InfraProviderResource, error) ) ([]*types.InfraProviderResource, error)
// DeleteByIdentifier deletes the Infra provider resource with the given identifier for the given space. // Delete soft deletes the Infra provider resource with the given id.
DeleteByIdentifier(ctx context.Context, spaceID int64, identifier string) error Delete(ctx context.Context, id int64) error
} }
PipelineStore interface { PipelineStore interface {

View File

@ -398,6 +398,30 @@ func (s gitspaceConfigStore) FindAll(ctx context.Context, ids []int64) ([]*types
return s.mapToGitspaceConfigs(ctx, dst) return s.mapToGitspaceConfigs(ctx, dst)
} }
func (s gitspaceConfigStore) ListActiveConfigsForInfraProviderResource(
ctx context.Context,
infraProviderResourceID int64,
) ([]*types.GitspaceConfig, error) {
stmt := database.Builder.
Select(gitspaceConfigSelectColumns).
From(gitspaceConfigsTable).
Where("gconf_infra_provider_resource_id = ?", infraProviderResourceID).
Where("gconf_is_deleted = false").
Where("gconf_is_marked_for_deletion = false")
sql, args, err := stmt.ToSql()
if err != nil {
return nil, errors.Wrap(err, "Failed to convert squirrel builder to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
var dst []*gitspaceConfigWithLatestInstance
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "Failed executing list gitspace config query")
}
return s.ToGitspaceConfigs(ctx, dst)
}
func (s gitspaceConfigStore) mapDBToGitspaceConfig( func (s gitspaceConfigStore) mapDBToGitspaceConfig(
ctx context.Context, ctx context.Context,
in *gitspaceConfig, in *gitspaceConfig,

View File

@ -124,7 +124,8 @@ func (i infraProviderConfigStore) FindByType(
db := dbtx.GetAccessor(ctx, i.db) db := dbtx.GetAccessor(ctx, i.db)
dst := new([]*infraProviderConfig) dst := new([]*infraProviderConfig)
if err := db.SelectContext(ctx, dst, sql, args...); err != nil { if err := db.SelectContext(ctx, dst, sql, args...); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list infraprovider resources") return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list infraprovider configs of type %s for"+
" space %d", infraProviderType, spaceID)
} }
return i.mapToInfraProviderConfigs(*dst) return i.mapToInfraProviderConfigs(*dst)
} }

View File

@ -17,7 +17,7 @@ package database
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "time"
"github.com/harness/gitness/app/store" "github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database" "github.com/harness/gitness/store/database"
@ -46,7 +46,9 @@ const (
ipreso_disk, ipreso_disk,
ipreso_network, ipreso_network,
ipreso_region, ipreso_region,
ipreso_opentofu_params ipreso_metadata,
ipreso_is_deleted,
ipreso_deleted
` `
infraProviderResourceSelectColumns = "ipreso_id," + infraProviderResourceInsertColumns infraProviderResourceSelectColumns = "ipreso_id," + infraProviderResourceInsertColumns
infraProviderResourceTable = `infra_provider_resources` infraProviderResourceTable = `infra_provider_resources`
@ -64,14 +66,16 @@ type infraProviderResource struct {
Disk null.String `db:"ipreso_disk"` Disk null.String `db:"ipreso_disk"`
Network null.String `db:"ipreso_network"` Network null.String `db:"ipreso_network"`
Region string `db:"ipreso_region"` // need list maybe Region string `db:"ipreso_region"` // need list maybe
OpenTofuParams []byte `db:"ipreso_opentofu_params"` Metadata []byte `db:"ipreso_metadata"`
Created int64 `db:"ipreso_created"` Created int64 `db:"ipreso_created"`
Updated int64 `db:"ipreso_updated"` Updated int64 `db:"ipreso_updated"`
IsDeleted bool `db:"ipreso_is_deleted"`
Deleted null.Int `db:"ipreso_deleted"`
} }
var _ store.InfraProviderResourceStore = (*infraProviderResourceStore)(nil) var _ store.InfraProviderResourceStore = (*infraProviderResourceStore)(nil)
// NewGitspaceConfigStore returns a new GitspaceConfigStore. // NewInfraProviderResourceStore returns a new InfraProviderResourceStore.
func NewInfraProviderResourceStore(db *sqlx.DB) store.InfraProviderResourceStore { func NewInfraProviderResourceStore(db *sqlx.DB) store.InfraProviderResourceStore {
return &infraProviderResourceStore{ return &infraProviderResourceStore{
db: db, db: db,
@ -82,12 +86,23 @@ type infraProviderResourceStore struct {
db *sqlx.DB db *sqlx.DB
} }
func (s infraProviderResourceStore) List(ctx context.Context, infraProviderConfigID int64, func (s infraProviderResourceStore) List(
_ types.ListQueryFilter) ([]*types.InfraProviderResource, error) { ctx context.Context,
stmt := database.Builder. infraProviderConfigID int64,
Select(infraProviderResourceSelectColumns). _ types.ListQueryFilter,
) ([]*types.InfraProviderResource, error) {
subQuery := squirrel.Select("MAX(ipreso_created)").
From(infraProviderResourceTable). From(infraProviderResourceTable).
Where("ipreso_infra_provider_config_id = $1", infraProviderConfigID) Where("ipreso_infra_provider_config_id = $1", infraProviderConfigID).
Where("ipreso_is_deleted = false").
GroupBy("ipreso_uid")
stmt := squirrel.Select(infraProviderResourceSelectColumns).
From(infraProviderResourceTable).
Where("ipreso_infra_provider_config_id = $2", infraProviderConfigID).
Where("ipreso_is_deleted = false").
Where(squirrel.Expr("ipreso_created IN (?)", subQuery)).
OrderBy("ipreso_uid", "ipreso_created DESC")
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
if err != nil { if err != nil {
@ -96,7 +111,8 @@ func (s infraProviderResourceStore) List(ctx context.Context, infraProviderConfi
db := dbtx.GetAccessor(ctx, s.db) db := dbtx.GetAccessor(ctx, s.db)
dst := new([]infraProviderResource) dst := new([]infraProviderResource)
if err := db.SelectContext(ctx, dst, sql, args...); err != nil { if err := db.SelectContext(ctx, dst, sql, args...); err != nil {
return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list infraprovider resources") return nil, database.ProcessSQLErrorf(ctx, err, "Failed to list infraprovider resources for config %d",
infraProviderConfigID)
} }
return mapToInfraProviderResources(*dst) return mapToInfraProviderResources(*dst)
} }
@ -105,7 +121,8 @@ func (s infraProviderResourceStore) Find(ctx context.Context, id int64) (*types.
stmt := database.Builder. stmt := database.Builder.
Select(infraProviderResourceSelectColumns). Select(infraProviderResourceSelectColumns).
From(infraProviderResourceTable). From(infraProviderResourceTable).
Where(infraProviderResourceIDColumn+" = $1", id) Where(infraProviderResourceIDColumn+" = $1", id).
Where("ipreso_is_deleted = false")
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
if err != nil { if err != nil {
@ -119,16 +136,23 @@ func (s infraProviderResourceStore) Find(ctx context.Context, id int64) (*types.
return mapToInfraProviderResource(dst) return mapToInfraProviderResource(dst)
} }
func (s infraProviderResourceStore) FindByIdentifier( func (s infraProviderResourceStore) FindByConfigAndIdentifier(
ctx context.Context, ctx context.Context,
spaceID int64, spaceID int64,
infraProviderConfigID int64,
identifier string, identifier string,
) (*types.InfraProviderResource, error) { ) (*types.InfraProviderResource, error) {
stmt := database.Builder. stmt :=
Select(infraProviderResourceSelectColumns). database.Builder.
From(infraProviderResourceTable). Select(infraProviderResourceSelectColumns).
Where("ipreso_uid = $1", identifier). From(infraProviderResourceTable).
Where("ipreso_space_id = $2", spaceID) OrderBy("ipreso_created DESC").
Limit(1).
Where("ipreso_uid = ?", identifier).
Where("ipreso_space_id = ?", spaceID).
Where("ipreso_infra_provider_config_id = ?", infraProviderConfigID).
Where("ipreso_is_deleted = false")
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "Failed to convert squirrel builder to sql") return nil, errors.Wrap(err, "Failed to convert squirrel builder to sql")
@ -145,7 +169,7 @@ func (s infraProviderResourceStore) Create(
ctx context.Context, ctx context.Context,
infraProviderResource *types.InfraProviderResource, infraProviderResource *types.InfraProviderResource,
) error { ) error {
jsonBytes, marshalErr := json.Marshal(infraProviderResource.Metadata) metadata, marshalErr := json.Marshal(infraProviderResource.Metadata)
if marshalErr != nil { if marshalErr != nil {
return marshalErr return marshalErr
} }
@ -165,7 +189,9 @@ func (s infraProviderResourceStore) Create(
infraProviderResource.Disk, infraProviderResource.Disk,
infraProviderResource.Network, infraProviderResource.Network,
infraProviderResource.Region, infraProviderResource.Region,
jsonBytes, metadata,
infraProviderResource.IsDeleted,
infraProviderResource.Deleted,
). ).
Suffix(ReturningClause + infraProviderResourceIDColumn) Suffix(ReturningClause + infraProviderResourceIDColumn)
sql, args, err := stmt.ToSql() sql, args, err := stmt.ToSql()
@ -180,57 +206,9 @@ func (s infraProviderResourceStore) Create(
return nil return nil
} }
func (s infraProviderResourceStore) Update(
ctx context.Context,
infraProviderResource *types.InfraProviderResource,
) error {
dbinfraProviderResource, err := s.mapToInternalInfraProviderResource(infraProviderResource)
if err != nil {
return fmt.Errorf(
"failed to map to DB Obj for infraprovider resource %s", infraProviderResource.UID)
}
stmt := database.Builder.
Update(infraProviderResourceTable).
Set("ipreso_display_name", dbinfraProviderResource.Name).
Set("ipreso_memory", dbinfraProviderResource.Memory).
Set("ipreso_disk", dbinfraProviderResource.Disk).
Set("ipreso_network", dbinfraProviderResource.Network).
Set("ipreso_region", dbinfraProviderResource.Region).
Set("ipreso_opentofu_params", dbinfraProviderResource.OpenTofuParams).
Set("ipreso_updated", dbinfraProviderResource.Updated).
Where("ipreso_id = ?", infraProviderResource.ID)
sql, args, err := stmt.ToSql()
if err != nil {
return errors.Wrap(err, "Failed to convert squirrel builder to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, sql, args...); err != nil {
return database.ProcessSQLErrorf(
ctx, err, "Failed to update infraprovider resource %s", infraProviderResource.UID)
}
return nil
}
func (s infraProviderResourceStore) DeleteByIdentifier(ctx context.Context, spaceID int64, identifier string) error {
stmt := database.Builder.
Delete(infraProviderResourceTable).
Where("ipreso_uid = $1", identifier).
Where("ipreso_space_id = $2", spaceID)
sql, args, err := stmt.ToSql()
if err != nil {
return errors.Wrap(err, "Failed to convert squirrel builder to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, sql, args...); err != nil {
return database.ProcessSQLErrorf(
ctx, err, "Failed to delete infra provider resource %s", identifier)
}
return nil
}
func mapToInfraProviderResource(in *infraProviderResource) (*types.InfraProviderResource, error) { func mapToInfraProviderResource(in *infraProviderResource) (*types.InfraProviderResource, error) {
metadataParamsMap := make(map[string]string) metadataParamsMap := make(map[string]string)
marshalErr := json.Unmarshal(in.OpenTofuParams, &metadataParamsMap) marshalErr := json.Unmarshal(in.Metadata, &metadataParamsMap)
if marshalErr != nil { if marshalErr != nil {
return nil, marshalErr return nil, marshalErr
} }
@ -249,30 +227,8 @@ func mapToInfraProviderResource(in *infraProviderResource) (*types.InfraProvider
Metadata: metadataParamsMap, Metadata: metadataParamsMap,
Created: in.Created, Created: in.Created,
Updated: in.Updated, Updated: in.Updated,
}, nil IsDeleted: in.IsDeleted,
} Deleted: in.Deleted.Ptr(),
func (s infraProviderResourceStore) mapToInternalInfraProviderResource(
in *types.InfraProviderResource,
) (*infraProviderResource, error) {
jsonBytes, marshalErr := json.Marshal(in.Metadata)
if marshalErr != nil {
return nil, marshalErr
}
return &infraProviderResource{
Identifier: in.UID,
InfraProviderConfigID: in.InfraProviderConfigID,
InfraProviderType: in.InfraProviderType,
Name: in.Name,
SpaceID: in.SpaceID,
CPU: null.StringFromPtr(in.CPU),
Memory: null.StringFromPtr(in.Memory),
Disk: null.StringFromPtr(in.Disk),
Network: null.StringFromPtr(in.Network),
Region: in.Region,
OpenTofuParams: jsonBytes,
Created: in.Created,
Updated: in.Updated,
}, nil }, nil
} }
@ -371,3 +327,23 @@ func (i InfraProviderResourceView) FindMany(ctx context.Context, ids []int64) ([
} }
return mapToInfraProviderResources(*dst) return mapToInfraProviderResources(*dst)
} }
func (s infraProviderResourceStore) Delete(ctx context.Context, id int64) error {
now := time.Now().UnixMilli()
stmt := database.Builder.
Update(infraProviderResourceTable).
Set("ipreso_updated", now).
Set("ipreso_deleted", now).
Set("ipreso_is_deleted", true).
Where("ipreso_id = $4", id)
sql, args, err := stmt.ToSql()
if err != nil {
return errors.Wrap(err, "Failed to convert squirrel builder to sql")
}
db := dbtx.GetAccessor(ctx, s.db)
if _, err := db.ExecContext(ctx, sql, args...); err != nil {
return database.ProcessSQLErrorf(
ctx, err, "Failed to update infraprovider resource %d", id)
}
return nil
}

View File

@ -0,0 +1,11 @@
DROP INDEX infra_provider_resources_uid_space_id_config_id_created;
ALTER TABLE infra_provider_resources
RENAME COLUMN ipreso_metadata TO ipreso_opentofu_params;
ALTER TABLE infra_provider_resources
DROP COLUMN ipreso_is_deleted;
ALTER TABLE infra_provider_resources
DROP COLUMN ipreso_deleted;
CREATE UNIQUE INDEX infra_provider_resources_uid_space_id ON infra_provider_resources (ipreso_uid, ipreso_space_id);

View File

@ -0,0 +1,10 @@
ALTER TABLE infra_provider_resources
RENAME COLUMN ipreso_opentofu_params TO ipreso_metadata;
ALTER TABLE infra_provider_resources
ADD COLUMN ipreso_is_deleted BOOL NOT NULL DEFAULT false;
ALTER TABLE infra_provider_resources
ADD COLUMN ipreso_deleted BIGINT;
DROP INDEX infra_provider_resources_uid_space_id;
CREATE UNIQUE INDEX infra_provider_resources_uid_space_id_config_id_created ON infra_provider_resources
(ipreso_uid, ipreso_space_id, ipreso_infra_provider_config_id, ipreso_created);

View File

@ -0,0 +1,11 @@
DROP INDEX infra_provider_resources_uid_space_id_config_id_created;
ALTER TABLE infra_provider_resources
RENAME COLUMN ipreso_metadata TO ipreso_opentofu_params;
ALTER TABLE infra_provider_resources
DROP COLUMN ipreso_is_deleted;
ALTER TABLE infra_provider_resources
DROP COLUMN ipreso_deleted;
CREATE UNIQUE INDEX infra_provider_resources_uid_space_id ON infra_provider_resources (ipreso_uid, ipreso_space_id);

View File

@ -0,0 +1,10 @@
ALTER TABLE infra_provider_resources
RENAME COLUMN ipreso_opentofu_params TO ipreso_metadata;
ALTER TABLE infra_provider_resources
ADD COLUMN ipreso_is_deleted BOOL NOT NULL DEFAULT false;
ALTER TABLE infra_provider_resources
ADD COLUMN ipreso_deleted BIGINT;
DROP INDEX infra_provider_resources_uid_space_id;
CREATE UNIQUE INDEX infra_provider_resources_uid_space_id_config_id_created ON infra_provider_resources
(ipreso_uid, ipreso_space_id, ipreso_infra_provider_config_id, ipreso_created);

View File

@ -308,7 +308,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
} }
dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory, reporter2) dockerProvider := infraprovider.ProvideDockerProvider(dockerConfig, dockerClientFactory, reporter2)
factory := infraprovider.ProvideFactory(dockerProvider) factory := infraprovider.ProvideFactory(dockerProvider)
infraproviderService := infraprovider2.ProvideInfraProvider(transactor, infraProviderResourceStore, infraProviderConfigStore, infraProviderTemplateStore, factory, spaceFinder) infraproviderService := infraprovider2.ProvideInfraProvider(transactor, gitspaceConfigStore, infraProviderResourceStore, infraProviderConfigStore, infraProviderTemplateStore, factory, spaceFinder)
gitnessSCM := scm.ProvideGitnessSCM(repoStore, repoFinder, gitInterface, tokenStore, principalStore, provider) gitnessSCM := scm.ProvideGitnessSCM(repoStore, repoFinder, gitInterface, tokenStore, principalStore, provider)
genericSCM := scm.ProvideGenericSCM() genericSCM := scm.ProvideGenericSCM()
scmFactory := scm.ProvideFactory(gitnessSCM, genericSCM) scmFactory := scm.ProvideFactory(gitnessSCM, genericSCM)

View File

@ -330,6 +330,6 @@ func volumeName(spacePath string, resourceKey string) string {
return name return name
} }
func (d DockerProvider) ValidateConfigAndResources(_ *types.InfraProviderConfig) error { func (d DockerProvider) ValidateConfig(_ *types.InfraProviderConfig) error {
return nil return nil
} }

View File

@ -69,6 +69,6 @@ type InfraProvider interface {
// ProvisioningType specifies whether the provider will provision new infra resources or it will reuse existing. // ProvisioningType specifies whether the provider will provision new infra resources or it will reuse existing.
ProvisioningType() enum.InfraProvisioningType ProvisioningType() enum.InfraProvisioningType
// ValidateConfigAndResources checks if the provided infra config and resources are valid as per the provider. // ValidateConfig checks if the provided infra config is as per the provider.
ValidateConfigAndResources(infraProviderConfig *types.InfraProviderConfig) error ValidateConfig(infraProviderConfig *types.InfraProviderConfig) error
} }

View File

@ -54,6 +54,8 @@ type InfraProviderResource struct {
InfraProviderType enum.InfraProviderType `json:"infra_provider_type"` InfraProviderType enum.InfraProviderType `json:"infra_provider_type"`
Created int64 `json:"created"` Created int64 `json:"created"`
Updated int64 `json:"updated"` Updated int64 `json:"updated"`
IsDeleted bool `json:"is_deleted,omitempty"`
Deleted *int64 `json:"deleted,omitempty"`
} }
func (i *InfraProviderResource) Identifier() int64 { func (i *InfraProviderResource) Identifier() int64 {