Refactor manifest generation

Signed-off-by: Philip Laine <philip.laine@xenit.se>
pull/376/head
Philip Laine 4 years ago
parent bd4d4d927e
commit b0d2a38ff6

@ -24,7 +24,6 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -33,7 +32,6 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
@ -41,6 +39,7 @@ import (
"github.com/fluxcd/toolkit/internal/flags" "github.com/fluxcd/toolkit/internal/flags"
"github.com/fluxcd/toolkit/internal/utils" "github.com/fluxcd/toolkit/internal/utils"
"github.com/fluxcd/toolkit/pkg/install" "github.com/fluxcd/toolkit/pkg/install"
"github.com/fluxcd/toolkit/pkg/sync"
) )
var bootstrapCmd = &cobra.Command{ var bootstrapCmd = &cobra.Command{
@ -65,9 +64,6 @@ var (
const ( const (
bootstrapDefaultBranch = "main" bootstrapDefaultBranch = "main"
bootstrapInstallManifest = "toolkit-components.yaml"
bootstrapSourceManifest = "toolkit-source.yaml"
bootstrapKustomizationManifest = "toolkit-kustomization.yaml"
) )
func init() { func init() {
@ -103,13 +99,6 @@ func bootstrapValidate() error {
} }
func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) { func generateInstallManifests(targetPath, namespace, tmpDir string, localManifests string) (string, error) {
manifestsDir := path.Join(tmpDir, targetPath, namespace)
if err := os.MkdirAll(manifestsDir, os.ModePerm); err != nil {
return "", fmt.Errorf("creating manifests dir failed: %w", err)
}
manifest := path.Join(manifestsDir, bootstrapInstallManifest)
opts := install.Options{ opts := install.Options{
BaseURL: localManifests, BaseURL: localManifests,
Version: bootstrapVersion, Version: bootstrapVersion,
@ -124,22 +113,28 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes
NotificationController: defaultNotification, NotificationController: defaultNotification,
ManifestsFile: fmt.Sprintf("%s.yaml", namespace), ManifestsFile: fmt.Sprintf("%s.yaml", namespace),
Timeout: timeout, Timeout: timeout,
TargetPath: targetPath,
} }
if localManifests == "" { if localManifests == "" {
opts.BaseURL = install.MakeDefaultOptions().BaseURL opts.BaseURL = install.MakeDefaultOptions().BaseURL
} }
output, err := install.Generate(opts) manifestPath, content, err := install.Generate(opts)
if err != nil { if err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err) return "", fmt.Errorf("generating install manifests failed: %w", err)
} }
if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil { filePath := path.Join(tmpDir, manifestPath)
if err := os.MkdirAll(path.Dir(manifestPath), os.ModePerm); err != nil {
return "", fmt.Errorf("creating manifest dir failed: %w", err)
}
if err := ioutil.WriteFile(filePath, []byte(content), os.ModePerm); err != nil {
return "", fmt.Errorf("generating install manifests failed: %w", err) return "", fmt.Errorf("generating install manifests failed: %w", err)
} }
return manifest, nil return filePath, nil
} }
func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error { func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error {
@ -158,70 +153,24 @@ func applyInstallManifests(ctx context.Context, manifestPath string, components
} }
func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error { func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir string, interval time.Duration) error {
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind) opts := sync.Options{
gitRepository := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
},
Spec: sourcev1.GitRepositorySpec{
URL: url, URL: url,
Interval: metav1.Duration{
Duration: interval,
},
Reference: &sourcev1.GitRepositoryRef{
Branch: branch, Branch: branch,
}, Interval: interval,
SecretRef: &corev1.LocalObjectReference{ TargetPath: targetPath,
Name: name,
},
},
} }
gitData, err := yaml.Marshal(gitRepository) output, err := sync.Generate(opts)
if err != nil { if err != nil {
return err return fmt.Errorf("generating install manifests failed: %w", err)
} }
if err := utils.WriteFile(string(gitData), filepath.Join(tmpDir, targetPath, namespace, bootstrapSourceManifest)); err != nil { for _, v := range output {
if err := utils.WriteFile(v["content"], filepath.Join(tmpDir, v["file_path"])); err != nil {
return err return err
} }
gvk = kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)
kustomization := kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{
Duration: 10 * time.Minute,
},
Path: fmt.Sprintf("./%s", strings.TrimPrefix(targetPath, "./")),
Prune: true,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourcev1.GitRepositoryKind,
Name: name,
},
Validation: "client",
},
}
ksData, err := yaml.Marshal(kustomization)
if err != nil {
return err
}
if err := utils.WriteFile(string(ksData), filepath.Join(tmpDir, targetPath, namespace, bootstrapKustomizationManifest)); err != nil {
return err
} }
if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil { if err := utils.GenerateKustomizationYaml(filepath.Join(tmpDir, targetPath, namespace)); err != nil {

@ -123,24 +123,23 @@ func installCmdRun(cmd *cobra.Command, args []string) error {
opts.BaseURL = install.MakeDefaultOptions().BaseURL opts.BaseURL = install.MakeDefaultOptions().BaseURL
} }
output, err := install.Generate(opts) _, content, err := install.Generate(opts)
if err != nil { if err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace)) manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace))
if err := ioutil.WriteFile(manifest, output, os.ModePerm); err != nil { if err := ioutil.WriteFile(manifest, []byte(content), os.ModePerm); err != nil {
return fmt.Errorf("install failed: %w", err) return fmt.Errorf("install failed: %w", err)
} }
yaml := string(output)
if verbose { if verbose {
fmt.Print(yaml) fmt.Print(content)
} else if installExport { } else if installExport {
fmt.Println("---") fmt.Println("---")
fmt.Println("# GitOps Toolkit revision", installVersion) fmt.Println("# GitOps Toolkit revision", installVersion)
fmt.Println("# Components:", strings.Join(installComponents, ",")) fmt.Println("# Components:", strings.Join(installComponents, ","))
fmt.Print(yaml) fmt.Print(content)
fmt.Println("---") fmt.Println("---")
return nil return nil
} }

@ -28,13 +28,13 @@ import (
// Generate returns the install manifests as a multi-doc YAML. // Generate returns the install manifests as a multi-doc YAML.
// The manifests are built from a GitHub release or from a // The manifests are built from a GitHub release or from a
// Kustomize overlay if the supplied Options.BaseURL is a local path. // Kustomize overlay if the supplied Options.BaseURL is a local path.
func Generate(options Options) ([]byte, error) { func Generate(options Options) (string, string, error) {
ctx, cancel := context.WithTimeout(context.Background(), options.Timeout) ctx, cancel := context.WithTimeout(context.Background(), options.Timeout)
defer cancel() defer cancel()
tmpDir, err := ioutil.TempDir("", options.Namespace) tmpDir, err := ioutil.TempDir("", options.Namespace)
if err != nil { if err != nil {
return nil, fmt.Errorf("temp dir error: %w", err) return "", "", fmt.Errorf("temp dir error: %w", err)
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
@ -42,26 +42,26 @@ func Generate(options Options) ([]byte, error) {
if !strings.HasPrefix(options.BaseURL, "http") { if !strings.HasPrefix(options.BaseURL, "http") {
if err := build(options.BaseURL, output); err != nil { if err := build(options.BaseURL, output); err != nil {
return nil, err return "", "", err
} }
} else { } else {
if err := fetch(ctx, options.BaseURL, options.Version, tmpDir); err != nil { if err := fetch(ctx, options.BaseURL, options.Version, tmpDir); err != nil {
return nil, err return "", "", err
} }
if err := generate(tmpDir, options); err != nil { if err := generate(tmpDir, options); err != nil {
return nil, err return "", "", err
} }
if err := build(tmpDir, output); err != nil { if err := build(tmpDir, output); err != nil {
return nil, err return "", "", err
} }
} }
content, err := ioutil.ReadFile(output) content, err := ioutil.ReadFile(output)
if err != nil { if err != nil {
return nil, err return "", "", err
} }
return content, nil return path.Join(options.TargetPath, options.Namespace, options.ManifestsFile), string(content), nil
} }

@ -24,7 +24,7 @@ import (
func TestGenerate(t *testing.T) { func TestGenerate(t *testing.T) {
opts := MakeDefaultOptions() opts := MakeDefaultOptions()
output, err := Generate(opts) _, output, err := Generate(opts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -33,6 +33,7 @@ type Options struct {
NotificationController string NotificationController string
ManifestsFile string ManifestsFile string
Timeout time.Duration Timeout time.Duration
TargetPath string
} }
func MakeDefaultOptions() Options { func MakeDefaultOptions() Options {
@ -51,6 +52,7 @@ func MakeDefaultOptions() Options {
NotificationController: "notification-controller", NotificationController: "notification-controller",
ManifestsFile: "toolkit-components.yaml", ManifestsFile: "toolkit-components.yaml",
Timeout: time.Minute, Timeout: time.Minute,
TargetPath: "",
} }
} }

@ -0,0 +1,23 @@
package sync
import "time"
type Options struct {
Interval time.Duration
URL string
Name string
Namespace string
Branch string
TargetPath string
}
func MakeDefaultOptions() Options {
return Options{
Interval: 1 * time.Minute,
URL: "",
Name: "gotk-system",
Namespace: "gotk-system",
Branch: "main",
TargetPath: "",
}
}

@ -0,0 +1,88 @@
package sync
import (
"fmt"
"path/filepath"
"strings"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
)
const (
bootstrapSourceManifest = "toolkit-source.yaml"
bootstrapKustomizationManifest = "toolkit-kustomization.yaml"
)
func Generate(options Options) ([]map[string]string, error) {
files := []map[string]string{}
gvk := sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)
gitRepository := sourcev1.GitRepository{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: options.Name,
Namespace: options.Namespace,
},
Spec: sourcev1.GitRepositorySpec{
URL: options.URL,
Interval: metav1.Duration{
Duration: options.Interval,
},
Reference: &sourcev1.GitRepositoryRef{
Branch: options.Branch,
},
SecretRef: &corev1.LocalObjectReference{
Name: options.Name,
},
},
}
gitData, err := yaml.Marshal(gitRepository)
if err != nil {
return nil, err
}
files = append(files, map[string]string{"file_path": filepath.Join(options.TargetPath, options.Namespace, bootstrapSourceManifest), "content": string(gitData)})
gvk = kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)
kustomization := kustomizev1.Kustomization{
TypeMeta: metav1.TypeMeta{
Kind: gvk.Kind,
APIVersion: gvk.GroupVersion().String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: options.Name,
Namespace: options.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{
Duration: 10 * time.Minute,
},
Path: fmt.Sprintf("./%s", strings.TrimPrefix(options.TargetPath, "./")),
Prune: true,
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Kind: sourcev1.GitRepositoryKind,
Name: options.Name,
},
Validation: "client",
},
}
ksData, err := yaml.Marshal(kustomization)
if err != nil {
return nil, err
}
files = append(files, map[string]string{"file_path": filepath.Join(options.TargetPath, options.Namespace, bootstrapKustomizationManifest), "content": string(ksData)})
return files, nil
}

@ -0,0 +1,32 @@
/*
Copyright 2020 The Flux CD contributors.
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 sync
import (
"fmt"
"testing"
)
func TestGenerate(t *testing.T) {
opts := MakeDefaultOptions()
output, err := Generate(opts)
if err != nil {
t.Fatal(err)
}
fmt.Println(output)
}
Loading…
Cancel
Save