Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
beaver
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
orus-io
beaver
Commits
15de5366d86d
Commit
15de5366d86d
authored
2 years ago
by
Florent Aide
Browse files
Options
Downloads
Patches
Plain Diff
cleanup .orig
parent
13cab2b329ba
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Pipeline
#42654
failed
2 years ago
Changes
1
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
runner/main.go.orig
+0
-384
0 additions, 384 deletions
runner/main.go.orig
with
0 additions
and
384 deletions
runner/main.go.orig
deleted
100644 → 0
+
0
−
384
View file @
13cab2b3
package runner
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/go-cmd/cmd"
"github.com/go-yaml/yaml"
)
var (
defaultFileMod os.FileMode = 0600
defaultDirMod os.FileMode = 0700
// TODO: find commands full path
yttCmd = "ytt"
helmCmd = "helm"
kubectlCmd = "kubectl"
)
// Runner is the struct in charge of launching commands
type Runner struct {
config *CmdConfig
}
// NewRunner ...
func NewRunner(cfg *CmdConfig) *Runner {
return &Runner{
config: cfg,
}
}
// Build is in charge of applying commands based on the config data
func (r *Runner) Build(tmpDir string) error {
var outputDir string
if r.config.Output == "" {
outputDir = filepath.Join(r.config.RootDir, "build", r.config.Namespace)
} else {
outputDir = r.config.Output
}
preBuildDir := filepath.Join(tmpDir, "pre-build")
if err := r.DoBuild(tmpDir, preBuildDir); err != nil {
return fmt.Errorf("failed to do pre-build: %w", err)
}
if err := r.config.SetShas(preBuildDir); err != nil {
return fmt.Errorf("failed to set SHAs: %w", err)
}
files, err := ioutil.ReadDir(preBuildDir)
if err != nil {
return fmt.Errorf("cannot list directory: %s - %w", preBuildDir, err)
}
if err := CleanDir(outputDir); err != nil {
return fmt.Errorf("cannot clean dir: %s: %w", outputDir, err)
}
variables, err := r.config.prepareVariables(true)
if err != nil {
return fmt.Errorf("cannot prepare variables: %w", err)
}
for _, file := range files {
inFilePath := filepath.Join(preBuildDir, file.Name())
outFilePath := filepath.Join(outputDir, file.Name())
outFile, err := os.Create(outFilePath)
if err != nil {
return fmt.Errorf("cannot open: %s - %w", outFilePath, err)
}
defer func() {
if err := outFile.Close(); err != nil {
r.config.Logger.Fatal().Err(err).Msg("cannot close hydrated file")
}
}()
if err := hydrate(inFilePath, outFile, variables); err != nil {
return fmt.Errorf("cannot hydrate: %s - %w", outFilePath, err)
}
}
return nil
}
func (r *Runner) DoBuild(tmpDir, outputDir string) error {
cmds, err := r.prepareCmds()
if err != nil {
return err
}
compiled, err := r.runCommands(tmpDir, cmds)
if err != nil {
return err
}
yttOutput, err := r.runYtt(tmpDir, compiled)
if err != nil {
return err
}
kustomizeOutput, err := r.kustomize(tmpDir, yttOutput)
if err != nil {
return err
}
if r.config.DryRun {
return nil
}
if err := CleanDir(outputDir); err != nil {
return fmt.Errorf("cannot clean dir: %s: %w", outputDir, err)
}
if _, err := YamlSplit(outputDir, kustomizeOutput.Name()); err != nil {
return fmt.Errorf("cannot split full compiled file: %w", err)
}
return nil
}
func (r *Runner) kustomize(tmpDir string, input *os.File) (*os.File, error) {
kustomizeFilePath := filepath.Join(tmpDir, "kustomization.yaml")
f, err := os.Create(kustomizeFilePath)
if err != nil {
return nil, fmt.Errorf("fail to open %s: %w", kustomizeFilePath, err)
}
_, err = f.Write([]byte(fmt.Sprintf("resources: [%s]", filepath.Base(input.Name()))))
if err != nil {
return nil, fmt.Errorf("fail to write %s: %w", kustomizeFilePath, err)
}
variables, err := r.config.prepareVariables(false)
if err != nil {
return nil, fmt.Errorf("cannot prepare kustomize variables: %w", err)
}
var lastKustomizeFolder string
for _, layer := range r.config.Layers {
for _, ext := range []string{"yml", "yaml"} {
fName := fmt.Sprintf("kustomization.%s", ext)
fPath := filepath.Join(layer, "kustomize", fName)
fStat, err := os.Stat(fPath)
if err != nil || fStat.IsDir() {
continue
}
backupFile := fmt.Sprintf("%s.back", fPath)
if err := Copy(fPath, backupFile); err != nil {
return nil, fmt.Errorf("cannot copy kustomization file: %w", err)
}
defer func(fPath string) {
if err := Copy(backupFile, fPath); err != nil {
r.config.Logger.Fatal().Err(err).Msg("cannot restore kustomization back file")
}
if err := os.Remove(backupFile); err != nil {
r.config.Logger.Fatal().Err(err).Msg("cannot remove kustomization back file")
}
}(fPath)
if err := os.Remove(fPath); err != nil {
return nil, fmt.Errorf("cannot remove original kustomization file: %w", err)
}
outFile, err := os.Create(fPath)
if err != nil {
return nil, fmt.Errorf("cannot open: %s - %w", fPath, err)
}
defer func() {
if err := outFile.Close(); err != nil {
r.config.Logger.Fatal().Err(err).Msg("cannot close hydrated kustomization file")
}
}()
// kustomize root cannot be absolute
RelInputFilePath, err := filepath.Rel(filepath.Join(layer, "kustomize"), tmpDir)
if err != nil {
return nil, fmt.Errorf("cannot find relative path for: %s - %w", tmpDir, err)
}
variables["beaver.build"] = RelInputFilePath
if err := hydrate(backupFile, outFile, variables); err != nil {
return nil, fmt.Errorf("cannot hydrate: %s - %w", fPath, err)
}
lastKustomizeFolder = filepath.Join(layer, "kustomize")
}
}
// now run customize on the last layer with a kustomize folder
if lastKustomizeFolder != "" {
// now run customize on the last layer with a kustomize folder
kustomizeCmd := cmd.NewCmd(kubectlCmd, []string{"kustomize", lastKustomizeFolder}...)
return r.runCommand(tmpDir, "kustomize", kustomizeCmd)
}
return input, nil
}
func (r *Runner) prepareCmds() (map[string]*cmd.Cmd, error) {
// create helm commands
// create ytt chart commands
cmds := make(map[string]*cmd.Cmd)
for name, chart := range r.config.Spec.Charts {
args, err := chart.BuildArgs(name, r.config.Namespace)
if err != nil {
return nil, fmt.Errorf("build: failed to build args %w", err)
}
switch chart.Type {
case HelmType:
cmds[name] = cmd.NewCmd(helmCmd, args...)
case YttType:
cmds[name] = cmd.NewCmd(yttCmd, args...)
default:
return nil, fmt.Errorf("unsupported chart %s type: %q", chart.Path, chart.Type)
}
}
for key, create := range r.config.Spec.Creates {
strArgs := key.BuildArgs(r.config.Namespace, create.Args)
name := fmt.Sprintf("%s_%s", key.Type, key.Name)
c := cmd.NewCmd(kubectlCmd, strArgs...)
c.Dir = create.Dir
cmds[name] = c
}
return cmds, nil
}
func (r *Runner) runCommand(tmpDir, name string, cmd *cmd.Cmd) (*os.File, error) {
r.config.Logger.Info().
Str("command", cmd.Name).
Str("args", strings.Join(cmd.Args, " ")).
Msg("will run command")
if r.config.DryRun {
r.config.Logger.Info().
Str("command", cmd.Name).
Str("args", strings.Join(cmd.Args, " ")).
Msg("would run command")
return nil, nil
}
stdOut, stdErr, err := RunCMD(cmd)
if err != nil {
r.config.Logger.Err(err).
Str("command", cmd.Name).
Str("args", strings.Join(cmd.Args, " ")).
Str("sdtout", strings.Join(stdOut, "\n")).
Str("stderr", strings.Join(stdErr, "\n")).
Msg("failed to run command")
// Error must be pretty printed to end users /!\
fmt.Printf("\n%s\n\n", strings.Join(stdErr, "\n"))
return nil, fmt.Errorf("failed to run command: %w", err)
}
tmpFile, err := ioutil.TempFile(tmpDir, fmt.Sprintf("compiled-%s-*.yaml", name))
if err != nil {
return nil, fmt.Errorf("cannot create compiled file: %w", err)
}
defer func() {
if err := tmpFile.Close(); err != nil {
r.config.Logger.
Err(err).
Str("temp file", tmpFile.Name()).
Msg("failed to close temp file")
}
}()
if _, err := tmpFile.WriteString(strings.Join(stdOut, "\n")); err != nil {
return nil, fmt.Errorf("cannot write compiled file: %w", err)
}
return tmpFile, nil
}
func (r *Runner) runCommands(tmpDir string, cmds map[string]*cmd.Cmd) ([]string, error) {
var compiled []string
for name, cmd := range cmds {
f, err := r.runCommand(tmpDir, name, cmd)
if err != nil {
return nil, err
}
compiled = append(compiled, f.Name())
}
return compiled, nil
}
func (r *Runner) runYtt(tmpDir string, compiled []string) (*os.File, error) {
// create ytt additional command
args := r.config.BuildYttArgs(r.config.Spec.Ytt, compiled)
yttExtraCmd := cmd.NewCmd(yttCmd, args...)
return r.runCommand(tmpDir, "ytt", yttExtraCmd)
}
func CleanDir(directory string) error {
if err := os.RemoveAll(directory); err != nil {
return fmt.Errorf("cannot cleanup output directory: %w", err)
}
if err := os.MkdirAll(directory, defaultDirMod); err != nil {
return fmt.Errorf("cannot create output directory: %w", err)
}
return nil
}
func Copy(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}
// YamlSplit takes a buildDir and an inputFile.
// it returns a list of yaml documents and an eventual error
func YamlSplit(buildDir, inputFile string) ([]string, error) {
var docs []string
var allResources []map[string]interface{}
input, err := os.ReadFile(inputFile)
if err != nil {
return nil, err
}
if err := unmarshalAllResources(input, &allResources); err != nil {
return nil, err
}
for _, resource := range allResources {
apiVersion, ok := resource["apiVersion"].(string)
if !ok {
return nil, fmt.Errorf("fail to type assert apiVersion from: %+v", resource)
}
kind, ok := resource["kind"].(string)
if !ok {
return nil, fmt.Errorf("kind missing from: %+v", resource)
}
metadata, ok := resource["metadata"].(map[interface{}]interface{})
if !ok {
return nil, fmt.Errorf("fail to type assert metadata from: %+v", resource)
}
name, ok := metadata["name"].(string)
if !ok {
return nil, fmt.Errorf("fail to type assert metadata.name from: %+v", resource)
}
filename := fmt.Sprintf("%s.%s.%s.yaml", kind, strings.ReplaceAll(apiVersion, "/", "_"), name)
fPath := filepath.Join(buildDir, filename)
out, err := yaml.Marshal(resource)
if err != nil {
return nil, fmt.Errorf("cannot marshal resource: %w", err)
}
if err := os.MkdirAll(buildDir, defaultDirMod); err != nil {
return nil, fmt.Errorf("cannot create build directory: %w", err)
}
content := append([]byte("---\n"), out...)
if err := os.WriteFile(fPath, content, defaultFileMod); err != nil {
return nil, fmt.Errorf("cannot write resource: %w", err)
}
docs = append(docs, fPath)
}
return docs, nil
}
func unmarshalAllResources(in []byte, out *[]map[string]interface{}) error {
r := bytes.NewReader(in)
decoder := yaml.NewDecoder(r)
for {
res := make(map[string]interface{})
if err := decoder.Decode(&res); err != nil {
// Break when there are no more documents to decode
if !errors.Is(err, io.EOF) {
return err
}
break
}
*out = append(*out, res)
}
return nil
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment