1
0
mirror of synced 2026-02-06 19:05:55 +00:00

refactor bootstrap process to use fluxcd/pkg/git

Signed-off-by: Sanskar Jaiswal <jaiswalsanskar078@gmail.com>
This commit is contained in:
Sanskar Jaiswal
2022-11-08 21:13:43 +05:30
parent a06652a374
commit 0a5048a56b
15 changed files with 233 additions and 607 deletions

View File

@@ -19,6 +19,7 @@ package bootstrap
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
@@ -40,21 +41,21 @@ import (
runclient "github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/flux2/pkg/bootstrap/git"
"github.com/fluxcd/flux2/pkg/log"
"github.com/fluxcd/flux2/pkg/manifestgen/install"
"github.com/fluxcd/flux2/pkg/manifestgen/kustomization"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
"github.com/fluxcd/flux2/pkg/status"
"github.com/fluxcd/pkg/git"
"github.com/fluxcd/pkg/git/repository"
)
type PlainGitBootstrapper struct {
url string
branch string
caBundle []byte
url string
branch string
author git.Author
signature git.Signature
commitMessageAppendix string
gpgKeyRing openpgp.EntityList
@@ -66,9 +67,9 @@ type PlainGitBootstrapper struct {
postGenerateSecret []PostGenerateSecretFunc
git git.Git
kube client.Client
logger log.Logger
gitClient repository.Client
kube client.Client
logger log.Logger
}
type GitOption interface {
@@ -95,10 +96,10 @@ func (o postGenerateSecret) applyGit(b *PlainGitBootstrapper) {
b.postGenerateSecret = append(b.postGenerateSecret, PostGenerateSecretFunc(o))
}
func NewPlainGitProvider(git git.Git, kube client.Client, opts ...GitOption) (*PlainGitBootstrapper, error) {
func NewPlainGitProvider(git repository.Client, kube client.Client, opts ...GitOption) (*PlainGitBootstrapper, error) {
b := &PlainGitBootstrapper{
git: git,
kube: kube,
gitClient: git,
kube: kube,
}
for _, opt := range opts {
opt.applyGit(b)
@@ -108,7 +109,7 @@ func NewPlainGitProvider(git git.Git, kube client.Client, opts ...GitOption) (*P
func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifestsBase string, options install.Options, _ sourcesecret.Options) error {
// Clone if not already
if _, err := b.git.Status(); err != nil {
if _, err := b.gitClient.Head(); err != nil {
if err != git.ErrNoGitRepository {
return err
}
@@ -116,7 +117,17 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
b.logger.Actionf("cloning branch %q from Git repository %q", b.branch, b.url)
var cloned bool
if err = retry(1, 2*time.Second, func() (err error) {
cloned, err = b.git.Clone(ctx, b.url, b.branch, b.caBundle)
_, err = b.gitClient.Clone(ctx, b.url, repository.CloneOptions{
CheckoutStrategy: repository.CheckoutStrategy{
Branch: b.branch,
},
})
if err != nil {
b.logger.Warningf(" clone failure: %s", err)
}
if err == nil {
cloned = true
}
return
}); err != nil {
return fmt.Errorf("failed to clone repository: %w", err)
@@ -134,28 +145,33 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
}
b.logger.Successf("generated component manifests")
// Write manifest to Git repository
if err = b.git.Write(manifests.Path, strings.NewReader(manifests.Content)); err != nil {
return fmt.Errorf("failed to write manifest %q: %w", manifests.Path, err)
// Write generated files and make a commit
var signer *openpgp.Entity
if b.gpgKeyRing != nil {
signer, err = getOpenPgpEntity(b.gpgKeyRing, b.gpgPassphrase, b.gpgKeyID)
if err != nil {
return fmt.Errorf("failed to generate OpenPGP entity: %w", err)
}
}
// Git commit generated
gpgOpts := git.WithGpgSigningOption(b.gpgKeyRing, b.gpgPassphrase, b.gpgKeyID)
commitMsg := fmt.Sprintf("Add Flux %s component manifests", options.Version)
if b.commitMessageAppendix != "" {
commitMsg = commitMsg + "\n\n" + b.commitMessageAppendix
}
commit, err := b.git.Commit(git.Commit{
Author: b.author,
commit, err := b.gitClient.Commit(git.Commit{
Author: b.signature,
Message: commitMsg,
}, gpgOpts)
}, repository.WithFiles(map[string]io.Reader{
manifests.Path: strings.NewReader(manifests.Content),
}), repository.WithSigner(signer))
if err != nil && err != git.ErrNoStagedFiles {
return fmt.Errorf("failed to commit sync manifests: %w", err)
}
if err == nil {
b.logger.Successf("committed sync manifests to %q (%q)", b.branch, commit)
b.logger.Actionf("pushing component manifests to %q", b.url)
if err = b.git.Push(ctx, b.caBundle); err != nil {
if err = b.gitClient.Push(ctx); err != nil {
return fmt.Errorf("failed to push manifests: %w", err)
}
} else {
@@ -166,16 +182,16 @@ func (b *PlainGitBootstrapper) ReconcileComponents(ctx context.Context, manifest
if mustInstallManifests(ctx, b.kube, options.Namespace) {
b.logger.Actionf("installing components in %q namespace", options.Namespace)
componentsYAML := filepath.Join(b.git.Path(), manifests.Path)
componentsYAML := filepath.Join(b.gitClient.Path(), manifests.Path)
kfile := filepath.Join(filepath.Dir(componentsYAML), konfig.DefaultKustomizationFileName())
if _, err := os.Stat(kfile); err == nil {
// Apply the components and their patches
if _, err := utils.Apply(ctx, b.restClientGetter, b.restClientOptions, b.git.Path(), kfile); err != nil {
if _, err := utils.Apply(ctx, b.restClientGetter, b.restClientOptions, b.gitClient.Path(), kfile); err != nil {
return err
}
} else {
// Apply the CRDs and controllers
if _, err := utils.Apply(ctx, b.restClientGetter, b.restClientOptions, b.git.Path(), componentsYAML); err != nil {
if _, err := utils.Apply(ctx, b.restClientGetter, b.restClientOptions, b.gitClient.Path(), componentsYAML); err != nil {
return err
}
}
@@ -237,12 +253,19 @@ func (b *PlainGitBootstrapper) ReconcileSyncConfig(ctx context.Context, options
}
// Clone if not already
if _, err := b.git.Status(); err != nil {
if _, err := b.gitClient.Head(); err != nil {
if err == git.ErrNoGitRepository {
b.logger.Actionf("cloning branch %q from Git repository %q", b.branch, b.url)
var cloned bool
if err = retry(1, 2*time.Second, func() (err error) {
cloned, err = b.git.Clone(ctx, b.url, b.branch, b.caBundle)
_, err = b.gitClient.Clone(ctx, b.url, repository.CloneOptions{
CheckoutStrategy: repository.CheckoutStrategy{
Branch: b.branch,
},
})
if err == nil {
cloned = true
}
return
}); err != nil {
return fmt.Errorf("failed to clone repository: %w", err)
@@ -260,41 +283,47 @@ func (b *PlainGitBootstrapper) ReconcileSyncConfig(ctx context.Context, options
if err != nil {
return fmt.Errorf("sync manifests generation failed: %w", err)
}
if err = b.git.Write(manifests.Path, strings.NewReader(manifests.Content)); err != nil {
return fmt.Errorf("failed to write manifest %q: %w", manifests.Path, err)
}
// Create secure Kustomize FS
fs, err := filesys.MakeFsOnDiskSecureBuild(b.git.Path())
fs, err := filesys.MakeFsOnDiskSecureBuild(b.gitClient.Path())
if err != nil {
return fmt.Errorf("failed to initialize Kustomize file system: %w", err)
}
if err = fs.WriteFile(filepath.Join(b.gitClient.Path(), manifests.Path), []byte(manifests.Content)); err != nil {
return err
}
// Generate Kustomization
kusManifests, err := kustomization.Generate(kustomization.Options{
FileSystem: fs,
BaseDir: b.git.Path(),
BaseDir: b.gitClient.Path(),
TargetPath: filepath.Dir(manifests.Path),
})
if err != nil {
return fmt.Errorf("%s generation failed: %w", konfig.DefaultKustomizationFileName(), err)
}
if err = b.git.Write(kusManifests.Path, strings.NewReader(kusManifests.Content)); err != nil {
return fmt.Errorf("failed to write manifest %q: %w", kusManifests.Path, err)
}
b.logger.Successf("generated sync manifests")
// Git commit generated
gpgOpts := git.WithGpgSigningOption(b.gpgKeyRing, b.gpgPassphrase, b.gpgKeyID)
// Write generated files and make a commit
var signer *openpgp.Entity
if b.gpgKeyRing != nil {
signer, err = getOpenPgpEntity(b.gpgKeyRing, b.gpgPassphrase, b.gpgKeyID)
if err != nil {
return fmt.Errorf("failed to generate OpenPGP entity: %w", err)
}
}
commitMsg := fmt.Sprintf("Add Flux sync manifests")
if b.commitMessageAppendix != "" {
commitMsg = commitMsg + "\n\n" + b.commitMessageAppendix
}
commit, err := b.git.Commit(git.Commit{
Author: b.author,
Message: commitMsg,
}, gpgOpts)
commit, err := b.gitClient.Commit(git.Commit{
Author: b.signature,
Message: commitMsg,
}, repository.WithFiles(map[string]io.Reader{
kusManifests.Path: strings.NewReader(kusManifests.Content),
}), repository.WithSigner(signer))
if err != nil && err != git.ErrNoStagedFiles {
return fmt.Errorf("failed to commit sync manifests: %w", err)
}
@@ -302,18 +331,22 @@ func (b *PlainGitBootstrapper) ReconcileSyncConfig(ctx context.Context, options
if err == nil {
b.logger.Successf("committed sync manifests to %q (%q)", b.branch, commit)
b.logger.Actionf("pushing sync manifests to %q", b.url)
err = b.git.Push(ctx, b.caBundle)
err = b.gitClient.Push(ctx)
if err != nil {
if strings.HasPrefix(err.Error(), gogit.ErrNonFastForwardUpdate.Error()) {
b.logger.Waitingf("git conflict detected, retrying with a fresh clone")
if err := os.RemoveAll(b.git.Path()); err != nil {
if err := os.RemoveAll(b.gitClient.Path()); err != nil {
return fmt.Errorf("failed to remove tmp dir: %w", err)
}
if err := os.Mkdir(b.git.Path(), 0o700); err != nil {
if err := os.Mkdir(b.gitClient.Path(), 0o700); err != nil {
return fmt.Errorf("failed to recreate tmp dir: %w", err)
}
if err = retry(1, 2*time.Second, func() (err error) {
_, err = b.git.Clone(ctx, b.url, b.branch, b.caBundle)
_, err = b.gitClient.Clone(ctx, b.url, repository.CloneOptions{
CheckoutStrategy: repository.CheckoutStrategy{
Branch: b.branch,
},
})
return
}); err != nil {
return fmt.Errorf("failed to clone repository: %w", err)
@@ -328,7 +361,7 @@ func (b *PlainGitBootstrapper) ReconcileSyncConfig(ctx context.Context, options
// Apply to cluster
b.logger.Actionf("applying sync manifests")
if _, err := utils.Apply(ctx, b.restClientGetter, b.restClientOptions, b.git.Path(), filepath.Join(b.git.Path(), kusManifests.Path)); err != nil {
if _, err := utils.Apply(ctx, b.restClientGetter, b.restClientOptions, b.gitClient.Path(), filepath.Join(b.gitClient.Path(), kusManifests.Path)); err != nil {
return err
}
@@ -338,7 +371,7 @@ func (b *PlainGitBootstrapper) ReconcileSyncConfig(ctx context.Context, options
}
func (b *PlainGitBootstrapper) ReportKustomizationHealth(ctx context.Context, options sync.Options, pollInterval, timeout time.Duration) error {
head, err := b.git.Head()
head, err := b.gitClient.Head()
if err != nil {
return err
}
@@ -390,3 +423,31 @@ func (b *PlainGitBootstrapper) ReportComponentsHealth(ctx context.Context, insta
b.logger.Successf("all components are healthy")
return nil
}
func getOpenPgpEntity(keyRing openpgp.EntityList, passphrase, keyID string) (*openpgp.Entity, error) {
if len(keyRing) == 0 {
return nil, fmt.Errorf("empty GPG key ring")
}
var entity *openpgp.Entity
if keyID != "" {
for _, ent := range keyRing {
if ent.PrimaryKey.KeyIdString() == keyID {
entity = ent
}
}
if entity == nil {
return nil, fmt.Errorf("no GPG private key matching key id '%s' found", keyID)
}
} else {
entity = keyRing[0]
}
err := entity.PrivateKey.Decrypt([]byte(passphrase))
if err != nil {
return nil, fmt.Errorf("unable to decrypt GPG private key: %w", err)
}
return entity, nil
}

View File

@@ -29,10 +29,10 @@ import (
"github.com/fluxcd/go-git-providers/gitprovider"
"github.com/fluxcd/flux2/pkg/bootstrap/git"
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
"github.com/fluxcd/pkg/git/repository"
)
type GitProviderBootstrapper struct {
@@ -62,11 +62,12 @@ type GitProviderBootstrapper struct {
provider gitprovider.Client
}
func NewGitProviderBootstrapper(git git.Git, provider gitprovider.Client, kube client.Client, opts ...GitProviderOption) (*GitProviderBootstrapper, error) {
func NewGitProviderBootstrapper(git repository.Client, provider gitprovider.Client,
kube client.Client, opts ...GitProviderOption) (*GitProviderBootstrapper, error) {
b := &GitProviderBootstrapper{
PlainGitBootstrapper: &PlainGitBootstrapper{
git: git,
kube: kube,
gitClient: git,
kube: kube,
},
bootstrapTransportType: "https",
syncTransportType: "ssh",

View File

@@ -1,46 +0,0 @@
package git
import (
"github.com/ProtonMail/go-crypto/openpgp"
)
// Option is a some configuration that modifies options for a commit.
type Option interface {
// ApplyToCommit applies this configuration to a given commit option.
ApplyToCommit(*CommitOptions)
}
// CommitOptions contains options for making a commit.
type CommitOptions struct {
*GPGSigningInfo
}
// GPGSigningInfo contains information for signing a commit.
type GPGSigningInfo struct {
KeyRing openpgp.EntityList
Passphrase string
KeyID string
}
type GpgSigningOption struct {
*GPGSigningInfo
}
func (w GpgSigningOption) ApplyToCommit(in *CommitOptions) {
in.GPGSigningInfo = w.GPGSigningInfo
}
func WithGpgSigningOption(keyRing openpgp.EntityList, passphrase, keyID string) Option {
// Return nil if no path is set, even if other options are configured.
if len(keyRing) == 0 {
return GpgSigningOption{}
}
return GpgSigningOption{
GPGSigningInfo: &GPGSigningInfo{
KeyRing: keyRing,
Passphrase: passphrase,
KeyID: keyID,
},
}
}

View File

@@ -1,52 +0,0 @@
/*
Copyright 2021 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 git
import (
"context"
"errors"
"io"
)
var (
ErrNoGitRepository = errors.New("no git repository")
ErrNoStagedFiles = errors.New("no staged files")
)
type Author struct {
Name string
Email string
}
type Commit struct {
Author
Hash string
Message string
}
// Git is an interface for basic Git operations on a single branch of a
// remote repository.
type Git interface {
Init(url, branch string) (bool, error)
Clone(ctx context.Context, url, branch string, caBundle []byte) (bool, error)
Write(path string, reader io.Reader) error
Commit(message Commit, options ...Option) (string, error)
Push(ctx context.Context, caBundle []byte) error
Status() (bool, error)
Head() (string, error)
Path() string
}

View File

@@ -1,286 +0,0 @@
/*
Copyright 2021 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 gogit
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/ProtonMail/go-crypto/openpgp"
gogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/fluxcd/flux2/pkg/bootstrap/git"
)
type GoGit struct {
path string
auth transport.AuthMethod
repository *gogit.Repository
}
type CommitOptions struct {
GpgKeyPath string
GpgKeyPassphrase string
KeyID string
}
func New(path string, auth transport.AuthMethod) *GoGit {
return &GoGit{
path: path,
auth: auth,
}
}
func (g *GoGit) Init(url, branch string) (bool, error) {
if g.repository != nil {
return false, nil
}
r, err := gogit.PlainInit(g.path, false)
if err != nil {
return false, err
}
if _, err = r.CreateRemote(&config.RemoteConfig{
Name: gogit.DefaultRemoteName,
URLs: []string{url},
}); err != nil {
return false, err
}
branchRef := plumbing.NewBranchReferenceName(branch)
if err = r.CreateBranch(&config.Branch{
Name: branch,
Remote: gogit.DefaultRemoteName,
Merge: branchRef,
}); err != nil {
return false, err
}
// PlainInit assumes the initial branch to always be master, we can
// overwrite this by setting the reference of the Storer to a new
// symbolic reference (as there are no commits yet) that points
// the HEAD to our new branch.
if err = r.Storer.SetReference(plumbing.NewSymbolicReference(plumbing.HEAD, branchRef)); err != nil {
return false, err
}
g.repository = r
return true, nil
}
func (g *GoGit) Clone(ctx context.Context, url, branch string, caBundle []byte) (bool, error) {
branchRef := plumbing.NewBranchReferenceName(branch)
r, err := gogit.PlainCloneContext(ctx, g.path, false, &gogit.CloneOptions{
URL: url,
Auth: g.auth,
RemoteName: gogit.DefaultRemoteName,
ReferenceName: branchRef,
SingleBranch: true,
NoCheckout: false,
Progress: nil,
Tags: gogit.NoTags,
CABundle: caBundle,
})
if err != nil {
if err == transport.ErrEmptyRemoteRepository || isRemoteBranchNotFoundErr(err, branchRef.String()) {
return g.Init(url, branch)
}
return false, err
}
g.repository = r
return true, nil
}
func (g *GoGit) Write(path string, reader io.Reader) error {
if g.repository == nil {
return git.ErrNoGitRepository
}
wt, err := g.repository.Worktree()
if err != nil {
return err
}
f, err := wt.Filesystem.Create(path)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, reader)
return err
}
func (g *GoGit) Commit(message git.Commit, opts ...git.Option) (string, error) {
if g.repository == nil {
return "", git.ErrNoGitRepository
}
wt, err := g.repository.Worktree()
if err != nil {
return "", err
}
status, err := wt.Status()
if err != nil {
return "", err
}
// apply the options
options := &git.CommitOptions{}
for _, opt := range opts {
opt.ApplyToCommit(options)
}
// go-git has [a bug](https://github.com/go-git/go-git/issues/253)
// whereby it thinks broken symlinks to absolute paths are
// modified. There's no circumstance in which we want to commit a
// change to a broken symlink: so, detect and skip those.
var changed bool
for file, _ := range status {
abspath := filepath.Join(g.path, file)
info, err := os.Lstat(abspath)
if err != nil {
return "", fmt.Errorf("checking if %s is a symlink: %w", file, err)
}
if info.Mode()&os.ModeSymlink > 0 {
// symlinks are OK; broken symlinks are probably a result
// of the bug mentioned above, but not of interest in any
// case.
if _, err := os.Stat(abspath); os.IsNotExist(err) {
continue
}
}
_, _ = wt.Add(file)
changed = true
}
if !changed {
head, err := g.repository.Head()
if err != nil {
return "", err
}
return head.Hash().String(), git.ErrNoStagedFiles
}
commitOpts := &gogit.CommitOptions{
Author: &object.Signature{
Name: message.Name,
Email: message.Email,
When: time.Now(),
},
}
if options.GPGSigningInfo != nil {
entity, err := getOpenPgpEntity(*options.GPGSigningInfo)
if err != nil {
return "", err
}
commitOpts.SignKey = entity
}
commit, err := wt.Commit(message.Message, commitOpts)
if err != nil {
return "", err
}
return commit.String(), nil
}
func (g *GoGit) Push(ctx context.Context, caBundle []byte) error {
if g.repository == nil {
return git.ErrNoGitRepository
}
return g.repository.PushContext(ctx, &gogit.PushOptions{
RemoteName: gogit.DefaultRemoteName,
Auth: g.auth,
Progress: nil,
CABundle: caBundle,
})
}
func (g *GoGit) Status() (bool, error) {
if g.repository == nil {
return false, git.ErrNoGitRepository
}
wt, err := g.repository.Worktree()
if err != nil {
return false, err
}
status, err := wt.Status()
if err != nil {
return false, err
}
return status.IsClean(), nil
}
func (g *GoGit) Head() (string, error) {
if g.repository == nil {
return "", git.ErrNoGitRepository
}
head, err := g.repository.Head()
if err != nil {
return "", err
}
return head.Hash().String(), nil
}
func (g *GoGit) Path() string {
return g.path
}
func isRemoteBranchNotFoundErr(err error, ref string) bool {
return strings.Contains(err.Error(), fmt.Sprintf("couldn't find remote ref %q", ref))
}
func getOpenPgpEntity(info git.GPGSigningInfo) (*openpgp.Entity, error) {
if len(info.KeyRing) == 0 {
return nil, fmt.Errorf("empty GPG key ring")
}
var entity *openpgp.Entity
if info.KeyID != "" {
for _, ent := range info.KeyRing {
if ent.PrimaryKey.KeyIdString() == info.KeyID {
entity = ent
}
}
if entity == nil {
return nil, fmt.Errorf("no GPG private key matching key id '%s' found", info.KeyID)
}
} else {
entity = info.KeyRing[0]
}
err := entity.PrivateKey.Decrypt([]byte(info.Passphrase))
if err != nil {
return nil, fmt.Errorf("unable to decrypt GPG private key: %w", err)
}
return entity, nil
}

View File

@@ -1,80 +0,0 @@
//go:build unit
// +build unit
package gogit
import (
"os"
"testing"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/fluxcd/flux2/pkg/bootstrap/git"
)
func TestGetOpenPgpEntity(t *testing.T) {
tests := []struct {
name string
keyPath string
passphrase string
id string
expectErr bool
}{
{
name: "no default key id given",
keyPath: "testdata/private.key",
passphrase: "flux",
id: "",
expectErr: false,
},
{
name: "key id given",
keyPath: "testdata/private.key",
passphrase: "flux",
id: "0619327DBD777415",
expectErr: false,
},
{
name: "wrong key id",
keyPath: "testdata/private.key",
passphrase: "flux",
id: "0619327DBD777416",
expectErr: true,
},
{
name: "wrong password",
keyPath: "testdata/private.key",
passphrase: "fluxe",
id: "0619327DBD777415",
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var entityList openpgp.EntityList
if tt.keyPath != "" {
r, err := os.Open(tt.keyPath)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
entityList, err = openpgp.ReadKeyRing(r)
if err != nil {
t.Errorf("unexpected error: %s", err)
}
}
gpgInfo := git.GPGSigningInfo{
KeyRing: entityList,
Passphrase: tt.passphrase,
KeyID: tt.id,
}
_, err := getOpenPgpEntity(gpgInfo)
if err != nil && !tt.expectErr {
t.Errorf("unexpected error: %s", err)
}
if err == nil && tt.expectErr {
t.Errorf("expected error when %s", tt.name)
}
})
}
}

Binary file not shown.

View File

@@ -23,9 +23,9 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/fluxcd/pkg/git"
runclient "github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/flux2/pkg/bootstrap/git"
"github.com/fluxcd/flux2/pkg/log"
)
@@ -48,42 +48,28 @@ func (o branchOption) applyGitProvider(b *GitProviderBootstrapper) {
o.applyGit(b.PlainGitBootstrapper)
}
func WithAuthor(name, email string) Option {
return authorOption{
func WithSignature(name, email string) Option {
return signatureOption{
Name: name,
Email: email,
}
}
type authorOption git.Author
type signatureOption git.Signature
func (o authorOption) applyGit(b *PlainGitBootstrapper) {
func (o signatureOption) applyGit(b *PlainGitBootstrapper) {
if o.Name != "" {
b.author.Name = o.Name
b.signature.Name = o.Name
}
if o.Email != "" {
b.author.Email = o.Email
b.signature.Email = o.Email
}
}
func (o authorOption) applyGitProvider(b *GitProviderBootstrapper) {
func (o signatureOption) applyGitProvider(b *GitProviderBootstrapper) {
o.applyGit(b.PlainGitBootstrapper)
}
func WithCABundle(b []byte) Option {
return caBundleOption(b)
}
type caBundleOption []byte
func (o caBundleOption) applyGit(b *PlainGitBootstrapper) {
b.caBundle = o
}
func (o caBundleOption) applyGitProvider(b *GitProviderBootstrapper) {
b.caBundle = o
}
func WithCommitMessageAppendix(appendix string) Option {
return commitMessageAppendixOption(appendix)
}

View File

@@ -76,7 +76,7 @@ func Generate(options Options) (*manifestgen.Manifest, error) {
var hostKey []byte
if keypair != nil {
if hostKey, err = scanHostKey(options.SSHHostname); err != nil {
if hostKey, err = ScanHostKey(options.SSHHostname); err != nil {
return nil, err
}
}
@@ -194,7 +194,7 @@ func generateKeyPair(options Options) (*ssh.KeyPair, error) {
return pair, nil
}
func scanHostKey(host string) ([]byte, error) {
func ScanHostKey(host string) ([]byte, error) {
if _, _, err := net.SplitHostPort(host); err != nil {
// Assume we are dealing with a hostname without a port,
// append the default SSH port as this is required for