Skip to content

Commit

Permalink
feat: install flux components API (#1656)
Browse files Browse the repository at this point in the history
  • Loading branch information
Azhovan authored Oct 9, 2023
1 parent 3f298ef commit 0876d02
Show file tree
Hide file tree
Showing 5 changed files with 598 additions and 32 deletions.
200 changes: 200 additions & 0 deletions apptests/flux/apply.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package flux

import (
"bufio"
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"sigs.k8s.io/cli-utils/pkg/kstatus/polling"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/kustomize/api/konfig"

"github.com/fluxcd/flux2/v2/pkg/manifestgen/kustomization"
runclient "github.com/fluxcd/pkg/runtime/client"
"github.com/fluxcd/pkg/ssa"

helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
imageautov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
imagereflectv1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
notificationv1b2 "github.com/fluxcd/notification-controller/api/v1beta2"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"

appsv1 "k8s.io/api/apps/v1"
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiruntime "k8s.io/apimachinery/pkg/runtime"
)

// Apply is the equivalent of 'kubectl apply --server-side -f'.
// If the given manifest is a kustomization.yaml, then apply performs the equivalent of 'kubectl apply --server-side -k'.
func Apply(ctx context.Context, rcg genericclioptions.RESTClientGetter, opts *runclient.Options, root, manifestPath string) (string, error) {
objs, err := readObjects(root, manifestPath)
if err != nil {
return "", err
}

if len(objs) == 0 {
return "", fmt.Errorf("no Kubernetes objects found at: %s", manifestPath)
}

if err := ssa.SetNativeKindsDefaults(objs); err != nil {
return "", err
}

changeSet := ssa.NewChangeSet()

// contains only CRDs and Namespaces
var stageOne []*unstructured.Unstructured

// contains all objects except for CRDs and Namespaces
var stageTwo []*unstructured.Unstructured

for _, u := range objs {
if ssa.IsClusterDefinition(u) {
stageOne = append(stageOne, u)
} else {
stageTwo = append(stageTwo, u)
}
}

if len(stageOne) > 0 {
cs, err := applySet(ctx, rcg, opts, stageOne)
if err != nil {
return "", err
}
changeSet.Append(cs.Entries)
}

if err := waitForSet(rcg, opts, changeSet); err != nil {
return "", err
}

if len(stageTwo) > 0 {
cs, err := applySet(ctx, rcg, opts, stageTwo)
if err != nil {
return "", err
}
changeSet.Append(cs.Entries)
}

return changeSet.String(), nil
}

func readObjects(root, manifestPath string) ([]*unstructured.Unstructured, error) {
fi, err := os.Lstat(manifestPath)
if err != nil {
return nil, err
}
if fi.IsDir() || !fi.Mode().IsRegular() {
return nil, fmt.Errorf("expected %q to be a file", manifestPath)
}

if isRecognizedKustomizationFile(manifestPath) {
resources, err := kustomization.BuildWithRoot(root, filepath.Dir(manifestPath))
if err != nil {
return nil, err
}
return ssa.ReadObjects(bytes.NewReader(resources))
}

ms, err := os.Open(manifestPath)
if err != nil {
return nil, err
}
defer ms.Close()

return ssa.ReadObjects(bufio.NewReader(ms))
}

func newManager(rcg genericclioptions.RESTClientGetter, opts *runclient.Options) (*ssa.ResourceManager, error) {
cfg, err := KubeConfig(rcg, opts)
if err != nil {
return nil, err
}
restMapper, err := rcg.ToRESTMapper()
if err != nil {
return nil, err
}
kubeClient, err := client.New(cfg, client.Options{Mapper: restMapper, Scheme: NewScheme()})
if err != nil {
return nil, err
}
kubePoller := polling.NewStatusPoller(kubeClient, restMapper, polling.Options{})

return ssa.NewResourceManager(kubeClient, kubePoller, ssa.Owner{
Field: "flux",
Group: "fluxcd.io",
}), nil
}

// Create the Scheme, methods for serializing and deserializing API objects
// which can be shared by tests.
func NewScheme() *apiruntime.Scheme {
scheme := apiruntime.NewScheme()
_ = apiextensionsv1.AddToScheme(scheme)
_ = corev1.AddToScheme(scheme)
_ = rbacv1.AddToScheme(scheme)
_ = appsv1.AddToScheme(scheme)
_ = networkingv1.AddToScheme(scheme)
_ = sourcev1b2.AddToScheme(scheme)
_ = sourcev1.AddToScheme(scheme)
_ = kustomizev1.AddToScheme(scheme)
_ = helmv2.AddToScheme(scheme)
_ = notificationv1.AddToScheme(scheme)
_ = notificationv1b2.AddToScheme(scheme)
_ = imagereflectv1.AddToScheme(scheme)
_ = imageautov1.AddToScheme(scheme)
return scheme
}

func KubeConfig(rcg genericclioptions.RESTClientGetter, opts *runclient.Options) (*rest.Config, error) {
cfg, err := rcg.ToRESTConfig()
if err != nil {
return nil, fmt.Errorf("kubernetes configuration load failed: %w", err)
}

// avoid throttling request when some Flux CRDs are not registered
cfg.QPS = opts.QPS
cfg.Burst = opts.Burst

return cfg, nil
}

func applySet(ctx context.Context, rcg genericclioptions.RESTClientGetter, opts *runclient.Options, objects []*unstructured.Unstructured) (*ssa.ChangeSet, error) {
man, err := newManager(rcg, opts)
if err != nil {
return nil, err
}

return man.ApplyAll(ctx, objects, ssa.DefaultApplyOptions())
}

func waitForSet(rcg genericclioptions.RESTClientGetter, opts *runclient.Options, changeSet *ssa.ChangeSet) error {
man, err := newManager(rcg, opts)
if err != nil {
return err
}
return man.WaitForSet(changeSet.ToObjMetadataSet(), ssa.WaitOptions{Interval: 2 * time.Second, Timeout: time.Minute})
}

func isRecognizedKustomizationFile(path string) bool {
base := filepath.Base(path)
for _, v := range konfig.RecognizedKustomizationFileNames() {
if base == v {
return true
}
}
return false
}
61 changes: 61 additions & 0 deletions apptests/flux/flux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package flux

import (
"context"
"fmt"
"os"
"path/filepath"

"github.com/fluxcd/flux2/v2/pkg/manifestgen"
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
runclient "github.com/fluxcd/pkg/runtime/client"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

type Options struct {
KubeconfigArgs *genericclioptions.ConfigFlags
KubeclientOptions *runclient.Options
Namespace string
Components []string
}

// Install installs flux components in the given namespace on the cluster using the given kubeconfig and client options.
func Install(ctx context.Context, opts Options) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}

// make default options for installing flux components
options := install.MakeDefaultOptions()
options.Namespace = opts.Namespace
options.Components = opts.Components

// generate flux manifest
manifest, err := install.Generate(options, "")
if err != nil {
return err
}

// create a temporary directory for the manifest
tmpDir, err := manifestgen.MkdirTempAbs("", opts.Namespace)
if err != nil {
return err
}
defer os.RemoveAll(tmpDir)

// write the manifest to the temporary directory
if _, err := manifest.WriteFile(tmpDir); err != nil {
return fmt.Errorf("install failed: %w", err)
}

_, err = Apply(
ctx,
opts.KubeconfigArgs,
opts.KubeclientOptions,
tmpDir,
filepath.Join(tmpDir, manifest.Path))

return nil
}
74 changes: 65 additions & 9 deletions apptests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,111 @@ module github.com/mesosphere/kommander-applications/apptests

go 1.20

require k8s.io/client-go v0.27.4
require (
github.com/fluxcd/flux2/v2 v2.1.1
github.com/fluxcd/helm-controller/api v0.36.1
github.com/fluxcd/image-automation-controller/api v0.36.1
github.com/fluxcd/image-reflector-controller/api v0.30.0
github.com/fluxcd/kustomize-controller/api v1.1.0
github.com/fluxcd/notification-controller/api v1.1.0
github.com/fluxcd/pkg/runtime v0.42.0
github.com/fluxcd/pkg/ssa v0.32.1
github.com/fluxcd/source-controller/api v1.1.1
k8s.io/apiextensions-apiserver v0.28.2
k8s.io/cli-runtime v0.28.2
k8s.io/client-go v0.28.2
sigs.k8s.io/cli-utils v0.35.0
sigs.k8s.io/controller-runtime v0.16.2
sigs.k8s.io/kustomize/api v0.14.0
)

require (
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/onsi/gomega v1.27.10 // indirect
golang.org/x/tools v0.13.0 // indirect
k8s.io/api v0.27.4 // indirect
k8s.io/apimachinery v0.27.4 // indirect
k8s.io/api v0.28.2
k8s.io/apimachinery v0.28.2
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fluxcd/pkg/apis/acl v0.1.0 // indirect
github.com/fluxcd/pkg/apis/kustomize v1.1.1 // indirect
github.com/fluxcd/pkg/apis/meta v1.1.2 // indirect
github.com/fluxcd/pkg/kustomize v1.3.4 // indirect
github.com/fluxcd/pkg/tar v0.2.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/term v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/component-base v0.28.2 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/kubectl v0.27.4 // indirect
k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
Loading

0 comments on commit 0876d02

Please sign in to comment.