Skip to content

Commit

Permalink
Add validation for adding profiles
Browse files Browse the repository at this point in the history
- Adds cobra cmd and a number of important flags for 'gitops add profile' such as --config-file, --name, --cluster, --version, and --auto-merge.
- Integration tests check that profile has been added to cluster. Also check that HelmRelease source has been generated.
- Validates given values for basic/required flags.
  • Loading branch information
nikimanoledaki committed Feb 2, 2022
1 parent 0d6fd9c commit 0ae622b
Show file tree
Hide file tree
Showing 10 changed files with 459 additions and 36 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ ginkgo.report
.deps
test/library/wego-library-test
tilt_modules
.envrc
2 changes: 2 additions & 0 deletions cmd/gitops/add/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/spf13/cobra"
"github.com/weaveworks/weave-gitops/cmd/gitops/add/app"
"github.com/weaveworks/weave-gitops/cmd/gitops/add/clusters"
"github.com/weaveworks/weave-gitops/cmd/gitops/add/profile"
)

func GetCommand(endpoint *string, client *resty.Client) *cobra.Command {
Expand All @@ -21,6 +22,7 @@ gitops add cluster`,

cmd.AddCommand(clusters.ClusterCommand(endpoint, client))
cmd.AddCommand(app.Cmd)
cmd.AddCommand(profile.ProfileCommand(client))

return cmd
}
64 changes: 64 additions & 0 deletions cmd/gitops/add/profile/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package profile

import (
"context"
"fmt"
"os"

"github.com/go-resty/resty/v2"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/weaveworks/weave-gitops/cmd/internal"
"github.com/weaveworks/weave-gitops/pkg/flux"
"github.com/weaveworks/weave-gitops/pkg/osys"
"github.com/weaveworks/weave-gitops/pkg/runner"
"github.com/weaveworks/weave-gitops/pkg/services"
"github.com/weaveworks/weave-gitops/pkg/services/auth"
"github.com/weaveworks/weave-gitops/pkg/services/profile"
)

var params profile.AddParams

// Provides support for adding a profile to gitops management.
func ProfileCommand(client *resty.Client) *cobra.Command {
cmd := &cobra.Command{
Use: "profile",
Short: "Add a profile to the cluster",
RunE: addProfileCmdRunE(client),
}

cmd.Flags().StringVar(&params.Name, "name", "", "Name of the profile")
cmd.Flags().StringVar(&params.Version, "version", "", "Version of the profile")
cmd.Flags().StringVar(&params.ConfigRepo, "config-repo", "", "URL of external repository (if any) which will hold automation manifests")
cmd.Flags().StringVar(&params.Cluster, "cluster", "", "Name of the cluster to add the profile to")
cmd.Flags().BoolVar(&params.AutoMerge, "auto-merge", false, "If set, 'gitops add profile' will merge automatically into the set --branch")

return cmd
}

func addProfileCmdRunE(client *resty.Client) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
log := internal.NewCLILogger(os.Stdout)
fluxClient := flux.New(osys.New(), &runner.CLIRunner{})
factory := services.NewFactory(fluxClient, log)
providerClient := internal.NewGitProviderClient(os.Stdout, os.LookupEnv, auth.NewAuthCLIHandler, log)

ctx := context.Background()
_, gitProvider, err := factory.GetGitClients(ctx, providerClient, services.GitConfigParams{
ConfigRepo: params.ConfigRepo,
Namespace: "weave-system",
IsHelmRepository: true,
DryRun: false,
})
if err != nil {
return fmt.Errorf("failed to get git clients: %w", err)
}

profileService := profile.NewService(ctx, log, osys.New())
if err := profileService.Add(gitProvider, params); err != nil {
return errors.Wrapf(err, "failed to add the profile %s", params.Name)
}

return nil
}
}
52 changes: 52 additions & 0 deletions cmd/gitops/add/profile/cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package profile_test

import (
"github.com/go-resty/resty/v2"
"github.com/jarcoal/httpmock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/spf13/cobra"
"github.com/weaveworks/weave-gitops/cmd/gitops/root"
)

var _ = Describe("Add Profiles", func() {
var (
cmd *cobra.Command
)

BeforeEach(func() {
client := resty.New()
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
cmd = root.RootCmd(client)
})

When("the flags are valid", func() {
It("accepts all known flags for adding a profile", func() {
cmd.SetArgs([]string{
"add", "profile",
"--name", "podinfo",
"--version", "0.0.1",
"--cluster", "prod",
"--config-repo", "https://ssh@github:test/test.git",
"--auto-merge", "true",
})

err := cmd.Execute()
Expect(err.Error()).NotTo(ContainSubstring("unknown flag"))
})
})

When("a flag is unknown", func() {
It("fails", func() {
cmd.SetArgs([]string{
"add", "profile",
"--unknown", "param",
})

err := cmd.Execute()
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("unknown flag: --unknown"))
})
})
})
13 changes: 13 additions & 0 deletions cmd/gitops/add/profile/profile_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package profile_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestProfile(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Profile Suite")
}
73 changes: 73 additions & 0 deletions pkg/services/profile/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package profile

import (
"context"
"errors"
"fmt"
"strings"

"github.com/weaveworks/weave-gitops/pkg/git"
"github.com/weaveworks/weave-gitops/pkg/gitproviders"
"github.com/weaveworks/weave-gitops/pkg/services/automation"
)

type AddParams struct {
Name string
Cluster string
ConfigRepo string
Version string
AutoMerge bool
}

// Add adds a new profile to the cluster
func (s *ProfileSvc) Add(gitProvider gitproviders.GitProvider, params AddParams) error {
validatedParams, err := s.ValidateAddParams(params)
if err != nil {
return err
}

ctx := context.Background()

configRepoUrl, err := gitproviders.NewRepoURL(params.ConfigRepo)
if err != nil {
return err
}

repoExists, err := gitProvider.RepositoryExists(ctx, configRepoUrl)
if err != nil {
return err
} else if !repoExists {
return fmt.Errorf("repository '%v' could not be found", configRepoUrl.String())
}

s.printAddSummary(validatedParams)
return nil
}

func (s *ProfileSvc) ValidateAddParams(params AddParams) (AddParams, error) {
if params.ConfigRepo == "" {
return params, errors.New("--config-repo should be provided")
}

if params.Name == "" {
return params, errors.New("--name should be provided")
}

if automation.ApplicationNameTooLong(params.Name) {
return params, fmt.Errorf("--name value is too long: %s; must be <= %d characters",
params.Name, automation.MaxKubernetesResourceNameLength)
}

if strings.HasPrefix(params.Name, "wego") {
return params, fmt.Errorf("the prefix 'wego' is used by weave gitops and is not allowed for a profile name")
}

return params, nil
}

func (s *ProfileSvc) printAddSummary(params AddParams) {
s.Logger.Println("Adding profile:\n")
s.Logger.Println("Name: %s", params.Name)

s.Logger.Println("")
}
99 changes: 99 additions & 0 deletions pkg/services/profile/add_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package profile_test

import (
"github.com/weaveworks/weave-gitops/pkg/services/profile"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var addParams profile.AddParams

var _ = Describe("Add Profile", func() {
BeforeEach(func() {
addParams = profile.AddParams{
ConfigRepo: "ssh://[email protected]/owner/config-repo.git",
Name: "foo",
}
})

It("adds a profile", func() {
gitProviders.RepositoryExistsReturns(true, nil)
gitProviders.GetRepoFilesReturns(makeTestFiles(), nil)
Expect(profileSvc.Add(gitProviders, addParams)).Should(Succeed())
Expect(gitProviders.RepositoryExistsCallCount()).To(Equal(1))
})

It("fails if the config repo does not exist", func() {
gitProviders.RepositoryExistsReturns(false, nil)
err := profileSvc.Add(gitProviders, addParams)
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("repository 'ssh://[email protected]/owner/config-repo.git' could not be found"))
})

It("fails if the --config-repo url format is wrong", func() {
addParams = profile.AddParams{
Name: "foo",
ConfigRepo: "{http:/-*wrong-url-827",
}

err := profileSvc.Add(gitProviders, addParams)
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("could not get provider name from URL {http:/-*wrong-url-827: could not parse git repo url \"{http:/-*wrong-url-827\": parse \"{http:/-*wrong-url-827\": first path segment in URL cannot contain colon"))
})

It("fails if the cluster name is not found", func() {
gitProviders.RepositoryExistsReturns(true, nil)
err := profileSvc.Add(gitClient, gitProviders, addParams)
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("failed to fetch cluster name"))
})
})

var _ = Describe("ValidateAddParams", func() {
It("fails if --config-repo is not provided", func() {
_, err := profileSvc.ValidateAddParams(profile.AddParams{})
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("--config-repo should be provided"))
})

When("--name is specified", func() {
It("fails if --name value is <= 63 characters in length", func() {
addParams = profile.AddParams{
Name: "a234567890123456789012345678901234567890123456789012345678901234",
ConfigRepo: "ssh://[email protected]/owner/config-repo.git",
}

_, err := profileSvc.ValidateAddParams(addParams)
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("--name value is too long: a234567890123456789012345678901234567890123456789012345678901234; must be <= 63 characters"))
})

It("fails if --name is prefixed by 'wego'", func() {
addParams = profile.AddParams{
Name: "wego-app",
ConfigRepo: "ssh://[email protected]/owner/config-repo.git",
}

_, err := profileSvc.ValidateAddParams(addParams)
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("the prefix 'wego' is used by weave gitops and is not allowed for a profile name"))
})
})

When("--name is not specified", func() {
It("fails", func() {
addParams = profile.AddParams{
ConfigRepo: "ssh://[email protected]/owner/config-repo.git",
}

_, err := profileSvc.ValidateAddParams(addParams)
Expect(err).NotTo(BeNil())
Expect(err).To(MatchError("--name should be provided"))
})
})
})

var _ = Describe("Add Profile with Gitlab", func() {
// TODO
})
32 changes: 32 additions & 0 deletions pkg/services/profile/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package profile

import (
"context"

"github.com/benbjohnson/clock"
"github.com/weaveworks/weave-gitops/pkg/git"
"github.com/weaveworks/weave-gitops/pkg/gitproviders"
"github.com/weaveworks/weave-gitops/pkg/logger"
"github.com/weaveworks/weave-gitops/pkg/osys"
)

type ProfileService interface {
// Add adds a new profile to the cluster
Add(configGit git.Git, gitProvider gitproviders.GitProvider, params AddParams) error
}

type ProfileSvc struct {
Context context.Context
Osys osys.Osys
Logger logger.Logger
Clock clock.Clock
}

func NewService(ctx context.Context, logger logger.Logger, osys osys.Osys) *ProfileSvc {
return &ProfileSvc{
Context: ctx,
Logger: logger,
Osys: osys,
Clock: clock.New(),
}
}
36 changes: 36 additions & 0 deletions pkg/services/profile/profile_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package profile_test

import (
"context"
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/weaveworks/weave-gitops/pkg/git/gitfakes"
"github.com/weaveworks/weave-gitops/pkg/gitproviders/gitprovidersfakes"
"github.com/weaveworks/weave-gitops/pkg/logger/loggerfakes"
"github.com/weaveworks/weave-gitops/pkg/osys/osysfakes"
"github.com/weaveworks/weave-gitops/pkg/services/profile"
)

var (
gitClient *gitfakes.FakeGit
osysClient *osysfakes.FakeOsys
gitProviders *gitprovidersfakes.FakeGitProvider
log *loggerfakes.FakeLogger
profileSvc *profile.ProfileSvc
)

var _ = BeforeSuite(func() {
gitClient = &gitfakes.FakeGit{}
osysClient = &osysfakes.FakeOsys{}
log = &loggerfakes.FakeLogger{}
gitProviders = &gitprovidersfakes.FakeGitProvider{}
profileSvc = profile.NewService(context.TODO(), log, osysClient)

})

func TestProfile(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Profile Suite")
}
Loading

0 comments on commit 0ae622b

Please sign in to comment.