From a4ef1f6992801e52a4f24bb0df53758c67a90941 Mon Sep 17 00:00:00 2001
From: Dipti Pai <diptipai89@outlook.com>
Date: Mon, 16 Sep 2024 12:08:14 -0700
Subject: [PATCH] Flux CLI change to add provider field to GitRepository spec.

- Add provider flag to `flux create source git` command with supported values: azure, generic.

- Unit tests validating the generated yaml and error conditions.

Signed-off-by: Dipti Pai <diptipai89@outlook.com>
---
 cmd/flux/create_source_git.go                 | 14 ++++-
 cmd/flux/create_source_git_test.go            | 25 ++++++++
 .../source-git-provider-azure.yaml            | 12 ++++
 .../source-git-provider-generic.yaml          | 12 ++++
 internal/flags/source_git_provider.go         | 57 +++++++++++++++++++
 5 files changed, 119 insertions(+), 1 deletion(-)
 create mode 100644 cmd/flux/testdata/create_source_git/source-git-provider-azure.yaml
 create mode 100644 cmd/flux/testdata/create_source_git/source-git-provider-generic.yaml
 create mode 100644 internal/flags/source_git_provider.go

diff --git a/cmd/flux/create_source_git.go b/cmd/flux/create_source_git.go
index 67b03dabec..0d31386bbb 100644
--- a/cmd/flux/create_source_git.go
+++ b/cmd/flux/create_source_git.go
@@ -56,6 +56,7 @@ type sourceGitFlags struct {
 	keyRSABits        flags.RSAKeyBits
 	keyECDSACurve     flags.ECDSACurve
 	secretRef         string
+	provider          flags.SourceGitProvider
 	caFile            string
 	privateKeyFile    string
 	recurseSubmodules bool
@@ -119,7 +120,13 @@ For private Git repositories, the basic authentication credentials are stored in
     --url=https://github.com/stefanprodan/podinfo \
     --branch=master \
     --username=username \
-    --password=password`,
+    --password=password
+
+  # Create a source for a Git repository using azure provider
+  flux create source git podinfo \
+    --url=https://dev.azure.com/foo/bar/_git/podinfo \
+    --branch=master \
+    --provider=azure`,
 	RunE: createSourceGitCmdRun,
 }
 
@@ -138,6 +145,7 @@ func init() {
 	createSourceGitCmd.Flags().Var(&sourceGitArgs.keyRSABits, "ssh-rsa-bits", sourceGitArgs.keyRSABits.Description())
 	createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
 	createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
+	createSourceGitCmd.Flags().Var(&sourceGitArgs.provider, "provider", sourceGitArgs.provider.Description())
 	createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
 	createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
 	createSourceGitCmd.Flags().BoolVar(&sourceGitArgs.recurseSubmodules, "recurse-submodules", false,
@@ -236,6 +244,10 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
 		}
 	}
 
+	if provider := sourceGitArgs.provider.String(); provider != "" {
+		gitRepository.Spec.Provider = provider
+	}
+
 	if createArgs.export {
 		return printExport(exportGit(&gitRepository))
 	}
diff --git a/cmd/flux/create_source_git_test.go b/cmd/flux/create_source_git_test.go
index b34e420893..d2ece63651 100644
--- a/cmd/flux/create_source_git_test.go
+++ b/cmd/flux/create_source_git_test.go
@@ -134,6 +134,31 @@ func TestCreateSourceGitExport(t *testing.T) {
 			args:   "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --branch=test --interval=1m0s --export",
 			assert: assertGoldenFile("testdata/create_source_git/source-git-branch.yaml"),
 		},
+		{
+			name:   "source with generic provider",
+			args:   "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --provider generic --branch=test --interval=1m0s --export",
+			assert: assertGoldenFile("testdata/create_source_git/source-git-provider-generic.yaml"),
+		},
+		{
+			name:   "source with azure provider",
+			args:   "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --provider azure --branch=test --interval=1m0s --export",
+			assert: assertGoldenFile("testdata/create_source_git/source-git-provider-azure.yaml"),
+		},
+		{
+			name:   "source with invalid provider",
+			args:   "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --provider dummy --branch=test --interval=1m0s --export",
+			assert: assertError("invalid argument \"dummy\" for \"--provider\" flag: source Git provider 'dummy' is not supported, must be one of: generic|azure"),
+		},
+		{
+			name:   "source with empty provider",
+			args:   "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --provider \"\" --branch=test --interval=1m0s --export",
+			assert: assertError("invalid argument \"\" for \"--provider\" flag: no source Git provider given, please specify the Git provider name"),
+		},
+		{
+			name:   "source with no provider",
+			args:   "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --branch=test --interval=1m0s --export --provider",
+			assert: assertError("flag needs an argument: --provider"),
+		},
 	}
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
diff --git a/cmd/flux/testdata/create_source_git/source-git-provider-azure.yaml b/cmd/flux/testdata/create_source_git/source-git-provider-azure.yaml
new file mode 100644
index 0000000000..e8af0cb177
--- /dev/null
+++ b/cmd/flux/testdata/create_source_git/source-git-provider-azure.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: source.toolkit.fluxcd.io/v1
+kind: GitRepository
+metadata:
+  name: podinfo
+  namespace: flux-system
+spec:
+  interval: 1m0s
+  provider: azure
+  ref:
+    branch: test
+  url: https://dev.azure.com/foo/bar/_git/podinfo
diff --git a/cmd/flux/testdata/create_source_git/source-git-provider-generic.yaml b/cmd/flux/testdata/create_source_git/source-git-provider-generic.yaml
new file mode 100644
index 0000000000..7cafc26ebb
--- /dev/null
+++ b/cmd/flux/testdata/create_source_git/source-git-provider-generic.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: source.toolkit.fluxcd.io/v1
+kind: GitRepository
+metadata:
+  name: podinfo
+  namespace: flux-system
+spec:
+  interval: 1m0s
+  provider: generic
+  ref:
+    branch: test
+  url: https://github.com/stefanprodan/podinfo
diff --git a/internal/flags/source_git_provider.go b/internal/flags/source_git_provider.go
new file mode 100644
index 0000000000..63cec33e91
--- /dev/null
+++ b/internal/flags/source_git_provider.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2024 The Flux authors
+
+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 flags
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/fluxcd/flux2/v2/internal/utils"
+	sourcev1 "github.com/fluxcd/source-controller/api/v1"
+)
+
+var supportedSourceGitProviders = []string{
+	sourcev1.GitProviderGeneric,
+	sourcev1.GitProviderAzure,
+}
+
+type SourceGitProvider string
+
+func (p *SourceGitProvider) String() string {
+	return string(*p)
+}
+
+func (p *SourceGitProvider) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no source Git provider given, please specify %s",
+			p.Description())
+	}
+	if !utils.ContainsItemString(supportedSourceGitProviders, str) {
+		return fmt.Errorf("source Git provider '%s' is not supported, must be one of: %v",
+			str, p.Type())
+	}
+	*p = SourceGitProvider(str)
+	return nil
+}
+
+func (p *SourceGitProvider) Type() string {
+	return strings.Join(supportedSourceGitProviders, "|")
+}
+
+func (p *SourceGitProvider) Description() string {
+	return "the Git provider name"
+}