Skip to content

Commit

Permalink
Add noop repo resource limiter to controller (#926)
Browse files Browse the repository at this point in the history
  • Loading branch information
darkodraskovic authored and Harness committed Dec 22, 2023
1 parent 7118dd6 commit 1a8e99f
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 32 deletions.
44 changes: 44 additions & 0 deletions app/api/controller/limiter/limiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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:https://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 limiter

import (
"context"

"github.com/harness/gitness/errors"
)

var ErrMaxNumReposReached = errors.New("maximum number of repositories reached")

// ResourceLimiter is an interface for managing resource limitation.
type ResourceLimiter interface {
// RepoCount allows the creation of a specified number of repositories.
RepoCount(ctx context.Context, count int) error
}

var _ ResourceLimiter = unlimited{}

type unlimited struct {
}

// NewResourceLimiter creates a new instance of ResourceLimiter.
func NewResourceLimiter() ResourceLimiter {
return unlimited{}
}

//nolint:revive
func (unlimited) RepoCount(ctx context.Context, count int) error {
return nil
}
27 changes: 27 additions & 0 deletions app/api/controller/limiter/wire.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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:https://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 limiter

import (
"github.com/google/wire"
)

var WireSet = wire.NewSet(
ProvideLimiter,
)

func ProvideLimiter() (ResourceLimiter, error) {
return NewResourceLimiter(), nil
}
4 changes: 4 additions & 0 deletions app/api/controller/repo/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
Expand Down Expand Up @@ -62,6 +63,7 @@ type Controller struct {
codeOwners *codeowners.Service
eventReporter *repoevents.Reporter
indexer keywordsearch.Indexer
resourceLimiter limiter.ResourceLimiter
}

func NewController(
Expand All @@ -82,6 +84,7 @@ func NewController(
codeOwners *codeowners.Service,
eventReporter *repoevents.Reporter,
indexer keywordsearch.Indexer,
limiter limiter.ResourceLimiter,
) *Controller {
return &Controller{
defaultBranch: config.Git.DefaultBranch,
Expand All @@ -102,6 +105,7 @@ func NewController(
codeOwners: codeOwners,
eventReporter: eventReporter,
indexer: indexer,
resourceLimiter: limiter,
}
}

Expand Down
62 changes: 38 additions & 24 deletions app/api/controller/repo/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ package repo
import (
"bytes"
"context"
"database/sql"
"fmt"
"strings"
"time"

apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/bootstrap"
Expand Down Expand Up @@ -64,31 +66,43 @@ func (c *Controller) Create(ctx context.Context, session *auth.Session, in *Crea
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}

gitResp, err := c.createGitRepository(ctx, session, in)
if err != nil {
return nil, fmt.Errorf("error creating repository on git: %w", err)
}

now := time.Now().UnixMilli()
repo := &types.Repository{
Version: 0,
ParentID: parentSpace.ID,
UID: in.UID,
GitUID: gitResp.UID,
Description: in.Description,
IsPublic: in.IsPublic,
CreatedBy: session.Principal.ID,
Created: now,
Updated: now,
ForkID: in.ForkID,
DefaultBranch: in.DefaultBranch,
}
err = c.repoStore.Create(ctx, repo)
if err != nil {
if dErr := c.deleteGitRepository(ctx, session, repo); dErr != nil {
log.Ctx(ctx).Warn().Err(dErr).Msg("failed to delete repo for cleanup")
var repo *types.Repository
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
if err := c.resourceLimiter.RepoCount(ctx, 1); err != nil {
return fmt.Errorf("resource limit exceeded: %w", limiter.ErrMaxNumReposReached)
}

gitResp, err := c.createGitRepository(ctx, session, in)
if err != nil {
return fmt.Errorf("error creating repository on git: %w", err)
}
return nil, fmt.Errorf("failed to create repository in storage: %w", err)

now := time.Now().UnixMilli()
repo = &types.Repository{
Version: 0,
ParentID: parentSpace.ID,
UID: in.UID,
GitUID: gitResp.UID,
Description: in.Description,
IsPublic: in.IsPublic,
CreatedBy: session.Principal.ID,
Created: now,
Updated: now,
ForkID: in.ForkID,
DefaultBranch: in.DefaultBranch,
}
err = c.repoStore.Create(ctx, repo)
if err != nil {
if dErr := c.deleteGitRepository(ctx, session, repo); dErr != nil {
log.Ctx(ctx).Warn().Err(dErr).Msg("failed to delete repo for cleanup")
}
return fmt.Errorf("failed to create repository in storage: %w", err)
}

return nil
}, sql.TxOptions{Isolation: sql.LevelSerializable})
if err != nil {
return nil, err
}

// backfil GitURL
Expand Down
10 changes: 7 additions & 3 deletions app/api/controller/repo/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/importer"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/types"
)

Expand All @@ -46,13 +47,16 @@ func (c *Controller) Import(ctx context.Context, session *auth.Session, in *Impo
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}

remoteRepository, provider, err := importer.LoadRepositoryFromProvider(ctx, in.Provider, in.ProviderRepo)
if err != nil {
return nil, err
if err := c.resourceLimiter.RepoCount(ctx, 1); err != nil {
return nil, errors.PreconditionFailed(err.Error())
}

var repo *types.Repository
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
remoteRepository, provider, err := importer.LoadRepositoryFromProvider(ctx, in.Provider, in.ProviderRepo)
if err != nil {
return err
}
repo = remoteRepository.ToRepo(
parentSpace.ID,
in.UID,
Expand Down
4 changes: 3 additions & 1 deletion app/api/controller/repo/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package repo

import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/auth/authz"
repoevents "github.com/harness/gitness/app/events/repo"
"github.com/harness/gitness/app/services/codeowners"
Expand Down Expand Up @@ -54,10 +55,11 @@ func ProvideController(
codeOwners *codeowners.Service,
reporeporter *repoevents.Reporter,
indexer keywordsearch.Indexer,
limiter limiter.ResourceLimiter,
) *Controller {
return NewController(config, tx, urlProvider,
uidCheck, authorizer, repoStore,
spaceStore, pipelineStore,
principalStore, ruleStore, principalInfoCache, protectionManager,
rpcClient, importer, codeOwners, reporeporter, indexer)
rpcClient, importer, codeOwners, reporeporter, indexer, limiter)
}
4 changes: 4 additions & 0 deletions app/api/controller/space/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package space

import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/controller/repo"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth/authz"
Expand Down Expand Up @@ -55,6 +56,7 @@ type Controller struct {
membershipStore store.MembershipStore
importer *importer.Repository
exporter *exporter.Repository
resourceLimiter limiter.ResourceLimiter
}

func NewController(config *types.Config, tx dbtx.Transactor, urlProvider url.Provider,
Expand All @@ -63,6 +65,7 @@ func NewController(config *types.Config, tx dbtx.Transactor, urlProvider url.Pro
connectorStore store.ConnectorStore, templateStore store.TemplateStore, spaceStore store.SpaceStore,
repoStore store.RepoStore, principalStore store.PrincipalStore, repoCtrl *repo.Controller,
membershipStore store.MembershipStore, importer *importer.Repository, exporter *exporter.Repository,
limiter limiter.ResourceLimiter,
) *Controller {
return &Controller{
nestedSpacesEnabled: config.NestedSpacesEnabled,
Expand All @@ -84,5 +87,6 @@ func NewController(config *types.Config, tx dbtx.Transactor, urlProvider url.Pro
membershipStore: membershipStore,
importer: importer,
exporter: exporter,
resourceLimiter: limiter,
}
}
5 changes: 5 additions & 0 deletions app/api/controller/space/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"context"
"fmt"

"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/importer"
Expand Down Expand Up @@ -66,6 +67,10 @@ func (c *Controller) Import(ctx context.Context, session *auth.Session, in *Impo

var space *types.Space
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
if err := c.resourceLimiter.RepoCount(ctx, len(remoteRepositories)); err != nil {
return fmt.Errorf("resource limit exceeded: %w", limiter.ErrMaxNumReposReached)
}

space, err = c.createSpaceInnerInTX(ctx, session, parentSpaceID, &in.CreateInput)
if err != nil {
return err
Expand Down
10 changes: 10 additions & 0 deletions app/api/controller/space/import_repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import (
"fmt"

apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/importer"
gitnesserrors "github.com/harness/gitness/errors"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
Expand Down Expand Up @@ -89,12 +91,20 @@ func (c *Controller) ImportRepositories(
return ImportRepositoriesOutput{}, usererror.BadRequestf("found no repositories at %s", in.ProviderSpace)
}

if err := c.resourceLimiter.RepoCount(ctx, len(remoteRepositories)); err != nil {
return ImportRepositoriesOutput{}, gitnesserrors.PreconditionFailed(err.Error())
}

repoIDs := make([]int64, 0, len(remoteRepositories))
cloneURLs := make([]string, 0, len(remoteRepositories))
repos := make([]*types.Repository, 0, len(remoteRepositories))
duplicateRepos := make([]*types.Repository, 0, len(remoteRepositories))

err = c.tx.WithTx(ctx, func(ctx context.Context) error {
if err := c.resourceLimiter.RepoCount(ctx, len(remoteRepositories)); err != nil {
return fmt.Errorf("resource limit exceeded: %w", limiter.ErrMaxNumReposReached)
}

for _, remoteRepository := range remoteRepositories {
repo := remoteRepository.ToRepo(
space.ID,
Expand Down
5 changes: 3 additions & 2 deletions app/api/controller/space/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package space

import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/controller/repo"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/exporter"
Expand All @@ -40,11 +41,11 @@ func ProvideController(config *types.Config, tx dbtx.Transactor, urlProvider url
connectorStore store.ConnectorStore, templateStore store.TemplateStore,
spaceStore store.SpaceStore, repoStore store.RepoStore, principalStore store.PrincipalStore,
repoCtrl *repo.Controller, membershipStore store.MembershipStore, importer *importer.Repository,
exporter *exporter.Repository,
exporter *exporter.Repository, limiter limiter.ResourceLimiter,
) *Controller {
return NewController(config, tx, urlProvider, sseStreamer, uidCheck, authorizer,
spacePathStore, pipelineStore, secretStore,
connectorStore, templateStore,
spaceStore, repoStore, principalStore,
repoCtrl, membershipStore, importer, exporter)
repoCtrl, membershipStore, importer, exporter, limiter)
}
3 changes: 3 additions & 0 deletions app/api/usererror/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"net/http"

apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/services/codeowners"
"github.com/harness/gitness/app/services/webhook"
"github.com/harness/gitness/blob"
Expand Down Expand Up @@ -72,6 +73,8 @@ func Translate(err error) *Error {
return ErrCyclicHierarchy
case errors.Is(err, store.ErrSpaceWithChildsCantBeDeleted):
return ErrSpaceWithChildsCantBeDeleted
case errors.Is(err, limiter.ErrMaxNumReposReached):
return Forbidden(err.Error())

// upload errors
case errors.Is(err, blob.ErrNotFound):
Expand Down
2 changes: 2 additions & 0 deletions cmd/gitness/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/harness/gitness/app/api/controller/connector"
"github.com/harness/gitness/app/api/controller/execution"
controllerkeywordsearch "github.com/harness/gitness/app/api/controller/keywordsearch"
"github.com/harness/gitness/app/api/controller/limiter"
controllerlogs "github.com/harness/gitness/app/api/controller/logs"
"github.com/harness/gitness/app/api/controller/pipeline"
"github.com/harness/gitness/app/api/controller/plugin"
Expand Down Expand Up @@ -105,6 +106,7 @@ func initSystem(ctx context.Context, config *types.Config) (*cliserver.System, e
server.WireSet,
url.WireSet,
space.WireSet,
limiter.WireSet,
repo.WireSet,
pullreq.WireSet,
controllerwebhook.WireSet,
Expand Down
Loading

0 comments on commit 1a8e99f

Please sign in to comment.