<|start_filename|>pkg/build/qemu.go<|end_filename|>
package build

import (
	"fmt"
	"io/ioutil"
	"os"
	"time"

	"github.com/mitchellh/colorstring"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/flanksource/commons/files"
	"github.com/flanksource/konfigadm/pkg/types"
	"github.com/flanksource/konfigadm/pkg/utils"
)

type Qemu struct{}

func (q Qemu) Build(image string, config *types.Config) {
	var scratch Scratch
	if config.Context.CaptureLogs != "" {
		log.Infof("Using scratch directory / disk")
		scratch = NewScratch()
	}

	iso, err := createIso(config)
	if err != nil {
		log.Fatalf("Failed to build ISO %v", err)
	}
	if iso == "" {
		log.Fatalf("Empty ISO created")
	}
	cmdLine := qemuSystem(image, iso)
	if config.Context.CaptureLogs != "" {
		cmdLine += fmt.Sprintf(" -hdb %s", scratch.GetImg())
	}

	log.Infof("Executing %s\n", colorstring.Color("[light_green]"+cmdLine))
	if err := utils.Exec(cmdLine); err != nil {
		log.Fatalf("Failed to run: %s, %s", cmdLine, err)
	}
	if config.Context.CaptureLogs != "" {
		log.Infof("Coping captured logs to %s\n", config.Context.CaptureLogs)
		if err := scratch.UnwrapToDir(config.Context.CaptureLogs); err != nil {
			log.Fatalf("Failed to Unwrap: %s", err)
		}
	}
}

func (q Qemu) Test(image string, config *types.Config, privateKeyFile string, template string) error {
	var scratch Scratch
	if config.Context.CaptureLogs != "" {
		log.Infof("Using scratch directory / disk")
		scratch = NewScratch()
	}

	iso, err := createTestIso(config)
	if err != nil {
		return errors.Wrap(err, "failed to build ISO")
	}
	if iso == "" {
		return errors.New("Empty ISO created")
	}

	tempfile, err := ioutil.TempFile("", "test-image")
	if err != nil {
		return errors.Wrap(err, "failed to create temporary file for test image")
	}
	if err := files.Copy(image, tempfile.Name()); err != nil {
		return errors.Wrapf(err, "failed to copy %s to %s: %v", image, tempfile.Name(), err)
	}

	defer os.Remove(tempfile.Name())

	cmdLine := qemuSystem(tempfile.Name(), iso)
	if config.Context.CaptureLogs != "" {
		cmdLine += fmt.Sprintf(" -hdb %s", scratch.GetImg())
	}

	go func() {
		log.Infof("Executing %s\n", colorstring.Color("[light_green]"+cmdLine))
		if err := utils.ExecNoOutput(cmdLine); err != nil {
			log.Errorf("failed to run %s: %v", cmdLine, err)
			return
		}
		if config.Context.CaptureLogs != "" {
			log.Infof("Coping captured logs to %s\n", config.Context.CaptureLogs)
			if err := scratch.UnwrapToDir(config.Context.CaptureLogs); err != nil {
				log.Errorf("Failed to Unwrap to dir: %s", err)
			}
		}
	}()

	// Wait for SSH to be available
	timeout := time.Now().Add(5 * time.Minute)
	sshHost := "127.0.0.1:2022"
	sshUser := "root"
	fmt.Println("Waiting for ssh")
	for {
		if _, err := utils.RunSSHCommand(sshHost, sshUser, privateKeyFile, "true"); err != nil {
			log.Infof("failed to connect to ssh %s@%s: %v, retrying in 5 seconds", sshUser, sshHost, err)
			time.Sleep(5 * time.Second)
		} else {
			log.Infof("connection to %s@%s succeeded", sshUser, sshHost)
			break
		}
		if time.Now().After(timeout) {
			return errors.Errorf("failed to connect to ssh %s@%s after 5 minutes", sshUser, sshHost)
		}
	}

	defer func() {
		if _, err := utils.RunSSHCommand(sshHost, sshUser, privateKeyFile, "shutdown -h -t 5"); err != nil {
			log.Fatalf("failed to shutdown qemu VM: %v", err)
		}
	}()

	scriptTemplate := `
wget https://github.com/aelsabbahy/goss/releases/download/v0.3.13/goss-linux-amd64 -O /usr/bin/goss
chmod +x /usr/bin/goss
cat <<EOF > goss.yaml
%s
EOF
`
	script := fmt.Sprintf(scriptTemplate, template)

	output, err := utils.RunSSHScript(sshHost, sshUser, privateKeyFile, script)
	if output != nil {
		log.Infof("Output:\n%s", output)
	}
	if err != nil {
		return errors.Wrap(err, "failed to run script")
	}

	output, err = utils.RunSSHCommand(sshHost, sshUser, privateKeyFile, "goss validate")
	if output != nil {
		log.Infof("Output:\n%s", output)
	}
	if err != nil {
		return errors.Wrap(err, "failed to run goss validate")
	}

	return nil
}

func qemuSystem(disk, iso string) string {
	return fmt.Sprintf(`qemu-system-x86_64 \
		-nodefaults \
		-display none \
		-machine accel=kvm:hvf \
		-cpu host -smp cpus=2 \
		-m 1024 \
		-hda %s \
		-cdrom %s \
		-device virtio-serial-pci \
		-serial stdio \
		-net nic -net user,hostfwd=tcp:127.0.0.1:2022-:22`, disk, iso)
}


<|start_filename|>pkg/phases/files.go<|end_filename|>
package phases

import (
	"github.com/flanksource/konfigadm/pkg/types"
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"os"
)

var Files types.Phase = filesPhase{}

type filesPhase struct{}

func (p filesPhase) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	var commands []types.Command
	files := make(map[string]types.File)
	for k, v := range sys.Filesystem {
		files[k] = v
	}

	for k, v := range sys.Files {
		files[k] = types.File{Content: Lookup(v)}
	}

	return commands, files, nil
}

func (p filesPhase) Verify(cfg *types.Config, results *types.VerifyResults, flags ...types.Flag) bool {
	verify := true
	for f := range cfg.Files {

		if _, err := os.Stat(f); err != nil {
			verify = false
			results.Fail("%s does not exist", f)
		} else {
			results.Pass("%s exists", f)
		}
	}

	for f := range cfg.Templates {
		if _, err := os.Stat(f); err != nil {
			verify = false
			results.Fail("%s does not exist", f)
		} else {
			results.Pass("%s exists", f)
		}
	}

	return verify
}

func Lookup(path string) string {
	_, err := os.Stat(path)
	if err != nil {
		return path
	}
	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Printf("Error reading: %s : %s", path, err)
		return path
	}
	return string(data)
}


<|start_filename|>pkg/phases/containers.go<|end_filename|>
package phases

import (
	"fmt"
	"os"

	"github.com/flanksource/konfigadm/pkg/types"
)

var Containers types.Phase = containers{}

type containers struct{}

func (p containers) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	var commands []types.Command
	files := types.Filesystem{}
	for _, c := range sys.Containers {

		sys.Services[c.Name()] = types.Service{
			Name:      c.Name(),
			ExecStart: exec(sys, c),
			Extra:     types.DefaultSystemdService(c.Name()),
		}
		if len(c.Env) > 0 {
			files["/etc/environment."+c.Name()] = types.File{Content: toEnvironmentFile(ctx, c)}
		}

	}
	return commands, files, nil
}

func (p containers) Verify(cfg *types.Config, results *types.VerifyResults, flags ...types.Flag) bool {
	verify := true
	for f := range cfg.Files {

		if _, err := os.Stat(f); err != nil {
			verify = false
			results.Fail("%s does not exist", f)
		} else {
			results.Pass("%s exists", f)
		}
	}

	for f := range cfg.Templates {
		if _, err := os.Stat(f); err != nil {
			verify = false
			results.Fail("%s does not exist", f)
		} else {
			results.Pass("%s exists", f)
		}
	}

	return verify
}

func toEnvironmentFile(ctx *types.SystemContext, c types.Container) string {
	s := ""
	for k, v := range c.Env {
		s += fmt.Sprintf("%s=%s\n", k, v)
	}
	return s
}

func exec(sys *types.Config, c types.Container) string {
	exec := c.DockerOpts
	if len(c.Env) > 0 {
		exec += fmt.Sprintf(" --env-file /etc/environment.%s", c.Name())
	}
	if c.Network != "" {
		exec += " --network " + c.Network
	}

	for _, v := range c.Volumes {
		exec += fmt.Sprintf(" -v %s", v)
	}

	for _, p := range c.Ports {
		exec += fmt.Sprintf(" -p %d:%d", p.Port, p.Target)
	}

	return fmt.Sprintf("%s run --rm --name %s %s %s %s", sys.ContainerRuntime.GetCLI(), c.Name(), exec, c.Image, c.Args)
}


<|start_filename|>pkg/phases/sysctl.go<|end_filename|>
package phases

import (
	"fmt"
	"os"
	"strings"

	"github.com/flanksource/konfigadm/pkg/types"

	"github.com/flanksource/konfigadm/pkg/utils"
)

var Sysctl types.Phase = sysctl{}

type sysctl struct{}

func (p sysctl) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	var commands []types.Command
	files := types.Filesystem{}

	if len(sys.Sysctls) > 0 {
		filename := fmt.Sprintf("/etc/sysctl.d/100-%s.conf", sys.Context.Name)
		files[filename] = types.File{Content: utils.MapToIni(sys.Sysctls)}
	}

	for k, v := range sys.Sysctls {
		// make sysctl application errors warnings
		commands = append(commands, types.Command{Cmd: fmt.Sprintf("sysctl -w %s=%s || true", k, v)})
	}
	return commands, files, nil
}

func (p sysctl) Verify(cfg *types.Config, results *types.VerifyResults, flags ...types.Flag) bool {
	verify := true
	for k, v := range cfg.Sysctls {
		if os.Getenv("container") != "" {
			results.Skip("sysctl[%s]: cannot test inside a container", k)
			continue
		}
		value := utils.SafeRead("/proc/sys" + strings.Replace(k, ".", "/", -1))
		if value == v {
			results.Pass("sysctl[%s]: %s", k, v)
		} else {
			results.Fail("sysctl[%s]: %s != %s", k, value, v)
			verify = false
		}
	}
	return verify
}


<|start_filename|>pkg/apps/cni.go<|end_filename|>
package apps

import (
	"github.com/flanksource/konfigadm/pkg/types"
)

var CNI types.Phase = cni{}

type cni struct{}

func (k cni) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {

	return []types.Command{}, types.Filesystem{}, nil
}


<|start_filename|>pkg/apps/cleanup.go<|end_filename|>
package apps

import (
	"github.com/flanksource/konfigadm/pkg/build"
	"github.com/flanksource/konfigadm/pkg/phases"
	"github.com/flanksource/konfigadm/pkg/types"
)

var Cleanup types.Phase = cleanup{}

type cleanup struct{}

func (c cleanup) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	fs := types.Filesystem{}
	cmds := types.Commands{}

	if sys.Cleanup == nil || !*sys.Cleanup {
		return cmds.GetCommands(), fs, nil
	}

	for _, os := range phases.BaseOperatingSystems {
		cmds.AddAll(os.GetPackageManager().CleanupCaches().WithTags(os.GetTags()...).GetCommands()...)
	}

	if ctx.CaptureLogs != "" {
		for _, cmd := range build.CaptureLogCommands() {
			cmds.Add(cmd)
		}
	}
	cmds.
		Add("rm -rf /tmp/* || true").
		Add("rm -rf /var/tmp/* || true").
		Add("rm -rf /usr/share/man/* || true").
		Add("rm -rf /usr/share/doc/* || true").
		Add("rm -rf /var/lib/dhclient/* || true").
		Add("rm /etc/netplan/50-cloud-init.yaml || true").
		Add("rm /etc/udev/rules.d/70-persistent-net.rules || true").
		Add("rm -f /etc/ssh/ssh_host_* || true").
		Add("sed -i '/^\\(HWADDR\\|UUID\\)=/d' /etc/sysconfig/network-scripts/ifcfg-* || true").
		Add("find /var/cache -type f -exec rm -rf {} \\;").
		Add("find /var/log -type f | while read -r f; do truncate -s 0 \"$f\"; done;").
		Add("rm -rf /var/run/cloud-init || true").
		Add("rm -rf /var/lib/cloud || true").
		Add("journalctl --rotate && sleep 5 && journalctl --vacuum-time=1s || true").
		Add("truncate -s 0 /etc/machine-id").
		Add("test -e /var/lib/dbus/machine-id  && truncate -s 0 /var/lib/dbus/machine-id").
		Add("truncate -s 0 /root/.bash_history").
		Add("echo Finished cleanup on $(date)  > /var/log/cleanup.log").
		Add("dd if=/dev/zero of=/EMPTY bs=1M  2>/dev/null || true;  rm -f /EMPTY")

	return cmds.GetCommands(), fs, nil
}


<|start_filename|>pkg/ansible/vault.go<|end_filename|>
package ansible

// https://github.com/pbthorste/avtool/blob/master/decrypt.go


<|start_filename|>pkg/types/apps.go<|end_filename|>
package types

//ContainerRuntime installs a container runtime such as docker or CRI-O
type ContainerRuntime struct {
	Type    string `yaml:"type,omitempty"`
	Arg     string `yaml:"arg,omitempty"`
	Options string `yaml:"options,omitempty"`
	Version string `yaml:"version,omitempty"`
	//Images is a list of container images to pre-pull
	Images []string `yaml:"images,omitempty"`
}

func (cri ContainerRuntime) GetCLI() string {
	if cri.Type == "containerd" {
		return "/usr/local/bin/ctr"
	}
	return "/usr/bin/docker"
}

//KubernetesSpec installs the packages and configures the system for kubernetes, it does not actually bootstrap and configure kubernetes itself
//Use kubeadm in a `command` to actually configure and start kubernetes
type KubernetesSpec struct {
	Version      string `yaml:"version,omitempty"`
	DownloadPath string `yaml:"download_path,omitempty"`
	ImagePrefix  string `yaml:"image_prefix,omitempty"`
}


<|start_filename|>cmd/verify.go<|end_filename|>
package cmd

import (
	"os"

	"github.com/flanksource/konfigadm/pkg/types"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var (
	//Verify command
	Verify = cobra.Command{
		Use:   "verify",
		Short: "Verify that the configuration has been applied correctly and is in a healthy state",
		Args:  cobra.MinimumNArgs(0),
		Run: func(cmd *cobra.Command, args []string) {
			cfg := GetConfig(cmd, args)
			_, _, err := cfg.ApplyPhases()
			if err != nil {
				log.Error(err)
			}
			verifier := types.VerifyResults{}
			if !cfg.Verify(&verifier) {
				verifier.Done()
				os.Exit(1)
			}
			verifier.Done()
		},
	}
)


<|start_filename|>pkg/build/scratch.go<|end_filename|>
package build

import (
	"io/ioutil"
	"os"
	"runtime"

	"github.com/flanksource/konfigadm/pkg/utils"
	log "github.com/sirupsen/logrus"
)

type Scratch interface {
	Create() error
	UnwrapToDir(dir string) error
	GetImg() string
}

type DarwinScratch struct {
	img string
}

func NewScratch() Scratch {
	var scratch Scratch
	if runtime.GOOS == "darwin" {
		scratch = &DarwinScratch{}
	}
	if err := scratch.Create(); err != nil {
		log.Errorf("Error in creating from scratch: %s", err)
	}
	return scratch
}
func (s *DarwinScratch) GetImg() string {
	return s.img
}
func (s *DarwinScratch) Create() error {
	tmp, _ := ioutil.TempFile("", "scratch*.img")
	s.img = tmp.Name()
	log.Infof("Creating %s", s.img)
	if err := utils.Exec("hdiutil create -fs FAT32 -size 100m    -volname scratch %s", s.img); err != nil {
		return err
	}
	return os.Rename(s.img+".dmg", s.img)
}

func (s *DarwinScratch) UnwrapToDir(dir string) error {
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	mount, _ := ioutil.TempDir("", "mount")
	if err := utils.Exec("hdiutil attach -mountpoint %s  %s ", mount, s.img); err != nil {
		return err
	}
	defer utils.Exec("hdiutil detach %s", mount) // nolint: errcheck
	return utils.Exec("cp -r %s/* %s", mount, dir)
}

func CaptureLogCommands() []string {
	return []string{
		"mkdir -p /scratch",
		"journalctl  -b --no-hostname -o short > /scratch/journal.log",
		"cp -r /var/log/ /scratch || true",
		"cp -r /var/lib/cloud/ /scratch || true",
	}
}


<|start_filename|>pkg/phases/template_test.go<|end_filename|>
package phases_test

import (
	. "github.com/flanksource/konfigadm/pkg/types"
	"github.com/onsi/gomega"
	log "github.com/sirupsen/logrus"
	"os"
	"testing"
)

func TestArgs(t *testing.T) {
	os.Setenv("key1", "val2")
	cfg, g := NewFixture("env.yml", t).WithVars("env1=value1", "env2=value2").Build()
	if _, _, err := cfg.ApplyPhases(); err != nil {
		log.Errorf("Failed to apply phases: %s", err)
	}
	g.Expect(cfg.Environment["env1"]).To(gomega.Equal("val: val2"))
}

func TestArgsFile(t *testing.T) {
}
func TestArgsRemotes(t *testing.T) {
}

func TestArgsPriority(t *testing.T) {
}

func TestArgRuntimeFlags(t *testing.T) {

}


<|start_filename|>pkg/phases/files_test.go<|end_filename|>
package phases_test

import (
	. "github.com/flanksource/konfigadm/pkg/types"
	"github.com/onsi/gomega"
	"io/ioutil"
	"testing"
)

func TestCopy(t *testing.T) {
	cfg, g := NewFixture("files.yml", t).Build()
	fs, _, _ := cfg.ApplyPhases()
	data, _ := ioutil.ReadFile("../../fixtures/files.yml")
	g.Expect(fs).To(gomega.HaveKey("/etc/test"))
	g.Expect(fs["/etc/test"].Content).To(gomega.Equal(string(data)))
}

func TestLookup(t *testing.T) {
}

func TestLookupFromContext(t *testing.T) {
}


<|start_filename|>pkg/phases/commands.go<|end_filename|>
package phases

import (
	"github.com/flanksource/konfigadm/pkg/types"
)

var CommandsPhase types.AllPhases = command{}

type command struct{}

func (p command) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	var commands []types.Command
	files := types.Filesystem{}
	commands = append(commands, sys.PreCommands...)
	commands = append(commands, sys.Commands...)
	commands = append(commands, sys.PostCommands...)
	sys.PreCommands = nil
	sys.Commands = nil
	sys.PostCommands = nil

	return commands, files, nil
}
func (p command) ProcessFlags(sys *types.Config, flags ...types.Flag) {
	sys.PreCommands = types.FilterFlags(sys.PreCommands, flags...)
	sys.Commands = types.FilterFlags(sys.Commands, flags...)
	sys.PostCommands = types.FilterFlags(sys.PostCommands, flags...)
}


<|start_filename|>pkg/types/fixture.go<|end_filename|>
package types

import (
	"testing"

	"github.com/onsi/gomega"
)

type Fixture struct {
	name  string
	vars  []string
	flags []Flag
	t     *testing.T
	g     *gomega.WithT
}

func (f *Fixture) WithVars(vars ...string) *Fixture {
	f.vars = vars
	return f
}

func (f *Fixture) WithFlags(flags ...Flag) *Fixture {
	f.flags = flags
	return f
}

func (f *Fixture) Build() (*Config, *gomega.WithT) {
	cfg, err := NewConfig("../../fixtures/" + f.name).
		WithFlags(f.flags...).
		WithVars(f.vars...).
		Build()
	if err != nil {
		f.t.Error(err)
	}
	return cfg, f.g
}

func NewFixture(name string, t *testing.T) *Fixture {
	return &Fixture{
		name: name,
		t:    t,
		g:    gomega.NewWithT(t),
	}
}


<|start_filename|>pkg/types/builder.go<|end_filename|>
package types

import (
	"strings"

	"github.com/flanksource/commons/logger"
	"gopkg.in/flanksource/yaml.v3"
)

type ConfigBuilder struct {
	configs []string
	vars    []string
	flags   []Flag
}

func (builder *ConfigBuilder) WithVars(vars ...string) *ConfigBuilder {
	builder.vars = vars
	return builder
}

func (builder *ConfigBuilder) WithFlags(flags ...Flag) *ConfigBuilder {
	builder.flags = flags
	return builder
}

func (builder *ConfigBuilder) Build() (*Config, error) {
	cfg := &Config{}
	cfg.Init()
	cfg.Context.Flags = builder.flags
	for _, config := range builder.configs {
		if config == "" {
			continue
		}
		c, err := newConfig(config)
		if err != nil {
			logger.Fatalf("Error parsing %s: %s", config, err)
		} else {
			data, _ := yaml.Marshal(c)
			logger.Tracef("\n%s\n", string(data))
		}
		cfg.ImportConfig(*c)
	}

	for _, v := range builder.vars {
		if strings.Contains(v, "=") {
			cfg.Context.Vars[strings.Split(v, "=")[0]] = strings.Split(v, "=")[1]
		}
	}

	return cfg, nil
}

func NewConfig(configs ...string) *ConfigBuilder {
	return &ConfigBuilder{
		configs: configs,
	}
}


<|start_filename|>pkg/build/driver.go<|end_filename|>
package build

import (
	"fmt"
	"strings"

	cloudinit "github.com/flanksource/konfigadm/pkg/cloud-init"
	"github.com/flanksource/konfigadm/pkg/types"
)

type Driver interface {
	Build(image string, cfg *types.Config)
	Test(image string, cfg *types.Config, privateKeyFile string, template string) error
}

func createIso(config *types.Config) (string, error) {
	cloudInit := config.ToCloudInit()

	if config.Context.CaptureLogs != "" {
		cloudInit.Runcmd = append([][]string{[]string{"bash", "-x", "-c", "mkdir /scratch; mount /dev/sdb1 /scratch"}}, cloudInit.Runcmd...)
	}
	if config.Context.CaptureLogs != "" && (config.Cleanup == nil || !*config.Cleanup) {
		cloudInit.Runcmd = append(cloudInit.Runcmd, []string{"bash", "-x", "-c", strings.Join(CaptureLogCommands(), "; ")})
	}

	// PowerState is once per instance and cloud-init clean (creating a new instance) fails on ubuntu 18.04:
	// IsADirectory: /var/lib/cloud/instance
	//	"cloudInit.PowerState.Mode = "poweroff"
	// so we append a shutdown manually
	cloudInit.Runcmd = append(cloudInit.Runcmd, []string{"shutdown", "-h", "now"})
	return cloudinit.CreateISO("builder", cloudInit.String())
}

func createTestIso(config *types.Config) (string, error) {
	cloudInit := config.ToCloudInit()

	if config.Context.CaptureLogs != "" {
		cloudInit.Runcmd = append([][]string{[]string{"bash", "-x", "-c", "mkdir /scratch; mount /dev/sdb1 /scratch"}}, cloudInit.Runcmd...)
	}
	if config.Context.CaptureLogs != "" && (config.Cleanup == nil || !*config.Cleanup) {
		cloudInit.Runcmd = append(cloudInit.Runcmd, []string{"bash", "-x", "-c", strings.Join(CaptureLogCommands(), "; ")})
	}

	fmt.Printf("Cloud init: \n%s\n", cloudInit)
	return cloudinit.CreateISO("builder", cloudInit.String())
}


<|start_filename|>pkg/phases/sysctl_test.go<|end_filename|>
package phases_test

import (
	"testing"

	. "github.com/flanksource/konfigadm/pkg/types"
	"github.com/onsi/gomega"
)

func TestSysctl(t *testing.T) {
	cfg, g := NewFixture("sysctl.yml", t).Build()
	files, commands, _ := cfg.ApplyPhases()
	g.Expect(files).To(gomega.HaveLen(1))
	g.Expect(commands).To(MatchCommand("sysctl -w net.ipv6.conf.all.disable_ipv6=1"))
}


<|start_filename|>pkg/phases/ansible.go<|end_filename|>
package phases

import (
	"fmt"
	"path"

	"github.com/flanksource/konfigadm/pkg/types"
)

var AnsiblePhase types.Phase = ansible{}

type ansible struct{}

func (p ansible) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	var commands []types.Command
	files := types.Filesystem{}

	if len(sys.Ansible) > 0 {
		sys.AppendPackages(nil, types.Package{
			Name: "ansible",
		})
		sys.AppendPackages(nil, types.Package{
			Name:  "python",
			Flags: []types.Flag{types.DEBIAN},
		})
		sys.AppendPackageRepo(types.PackageRepo{
			Name:   "epel",
			URL:    "http://download.fedoraproject.org/pub/epel/7/\\$basearch",
			GPGKey: "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-7",
		}, types.REDHAT_LIKE)
	}

	for _, playbook := range sys.Ansible {
		filename := path.Join(playbook.Workspace, playbook.PlaybookPath)
		files[filename] = types.File{
			Content:     string(playbook.Playbook),
			Permissions: "0600",
			Owner:       "root",
		}
		sys.AddCommand(fmt.Sprintf("cd %s && ansible-playbook -i 'localhost, ' %s", playbook.Workspace, playbook.PlaybookPath))
	}
	return commands, files, nil
}

func (p ansible) Verify(cfg *types.Config, results *types.VerifyResults, flags ...types.Flag) bool {
	verify := true
	return verify
}


<|start_filename|>pkg/build/libguestfs.go<|end_filename|>
package build

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"github.com/flanksource/konfigadm/pkg/types"
	"github.com/flanksource/konfigadm/pkg/utils"
	"github.com/mitchellh/colorstring"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"gopkg.in/flanksource/yaml.v3"
)

type Libguestfs struct{}

func (l Libguestfs) Build(image string, cfg *types.Config) {

	_, _, err := cfg.ApplyPhases()
	if err != nil {
		log.Fatalf("Error applying phases %s\n", err)
	}
	data, _ := yaml.Marshal(cfg)
	tmpfile, err := ioutil.TempFile("", "konfigadm.*.yml")
	if err != nil {
		log.Fatalf("Cannot create tempfile %s", err)
	}
	if _, err := tmpfile.Write(data); err != nil {
		log.Fatalf("Error writing tmp file %s", err)
	}
	os.Remove("/tmp/builder.log")
	os.Remove("builder.log")

	konfigadmPath, _ := os.Executable()
	konfigAdm := path.Base(konfigadmPath)
	cmdLine := fmt.Sprintf(`
		virt-customize -a %s \
		--delete /tmp/builder.log \
		--copy-in %s:/tmp \
		--copy-in %s:/tmp \
		--run-command '/tmp/%s apply -vv -c %s' 1>&2`, image, konfigadmPath, tmpfile.Name(), konfigAdm, tmpfile.Name())

	log.Infof("Executing %s\n", colorstring.Color("[light_green]"+cmdLine))
	if err := utils.Exec(cmdLine); err != nil {
		log.Errorf("builder.log: %s\n", utils.SafeRead("builder.log"))
		log.Fatalf("Failed to run: %s, %s", cmdLine, err)
	}
	if err := utils.Exec(fmt.Sprintf("virt-copy-out -a %s /tmp/builder.log .", image)); err != nil {
		log.Errorf("builder.log: %s\n", utils.SafeRead("builder.log"))
		log.Fatalf("Failed to run: virt-copy-out -a %s /tmp/builder.log . %s", image, err)
	}
	log.Infof("builder.log %s\n", utils.SafeRead("builder.log"))
}

func (l Libguestfs) Test(image string, cfg *types.Config, privateKeyFile string, template string) error {
	return errors.New("Not implemented")
}


<|start_filename|>pkg/types/command.go<|end_filename|>
package types

import (
	"fmt"
	"strings"

	log "github.com/sirupsen/logrus"
	"gopkg.in/flanksource/yaml.v3"
)

//Command encapsulates a command and the tags for which it is applicable
type Command struct {
	Cmd   string
	Flags []Flag
}

type Commands struct {
	dependencies *[]Command
	commands     *[]Command
}

func NewCommand(cmd string) Commands {
	return Commands{
		commands: &[]Command{Command{Cmd: cmd}},
	}
}

func (c *Commands) AddAll(cmd ...Command) *Commands {
	if c.commands == nil {
		c.commands = &[]Command{}
	}
	commandsSlice := *c.commands
	commandsSlice = append(commandsSlice, cmd...)
	c.commands = &commandsSlice
	return c
}

func (c *Commands) Add(commands ...string) *Commands {
	if c.commands == nil {
		c.commands = &[]Command{}
	}
	commandsSlice := *c.commands
	for _, command := range commands {
		commandsSlice = append(commandsSlice, Command{Cmd: command})
	}
	c.commands = &commandsSlice
	return c
}

func (c *Commands) AddDependency(commands ...string) *Commands {
	if c.dependencies == nil {
		c.dependencies = &[]Command{}
	}
	commandsSlice := *c.dependencies
	for _, command := range commands {
		commandsSlice = append(commandsSlice, Command{Cmd: command})
	}
	c.dependencies = &commandsSlice
	return c
}

func contains(commands []Command, other Command) bool {
	for _, cmd := range commands {
		if cmd.Cmd == other.Cmd && MatchesAny(cmd.Flags, other.Flags) {
			return true
		}
	}
	return false
}

func (c Commands) GetCommands() []Command {
	if c.dependencies == nil && c.commands == nil {
		return []Command{}
	}
	if c.dependencies == nil {
		return *c.commands
	}
	if c.commands == nil {
		return *c.dependencies
	}
	return append(*c.dependencies, *c.commands...)
}

func (c *Commands) Append(c2 Commands) *Commands {
	var cmdSlice []Command
	var depSlice []Command
	if c.commands == nil {
		c.commands = &[]Command{}
	}

	if c2.commands != nil {
		cmdSlice = append(*c.commands, *c2.commands...)
		c.commands = &cmdSlice
	}
	if c.dependencies == nil {
		c.dependencies = &[]Command{}
	}
	if c2.dependencies != nil {
		depSlice = append(*c.dependencies, *c2.dependencies...)
		c.dependencies = &depSlice
	}
	return c
}

func (c *Commands) Merge() []Command {
	commands := []Command{}
	if c.dependencies != nil {
		for _, cmd := range *c.dependencies {
			if contains(commands, cmd) {
				continue
			}
			commands = append(commands, cmd)
		}
	}
	if c.commands != nil {
		commands = append(commands, *c.commands...)
	}
	return commands
}

func (c Commands) WithTags(tags ...Flag) Commands {
	new := Commands{commands: &[]Command{}, dependencies: &[]Command{}}
	if c.commands != nil {
		commands := *new.commands
		for _, command := range *c.commands {
			command.Flags = tags
			commands = append(commands, command)
		}
		new.commands = &commands
	}

	if c.dependencies != nil {
		dependencies := *new.dependencies
		for _, command := range *c.dependencies {
			command.Flags = tags
			dependencies = append(dependencies, command)
		}
		new.dependencies = &dependencies
	}
	return new
}

func (c Command) String() string {
	return c.Cmd + fmt.Sprintf("%s", c.Flags)
}

func (cfg *Config) AddCommand(cmd string, flags ...*Flag) *Config {
	command := Command{Cmd: cmd}
	for _, flag := range flags {
		if flag != nil {
			command.Flags = append(command.Flags, *flag)
		}
	}
	cfg.Commands = append(cfg.Commands, command)
	return cfg
}

//UnmarshalYAML decodes comments into tags
func (c *Command) UnmarshalYAML(node *yaml.Node) error {
	c.Cmd = node.Value
	comment := node.LineComment
	if !strings.Contains(comment, "#") {
		return nil
	}
	comment = comment[1:]
	for _, flag := range strings.Split(comment, " ") {
		if FLAG, ok := FLAG_MAP[flag]; ok {
			c.Flags = append(c.Flags, FLAG)
		} else {
			log.Debugf("Ignoring flags: %s on line: %s\n", comment, node.Value)
			return nil
		}
	}
	return nil
}

//MarshalYAML ads tags as comments
func (c Command) MarshalYAML() (interface{}, error) {
	return &yaml.Node{
		Kind:        yaml.ScalarNode,
		Tag:         "!!str",
		LineComment: Marshall(c.Flags),
		Value:       c.Cmd,
	}, nil
}

//FindCmd returns a list of commands starting with prefix
func (cfg *Config) FindCmd(prefix string) []*Command {
	cmds := []*Command{}

	for _, cmd := range cfg.PreCommands {
		if strings.HasPrefix(cmd.Cmd, prefix) {
			cmds = append(cmds, &cmd)
		}
	}
	return cmds
}


<|start_filename|>cmd/minify.go<|end_filename|>
package cmd

import (
	"fmt"

	log "github.com/sirupsen/logrus"

	"github.com/spf13/cobra"
	"gopkg.in/flanksource/yaml.v3"
)

var primitive bool
var bash bool
var (
	Minify = cobra.Command{
		Use:   "minify",
		Short: "Resolve all lookups and dependencies and export a single config file",
		Args:  cobra.MinimumNArgs(0),
		Run: func(cmd *cobra.Command, args []string) {
			cfg := GetConfig(cmd, args)
			fs, commands, err := cfg.ApplyPhases()
			if err != nil {
				log.Fatalf("Error applying phases %s\n", err)
			}

			if primitive {
				data, _ := yaml.Marshal(map[string]interface{}{
					"filesystem": fs,
					"commands":   commands,
				})
				fmt.Println(string(data))
			} else if bash {
				if out, err := cfg.ToBash(); err != nil {
					log.Fatalf("Error converting to bas: %v", err)
				} else {
					fmt.Println(out)
				}
			} else {
				data, _ := yaml.Marshal(cfg)
				fmt.Println(string(data))

			}
		},
	}
)

func init() {
	Minify.Flags().BoolVar(&primitive, "primitive", false, "Minify down to primitive level of commands and files only")
	Minify.Flags().BoolVar(&bash, "bash", false, "Export a single bash file with base64 encoded files")
}


<|start_filename|>Dockerfile<|end_filename|>
FROM ubuntu:bionic
ARG SYSTOOLS_VERSION=3.6

RUN apt-get update && \
  apt-get install -y  checkinstall qemu genisoimage  gnupg-agent curl apt-transport-https wget jq git sudo python-setuptools python-pip python-dev build-essential xz-utils ca-certificates unzip zip software-properties-common && \
  rm -Rf /var/lib/apt/lists/*  && \
  rm -Rf /usr/share/doc && rm -Rf /usr/share/man  && \
  apt-get clean

RUN wget --no-check-certificate https://github.com/moshloop/systools/releases/download/${SYSTOOLS_VERSION}/systools.deb && dpkg -i systools.deb
RUN curl -fksSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
RUN sudo add-apt-repository \
  "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) \
  stable"
RUN apt-get update && \
  apt-get install -y docker-ce docker-ce-cli containerd.io  && \
  rm -Rf /var/lib/apt/lists/*  && \
  rm -Rf /usr/share/doc && rm -Rf /usr/share/man  && \
  apt-get clean

RUN alias curl="curl -k" && curl https://sdk.cloud.google.com | bash -s --  --disable-prompts --install-dir=/opt
RUN wget --no-check-certificate https://dl.google.com/go/go1.13.1.linux-amd64.tar.gz && \
  tar -C /usr/local -xzf go1.13.1.linux-amd64.tar.gz && \
  rm go1.13.1.linux-amd64.tar.gz
RUN  pip install awscli azure-cli==2.10.1 sshtunnel==0.1.3

ARG OVFTOOL_LOCATION
RUN wget $OVFTOOL_LOCATION && \
  OVFTOOL_BIN=$(basename $OVFTOOL_LOCATION) && \
  chmod +x $OVFTOOL_BIN && \
  ./$OVFTOOL_BIN --eulas-agreed --required && \
  rm $OVFTOOL_BIN

ARG PACKER_VERSION=1.2.4
RUN install_bin https://releases.hashicorp.com/packer/${PACKER_VERSION}/packer_${PACKER_VERSION}_linux_amd64.zip

ARG GOVC_VERSION=prerelease-v0.21.0-58-g8d28646
RUN install_bin https://github.com/vmware/govmomi/releases/download/${GOVC_VERSION}/govc_linux_amd64.gz

ARG SOPS_VERSION=3.4.0
RUN install_deb https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/sops_${SOPS_VERSION}_amd64.deb

RUN install_bin https://github.com/CrunchyData/postgres-operator/releases/download/v4.1.0/expenv
RUN install_bin https://github.com/hongkailiu/gojsontoyaml/releases/download/e8bd32d/gojsontoyaml

ARG KONFIGADM_VERSION=
RUN install_bin https://github.com/flanksource/konfigadm/releases/download/${KONFIGADM_VERSION}/konfigadm


<|start_filename|>cmd/common.go<|end_filename|>
package cmd

import (
	"os"

	_ "github.com/flanksource/konfigadm/pkg" // nolint: golint
	"github.com/flanksource/konfigadm/pkg/phases"
	"github.com/flanksource/konfigadm/pkg/types"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

func GetConfigWithImage(cmd *cobra.Command, args []string, image Image) *types.Config {
	return nil
}

func GetConfig(cmd *cobra.Command, args []string) *types.Config {
	configs, err := cmd.Flags().GetStringSlice("config")
	if err != nil {
		log.Fatalf("%s", err)
	}
	vars, err := cmd.Flags().GetStringSlice("var")
	if err != nil {
		log.Fatalf("%s", err)
	}

	flags := []types.Flag{}

	if alias != nil {
		flags = append(flags, alias.Tags...)
	} else if ok, _ := cmd.Flags().GetBool("detect-tags"); ok {
		for _, _os := range phases.SupportedOperatingSystems {
			if _os.DetectAtRuntime() {
				flags = append(flags, _os.GetTags()...)
			}
		}
		if os.Getenv("container") != "" {
			flags = append(flags, types.CONTAINER)
		}
		log.Debugf("Detected %s\n", flags)
	}

	flagNames, err := cmd.Flags().GetStringSlice("tag")
	if err != nil {
		log.Fatalf("%s", err)
	}

	for _, name := range flagNames {
		if flag, ok := types.FLAG_MAP[name]; ok {
			flags = append(flags, flag)
		} else {
			log.Fatalf("Unknown flag %s", name)
		}

	}

	log.Infof("Using tags: %s\n", flags)

	cfg, err := types.NewConfig(append(configs, args...)...).
		WithVars(vars...).
		WithFlags(flags...).
		Build()

	if err != nil {
		panic(nil)
	}
	return cfg

}


<|start_filename|>pkg/phases/commands_test.go<|end_filename|>
package phases_test

import (
	"testing"

	. "github.com/flanksource/konfigadm/pkg/types"
	"github.com/onsi/gomega"
)

func init() {
	// log.SetLevel(log.TraceLevel)
}

func TestCommandRuntimeFlag(t *testing.T) {
}

func setupCommandFixture(t *testing.T, flag Flag) ([]Command, *gomega.WithT) {
	cfg, g := NewFixture("commands.yml", t).WithFlags(flag).Build()
	_, commands, _ := cfg.ApplyPhases()
	return commands, g
}

func TestCommand(t *testing.T) {
	commands, g := setupCommandFixture(t, DEBIAN)
	g.Expect(commands).To(MatchCommand("echo command"))
}

func TestPreCommand(t *testing.T) {
	commands, g := setupCommandFixture(t, DEBIAN)
	g.Expect(commands).To(MatchCommand("echo pre"))
}
func TestPostCommand(t *testing.T) {
	commands, g := setupCommandFixture(t, DEBIAN)
	g.Expect(commands).To(MatchCommand("echo post"))
}
func TestCommandInterpolation(t *testing.T) {
}


<|start_filename|>pkg/phases/users.go<|end_filename|>
package phases

import (
	"encoding/base64"
	"fmt"
	"strings"

	"github.com/flanksource/konfigadm/pkg/types"
)

var Users types.Phase = users{}

type users struct{}

func (u users) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
	files := types.Filesystem{}
	var commands []types.Command

	for _, user := range sys.Users {

		if user.Sudo != "" {
			files["/etc/sudoers.d/91-"+user.Name] = types.File{Content: fmt.Sprintf("%s %s", user.Name, user.Sudo)}
		}
		cmd := fmt.Sprintf("getent passwd %s || (useradd -m", user.Name)

		if user.Shell != "" {
			cmd += " -s " + user.Shell
		}
		if user.UID != "" {
			cmd += " -u " + user.UID
		}

		if user.Gecos != "" {
			cmd += fmt.Sprintf(" -c \"%s\"", user.Gecos)
		}
		cmd += fmt.Sprintf(" %s ) ", user.Name)

		authorizedKeys := base64.StdEncoding.EncodeToString([]byte(strings.Join(user.SSHAuthorizedKeys, "\n")))

		commands = append(commands, types.Command{Cmd: cmd})
		commands = append(commands, types.Command{Cmd: fmt.Sprintf("mkdir -p /home/%s/.ssh/ && ( echo %s | base64 -d > /home/%s/.ssh/authorized_keys ) && chown %s /home/%s/.ssh", user.Name, authorizedKeys, user.Name, user.Name, user.Name)})

	}
	return commands, files, nil

}
