Skip to content

Commit

Permalink
This adds support for using an HTTPS URL when running install.
Browse files Browse the repository at this point in the history
$ gitops install --config-repo https://github.com/org/repo.git --flux-https-password=$GITHUB_TOKEN will pass through the provided password to flux.

https://fluxcd.io/docs/installation/#generic-git-server
  • Loading branch information
bigkevmcd authored and jpellizzari committed Jan 21, 2022
1 parent 1fd0468 commit 5713073
Show file tree
Hide file tree
Showing 14 changed files with 207 additions and 109 deletions.
32 changes: 14 additions & 18 deletions cmd/gitops/install/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,13 @@ import (
"github.com/weaveworks/weave-gitops/pkg/services/applier"
"github.com/weaveworks/weave-gitops/pkg/services/auth"
"github.com/weaveworks/weave-gitops/pkg/services/automation"
"github.com/weaveworks/weave-gitops/pkg/services/gitops"
"github.com/weaveworks/weave-gitops/pkg/services/gitopswriter"
"github.com/weaveworks/weave-gitops/pkg/services/gitrepo"
)

type params struct {
DryRun bool
AutoMerge bool
ConfigRepo string
}

var (
installParams params
gitopsParams gitops.InstallParams
)

var Cmd = &cobra.Command{
Expand All @@ -58,9 +53,10 @@ repo. If a previous version is installed, then an in-place upgrade will be perfo
const LabelPartOf = "app.kubernetes.io/part-of"

func init() {
Cmd.Flags().BoolVar(&installParams.DryRun, "dry-run", false, "Outputs all the manifests that would be installed")
Cmd.Flags().BoolVar(&installParams.AutoMerge, "auto-merge", false, "If set, 'gitops install' will automatically update the default branch for the configuration repository")
Cmd.Flags().StringVar(&installParams.ConfigRepo, "config-repo", "", "URL of external repository that will hold automation manifests")
Cmd.Flags().BoolVar(&gitopsParams.DryRun, "dry-run", false, "Outputs all the manifests that would be installed")
Cmd.Flags().StringVar(&gitopsParams.ConfigRepo, "config-repo", "", "URL of external repository that will hold automation manifests")
Cmd.Flags().StringVar(&gitopsParams.FluxHTTPSUsername, "flux-https-username", "git", "Optional: only needed if using an https:// repo URL for flux")
Cmd.Flags().StringVar(&gitopsParams.FluxHTTPSPassword, "flux-https-password", "", "Optional: only needed if using an https:// repo URL for flux")
cobra.CheckErr(Cmd.MarkFlagRequired("config-repo"))
}

Expand All @@ -75,7 +71,7 @@ func installRunCmd(cmd *cobra.Command, args []string) error {

osysClient := osys.New()
log := internal.NewCLILogger(os.Stdout)
flux := flux.New(osysClient, &runner.CLIRunner{})
runner := flux.New(osysClient, &runner.CLIRunner{})

k, _, err := kube.NewKubeHTTPClient()
if err != nil {
Expand All @@ -99,7 +95,6 @@ func installRunCmd(cmd *cobra.Command, args []string) error {
clusterApplier := applier.NewClusterApplier(k)

var gitClient git.Git

var gitProvider gitproviders.GitProvider

factory := services.NewFactory(flux, log)
Expand All @@ -123,10 +118,12 @@ func installRunCmd(cmd *cobra.Command, args []string) error {

gitClient, gitProvider, err = factory.GetGitClients(context.Background(), providerClient, services.GitConfigParams{
// We need to set URL and ConfigRepo to the same value so a deploy key is created for public config repos
URL: installParams.ConfigRepo,
ConfigRepo: installParams.ConfigRepo,
Namespace: namespace,
DryRun: installParams.DryRun,
URL: installParams.ConfigRepo,
ConfigRepo: installParams.ConfigRepo,
Namespace: namespace,
DryRun: installParams.DryRun,
FluxHTTPSUsername: gitopsParams.FluxHTTPSUsername,
FluxHTTPSPassword: gitopsParams.FluxHTTPSPassword,
})

if err != nil {
Expand All @@ -151,7 +148,7 @@ func installRunCmd(cmd *cobra.Command, args []string) error {

manifests := append(clusterAutomation.Manifests(), wegoConfigManifest)

if installParams.DryRun {
if gitopsParams.DryRun {
for _, manifest := range manifests {
log.Println(string(manifest.Content))
}
Expand All @@ -168,6 +165,5 @@ func installRunCmd(cmd *cobra.Command, args []string) error {
if err != nil {
return fmt.Errorf("failed associating cluster: %w", err)
}

return nil
}
36 changes: 29 additions & 7 deletions pkg/flux/flux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ type Flux interface {
GetExePath() (string, error)
Install(namespace string, export bool) ([]byte, error)
Uninstall(namespace string, export bool) error
CreateSourceGit(name string, repoUrl gitproviders.RepoURL, branch string, secretRef string, namespace string) ([]byte, error)
CreateSourceHelm(name string, url string, namespace string) ([]byte, error)
CreateSourceGit(name string, repoUrl gitproviders.RepoURL, branch, secretRef, namespace string, creds *HTTPSCreds) ([]byte, error)
CreateSourceHelm(name, url, namespace string) ([]byte, error)
CreateKustomization(name string, source string, path string, namespace string) ([]byte, error)
CreateHelmReleaseGitRepository(name, source, path, namespace, targetNamespace string) ([]byte, error)
CreateHelmReleaseHelmRepository(name, chart, namespace, targetNamespace string) ([]byte, error)
CreateSecretGit(name string, repoUrl gitproviders.RepoURL, namespace string) ([]byte, error)
CreateSecretGit(name string, repoUrl gitproviders.RepoURL, namespace string, creds *HTTPSCreds) ([]byte, error)
GetVersion() (string, error)
GetAllResourcesStatus(name string, namespace string) ([]byte, error)
SuspendOrResumeApp(pause wego.SuspendActionType, name, namespace, deploymentType string) ([]byte, error)
Expand All @@ -42,6 +42,13 @@ const (

const fluxBinaryPathEnvVar = "WEAVE_GITOPS_FLUX_BIN_PATH"

// HTTPSCreds is an optional username/password use to authenticate flux
// source operations for https based git servers.
type HTTPSCreds struct {
Username string
Password string
}

type FluxClient struct {
osys osys.Osys
runner runner.Runner
Expand Down Expand Up @@ -98,7 +105,7 @@ func (f *FluxClient) Uninstall(namespace string, dryRun bool) error {
return nil
}

func (f *FluxClient) CreateSourceGit(name string, repoUrl gitproviders.RepoURL, branch string, secretRef string, namespace string) ([]byte, error) {
func (f *FluxClient) CreateSourceGit(name string, repoUrl gitproviders.RepoURL, branch, secretRef, namespace string, creds *HTTPSCreds) ([]byte, error) {
args := []string{
"create", "source", "git", name,
"--branch", branch,
Expand All @@ -113,6 +120,7 @@ func (f *FluxClient) CreateSourceGit(name string, repoUrl gitproviders.RepoURL,
args = append(args, "--url", makePublicUrl(repoUrl))
}

args = append(args, argsFromCreds(creds)...)
out, err := f.runFluxCmd(args...)
if err != nil {
return out, fmt.Errorf("failed to create source git: %w", err)
Expand All @@ -121,6 +129,19 @@ func (f *FluxClient) CreateSourceGit(name string, repoUrl gitproviders.RepoURL,
return out, nil
}

func argsFromCreds(creds *HTTPSCreds) []string {
args := []string{}
if creds != nil {
if creds.Username != "" {
args = append(args, "--username", creds.Username)
}
if creds.Password != "" {
args = append(args, "--password", creds.Password)
}
}
return args
}

func makePublicUrl(repoUrl gitproviders.RepoURL) string {
trimmed := ""

Expand Down Expand Up @@ -227,14 +248,15 @@ func (f *FluxClient) CreateHelmReleaseHelmRepository(name, chart, namespace, tar
return out, nil
}

// CreatSecretGit Creates a Git secret returns the deploy key
func (f *FluxClient) CreateSecretGit(name string, repoUrl gitproviders.RepoURL, namespace string) ([]byte, error) {
// CreateSecretGit Creates a Git secret returns the deploy key.
func (f *FluxClient) CreateSecretGit(name string, repoUrl gitproviders.RepoURL, namespace string, creds *HTTPSCreds) ([]byte, error) {
args := []string{
"create", "secret", "git", name,
"--url", repoUrl.String(),
"--namespace", namespace,
"--export",
}
args = append(args, argsFromCreds(creds)...)

out, err := f.runFluxCmd(args...)
if err != nil {
Expand Down Expand Up @@ -276,7 +298,7 @@ func (f *FluxClient) runFluxCmd(args ...string) ([]byte, error) {

out, err := f.runner.Run(fluxPath, args...)
if err != nil {
return []byte{}, fmt.Errorf("failed to run flux with output: %s and error: %w", string(out), err)
return []byte{}, fmt.Errorf("failed to run flux %v with output: %s and error: %w", args, out, err)
}

return out, nil
Expand Down
50 changes: 45 additions & 5 deletions pkg/flux/flux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,24 @@ var _ = Describe("CreateSourceGit", func() {

repoUrl, err := gitproviders.NewRepoURL("https://github.com/foo/my-name", false)
Expect(err).ShouldNot(HaveOccurred())
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "my-secret", wego.DefaultNamespace)
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "my-secret", wego.DefaultNamespace, nil)
Expect(err).ShouldNot(HaveOccurred())
Expect(out).To(Equal([]byte("out")))

Expect(runner.RunCallCount()).To(Equal(1))

cmd, args := runner.RunArgsForCall(0)
Expect(cmd).To(Equal(fluxPath()))
Expect(strings.Join(args, " ")).To(Equal(fmt.Sprintf("create source git my-name --branch main --namespace %s --interval 30s --export --secret-ref my-secret --url ssh://git@github.com/foo/my-name.git", wego.DefaultNamespace)))
Expect(strings.Join(args, " ")).To(Equal(fmt.Sprintf("create source git my-name --branch main --namespace %s --interval 30s --export --secret-ref my-secret --url https://github.com/foo/my-name.git", wego.DefaultNamespace)))
})

It("creates a git source for a public repo", func() {
runner.RunStub = func(s1 string, s2 ...string) ([]byte, error) {
return []byte("out"), nil
}
repoUrl, err := gitproviders.NewRepoURL("ssh://git@github.com/foo/my-name", false)
Expect(err).ShouldNot(HaveOccurred())
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "", wego.DefaultNamespace)
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "", wego.DefaultNamespace, nil)
Expect(err).ShouldNot(HaveOccurred())
Expect(out).To(Equal([]byte("out")))

Expand All @@ -121,7 +122,7 @@ var _ = Describe("CreateSourceGit", func() {

repoUrl, err := gitproviders.NewRepoURL("ssh://git@gitlab.com/foo/my-name", false)
Expect(err).ShouldNot(HaveOccurred())
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "", wego.DefaultNamespace)
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "", wego.DefaultNamespace, nil)
Expect(err).ShouldNot(HaveOccurred())
Expect(out).To(Equal([]byte("out")))

Expand All @@ -132,6 +133,25 @@ var _ = Describe("CreateSourceGit", func() {

Expect(strings.Join(args, " ")).To(Equal(fmt.Sprintf("create source git my-name --branch main --namespace %s --interval 30s --export --url https://gitlab.com/foo/my-name.git", wego.DefaultNamespace)))
})

It("passes http credentials to flux", func() {
runner.RunStub = func(s1 string, s2 ...string) ([]byte, error) {
return []byte("out"), nil
}

repoUrl, err := gitproviders.NewRepoURL("https://gitlab.com/foo/my-name")
Expect(err).ShouldNot(HaveOccurred())
out, err := fluxClient.CreateSourceGit("my-name", repoUrl, "main", "", wego.DefaultNamespace, &flux.HTTPSCreds{Username: "test", Password: "password"})
Expect(err).ShouldNot(HaveOccurred())
Expect(out).To(Equal([]byte("out")))

Expect(runner.RunCallCount()).To(Equal(1))

cmd, args := runner.RunArgsForCall(0)
Expect(cmd).To(Equal(fluxPath()))

Expect(strings.Join(args, " ")).To(Equal(fmt.Sprintf("create source git my-name --branch main --namespace %s --interval 30s --export --url https://gitlab.com/foo/my-name.git --username test --password password", wego.DefaultNamespace)))
})
})

var _ = Describe("CreateSourceHelm", func() {
Expand Down Expand Up @@ -246,7 +266,7 @@ var _ = Describe("CreateSecretGit", func() {

repoUrl, err := gitproviders.NewRepoURL("ssh://git@github.com/foo/bar.git", false)
Expect(err).ShouldNot(HaveOccurred())
out, err := fluxClient.CreateSecretGit("my-secret", repoUrl, wego.DefaultNamespace)
out, err := fluxClient.CreateSecretGit("my-secret", repoUrl, wego.DefaultNamespace, nil)
Expect(err).ShouldNot(HaveOccurred())
Expect(out).To(Equal([]byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCh...")))

Expand All @@ -257,6 +277,26 @@ var _ = Describe("CreateSecretGit", func() {

Expect(strings.Join(args, " ")).To(Equal(fmt.Sprintf("create secret git my-secret --url ssh://git@github.com/foo/bar.git --namespace %s --export", wego.DefaultNamespace)))
})

It("passes http credentials through to flux", func() {
runner.RunStub = func(s1 string, s2 ...string) ([]byte, error) {
return []byte(`ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCh...`), nil
}

repoUrl, err := gitproviders.NewRepoURL("ssh://git@github.com/foo/bar.git")
Expect(err).ShouldNot(HaveOccurred())
out, err := fluxClient.CreateSecretGit("my-secret", repoUrl, wego.DefaultNamespace, &flux.HTTPSCreds{Username: "test", Password: "password"})
Expect(err).ShouldNot(HaveOccurred())
Expect(out).To(Equal([]byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCh...")))

Expect(runner.RunCallCount()).To(Equal(1))

cmd, args := runner.RunArgsForCall(0)
Expect(cmd).To(Equal(fluxPath()))

Expect(strings.Join(args, " ")).To(Equal(fmt.Sprintf("create secret git my-secret --url ssh://git@github.com/foo/bar.git --namespace %s --export --username test --password password", wego.DefaultNamespace)))
})

})

func fluxPath() string {
Expand Down
4 changes: 3 additions & 1 deletion pkg/gitproviders/repo_url.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ func normalizeRepoURLString(url string) (string, error) {
// https://github.com/weaveworks/weave-gitops/issues/878
// A trailing slash causes problems when naming secrets.
url = strings.TrimSuffix(url, "/")

if !strings.HasSuffix(url, ".git") {
url = url + ".git"
}
Expand All @@ -160,6 +159,9 @@ func normalizeRepoURLString(url string) (string, error) {
if err != nil {
return "", fmt.Errorf("could not parse git repo url while normalizing %q: %w", url, err)
}
if u.Scheme == "https" {
return u.String(), nil
}

return fmt.Sprintf("ssh://git@%s%s", u.Host, u.Path), nil
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/gitproviders/repo_url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ var _ = DescribeTable("NewRepoURL", func(input, gitProviderEnv string, expected
protocol: RepositoryURLProtocolSSH,
}),
Entry("github https", "https://github.com/someuser/podinfo.git", "", expectedRepoURL{
s: "ssh://git@github.com/someuser/podinfo.git",
s: "https://github.com/someuser/podinfo.git",
owner: "someuser",
name: "podinfo",
provider: GitProviderGitHub,
protocol: RepositoryURLProtocolSSH,
protocol: RepositoryURLProtocolHTTPS,
}),
Entry("gitlab git clone style", "git@gitlab.com:someuser/podinfo.git", "", expectedRepoURL{
s: "ssh://git@gitlab.com/someuser/podinfo.git",
Expand All @@ -111,18 +111,18 @@ var _ = DescribeTable("NewRepoURL", func(input, gitProviderEnv string, expected
protocol: RepositoryURLProtocolSSH,
}),
Entry("gitlab https", "https://gitlab.com/someuser/podinfo.git", "", expectedRepoURL{
s: "ssh://git@gitlab.com/someuser/podinfo.git",
s: "https://gitlab.com/someuser/podinfo.git",
owner: "someuser",
name: "podinfo",
provider: GitProviderGitLab,
protocol: RepositoryURLProtocolSSH,
protocol: RepositoryURLProtocolHTTPS,
}),
Entry("trailing slash in url", "https://github.com/sympatheticmoose/podinfo-deploy/", "", expectedRepoURL{
s: "ssh://git@github.com/sympatheticmoose/podinfo-deploy.git",
s: "https://github.com/sympatheticmoose/podinfo-deploy.git",
owner: "sympatheticmoose",
name: "podinfo-deploy",
provider: GitProviderGitHub,
protocol: RepositoryURLProtocolSSH,
protocol: RepositoryURLProtocolHTTPS,
}),
Entry(
"custom domain",
Expand Down
2 changes: 1 addition & 1 deletion pkg/services/app/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func (a *AppSvc) updateParametersIfNecessary(ctx context.Context, gitProvider gi
}

if params.Name == "" {
repoName := utils.UrlToRepoName(params.Url)
repoName := utils.URLToRepoName(params.Url)
if automation.ApplicationNameTooLong(repoName) {
return params, fmt.Errorf("url base name %q is too long to use as application name (must be <= %d characters); please specify name with '--name'",
repoName, automation.MaxKubernetesResourceNameLength)
Expand Down
Loading

0 comments on commit 5713073

Please sign in to comment.