Example #1
0
// Install loads a chart into Kubernetes.
//
// If the chart is not found in the workspace, it is fetched and then installed.
//
// During install, manifests are sent to Kubernetes in the following order:
//
//	- Namespaces
// 	- Secrets
// 	- Volumes
// 	- Services
// 	- Pods
// 	- ReplicationControllers
func Install(chart, home, namespace string, force bool) {
	if !chartInstalled(chart, home) {
		log.Info("No installed chart named %q. Installing now.", chart)
		fetch(chart, chart, home)
	}

	cd := filepath.Join(home, WorkspaceChartPath, chart)
	c, err := model.Load(cd)
	if err != nil {
		log.Die("Failed to load chart: %s", err)
	}

	// Give user the option to bale if dependencies are not satisfied.
	nope, err := dependency.Resolve(c.Chartfile, filepath.Join(home, WorkspaceChartPath))
	if err != nil {
		log.Warn("Failed to check dependencies: %s", err)
		if !force {
			log.Die("Re-run with --force to install anyway.")
		}
	} else if len(nope) > 0 {
		log.Warn("Unsatisfied dependencies:")
		for _, d := range nope {
			log.Msg("\t%s %s", d.Name, d.Version)
		}
		if !force {
			log.Die("Stopping install. Re-run with --force to install anyway.")
		}
	}

	if err := uploadManifests(c, namespace); err != nil {
		log.Die("Failed to upload manifests: %s", err)
	}
	PrintREADME(chart, home)
}
Example #2
0
// Fetch gets a chart from the source repo and copies to the workdir.
//
// - chart is the source
// - lname is the local name for that chart (chart-name); if blank, it is set to the chart.
// - homedir is the home directory for the user
// - ns is the namespace for this package. If blank, it is set to the DefaultNS.
func Fetch(chart, lname, homedir string) {

	if lname == "" {
		lname = chart
	}

	fetch(chart, lname, homedir)

	cfile, err := model.LoadChartfile(filepath.Join(homedir, WorkspaceChartPath, chart, "Chart.yaml"))
	if err != nil {
		log.Die("Source is not a valid chart. Missing Chart.yaml: %s", err)
	}

	deps, err := dependency.Resolve(cfile, filepath.Join(homedir, WorkspaceChartPath))
	if err != nil {
		log.Warn("Could not check dependencies: %s", err)
		return
	}

	if len(deps) > 0 {
		log.Warn("Unsatisfied dependencies:")
		for _, d := range deps {
			log.Msg("\t%s %s", d.Name, d.Version)
		}
	}

	PrintREADME(lname, homedir)
}
Example #3
0
// joinChart reads chart files and joins them with YAML delimiters
func joinChart(chartDir string, files []string) (bytes.Buffer, error) {

	var output bytes.Buffer

	for _, f := range files {
		contents, err := ioutil.ReadFile(f)
		if err != nil {
			return output, err
		}

		rf, err := filepath.Rel(chartDir, f)
		if err != nil {
			log.Warn("Could not find relative path: %s", err)
			return output, err
		}

		delimiter := fmt.Sprintf("--- # %s\n", rf)

		output.WriteString(delimiter)
		output.Write(contents)
		output.WriteString("--- # end\n")

	}

	return output, nil
}
Example #4
0
// listChart enumerates all of the relevant files in a chart
func listChart(chartDir string) ([]string, error) {

	var files []string

	metadataFile := path.Join(chartDir, "Chart.yaml")
	manifestDir := path.Join(chartDir, "manifests")

	// check for existence of important files and directories
	for _, path := range []string{chartDir, metadataFile, manifestDir} {
		if _, err := os.Stat(path); os.IsNotExist(err) {
			return nil, err
		}
	}

	// add metadata file to front of list
	files = append(files, metadataFile)

	// add manifest files
	walker := func(fname string, fi os.FileInfo, e error) error {
		if e != nil {
			log.Warn("Encounter error walking %q: %s", fname, e)
			return nil
		}

		if filepath.Ext(fname) == ".yaml" {
			files = append(files, fname)
		}

		return nil
	}
	filepath.Walk(manifestDir, walker)

	return files, nil
}
Example #5
0
// Files gets a list of all manifest files inside of a chart.
//
// chartDir should contain the path to a chart (the directory which
// holds a Chart.yaml file).
//
// This returns an error if it can't access the directory.
func Files(chartDir string) ([]string, error) {
	dir := filepath.Join(chartDir, "manifests")
	files := []string{}

	if _, err := os.Stat(dir); err != nil {
		return files, err
	}

	// add manifest files
	walker := func(fname string, fi os.FileInfo, e error) error {
		if e != nil {
			log.Warn("Encountered error walking %q: %s", fname, e)
			return nil
		}

		if fi.IsDir() {
			return nil
		}

		if filepath.Ext(fname) == ".yaml" {
			files = append(files, fname)
		}

		return nil
	}
	filepath.Walk(dir, walker)

	return files, nil
}
Example #6
0
func AltInstall(chart, cachedir, home, namespace string, force bool) {
	// Make sure there is a chart in the cachedir.
	if _, err := os.Stat(filepath.Join(cachedir, "Chart.yaml")); err != nil {
		log.Die("Expected a Chart.yaml in %s: %s", cachedir, err)
	}
	// Make sure there is a manifests dir.
	if fi, err := os.Stat(filepath.Join(cachedir, "manifests")); err != nil {
		log.Die("Expected 'manifests/' in %s: %s", cachedir, err)
	} else if !fi.IsDir() {
		log.Die("Expected 'manifests/' to be a directory in %s: %s", cachedir, err)
	}

	// Copy the source chart to the workspace. We ruthlessly overwrite in
	// this case.
	dest := filepath.Join(home, WorkspaceChartPath, chart)
	if err := copyDir(cachedir, dest); err != nil {
		log.Die("Failed to copy %s to %s: %s", cachedir, dest, err)
	}

	// Load the chart.
	c, err := model.Load(dest)
	if err != nil {
		log.Die("Failed to load chart: %s", err)
	}

	// Give user the option to bale if dependencies are not satisfied.
	nope, err := dependency.Resolve(c.Chartfile, filepath.Join(home, WorkspaceChartPath))
	if err != nil {
		log.Warn("Failed to check dependencies: %s", err)
		if !force {
			log.Die("Re-run with --force to install anyway.")
		}
	} else if len(nope) > 0 {
		log.Warn("Unsatisfied dependencies:")
		for _, d := range nope {
			log.Msg("\t%s %s", d.Name, d.Version)
		}
		if !force {
			log.Die("Stopping install. Re-run with --force to install anyway.")
		}
	}

	if err := uploadManifests(c, namespace); err != nil {
		log.Die("Failed to upload manifests: %s", err)
	}
}
Example #7
0
// Copy a directory and its subdirectories.
func copyDir(src, dst string) error {

	var failure error

	walker := func(fname string, fi os.FileInfo, e error) error {
		if e != nil {
			log.Warn("Encounter error walking %q: %s", fname, e)
			failure = e
			return nil
		}

		log.Info("Copying %s", fname)
		rf, err := filepath.Rel(src, fname)
		if err != nil {
			log.Warn("Could not find relative path: %s", err)
			return nil
		}
		df := filepath.Join(dst, rf)

		// Handle directories by creating mirrors.
		if fi.IsDir() {
			if err := os.MkdirAll(df, fi.Mode()); err != nil {
				log.Warn("Could not create %q: %s", df, err)
				failure = err
			}
			return nil
		}

		// Otherwise, copy files.
		in, err := os.Open(fname)
		if err != nil {
			log.Warn("Skipping file %s: %s", fname, err)
			return nil
		}
		out, err := os.Create(df)
		if err != nil {
			in.Close()
			log.Warn("Skipping file copy %s: %s", fname, err)
			return nil
		}
		if _, err = io.Copy(out, in); err != nil {
			log.Warn("Copy from %s to %s failed: %s", fname, df, err)
		}

		if err := out.Close(); err != nil {
			log.Warn("Failed to close %q: %s", df, err)
		}
		if err := in.Close(); err != nil {
			log.Warn("Failed to close reader %q: %s", fname, err)
		}

		return nil
	}
	filepath.Walk(src, walker)
	return failure
}
Example #8
0
// sortManifests sorts manifests into their respective categories, adding to the Chart.
func sortManifests(chart *Chart, manifests []*manifest.Manifest) {
	for _, m := range manifests {
		vo := m.VersionedObject

		if m.Version != "v1" {
			log.Warn("Unsupported version %q", m.Version)
			continue
		}

		switch m.Kind {
		default:
			log.Warn("No support for kind %s. Ignoring.", m.Kind)
		case "Pod":
			o := vo.(*v1.Pod)
			o.Annotations = setOriginFile(o.Annotations, m.Source)
			chart.Pods = append(chart.Pods, o)
		case "ReplicationController":
			o := vo.(*v1.ReplicationController)
			o.Annotations = setOriginFile(o.Annotations, m.Source)
			chart.ReplicationControllers = append(chart.ReplicationControllers, o)
		case "Service":
			o := vo.(*v1.Service)
			o.Annotations = setOriginFile(o.Annotations, m.Source)
			chart.Services = append(chart.Services, o)
		case "Secret":
			o := vo.(*v1.Secret)
			o.Annotations = setOriginFile(o.Annotations, m.Source)
			chart.Secrets = append(chart.Secrets, o)
		case "PersistentVolume":
			o := vo.(*v1.PersistentVolume)
			o.Annotations = setOriginFile(o.Annotations, m.Source)
			chart.PersistentVolumes = append(chart.PersistentVolumes, o)
		case "Namespace":
			o := vo.(*v1.Namespace)
			o.Annotations = setOriginFile(o.Annotations, m.Source)
			chart.Namespaces = append(chart.Namespaces, o)
		}
	}
}
Example #9
0
// List lists all of the local charts.
func List(homedir string) {
	md := filepath.Join(homedir, WorkspaceChartPath, "*")
	charts, err := filepath.Glob(md)
	if err != nil {
		log.Warn("Could not find any charts in %q: %s", md, err)
	}
	for _, c := range charts {
		cname := filepath.Base(c)
		if ch, err := model.Load(filepath.Join(c, "Chart.yaml")); err == nil {
			log.Info("\t%s (%s %s) - %s", cname, ch.Name, ch.Version, ch.Description)
			continue
		}
		log.Info("\t%s (unknown)", cname)
	}
}
Example #10
0
// PrintREADME prints the README file (if it exists) to the console.
func PrintREADME(chart, home string) {
	p := filepath.Join(home, WorkspaceChartPath, chart, "README.*")
	files, err := filepath.Glob(p)
	if err != nil || len(files) == 0 {
		// No README. Skip.
		log.Debug("No readme in %s", p)
		return
	}

	f, err := os.Open(files[0])
	if err != nil {
		log.Warn("Could not read README: %s", err)
		return
	}
	log.Msg(strings.Repeat("=", 40))
	io.Copy(log.Stdout, f)
	log.Msg(strings.Repeat("=", 40))
	f.Close()

}
Example #11
0
func Install(chart, home, namespace string) {
	Fetch(chart, chart, home)
	log.Info("kubectl --namespace=%q create -f %s.yaml", namespace, chart)
	if !chartInstalled(chart, home) {
		log.Info("No installed chart named %q. Installing now.", chart)
		Fetch(chart, chart, home)
	}

	d := filepath.Join(home, WorkspaceChartPath, chart, "manifests")
	log.Debug("Looking for manifests in %q", d)
	files, err := manifestFiles(d)
	if err != nil {
		log.Die("No manifests to install: %s", err)
	}

	for _, f := range files {
		if err := kubectlCreate(f, namespace); err != nil {
			log.Warn("Failed to install manifest %q: %s", f, err)
		}
	}
}
Example #12
0
func deleteChart(c *model.Chart, ns string) error {
	// We delete charts in the ALMOST reverse order that we created them. We
	// start with services to effectively shut down traffic. Then we delete
	// rcs and pods.
	ktype := "service"
	for _, o := range c.Services {
		if err := kubectlDelete(o.Name, ktype, ns); err != nil {
			log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err)
		}
	}
	ktype = "rc"
	for _, o := range c.ReplicationControllers {
		if err := kubectlDelete(o.Name, ktype, ns); err != nil {
			log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err)
		}
	}
	ktype = "pod"
	for _, o := range c.Pods {
		if err := kubectlDelete(o.Name, ktype, ns); err != nil {
			log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err)
		}
	}

	ktype = "secret"
	for _, o := range c.Secrets {
		if err := kubectlDelete(o.Name, ktype, ns); err != nil {
			log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err)
		}
	}
	ktype = "persistentvolume"
	for _, o := range c.PersistentVolumes {
		if err := kubectlDelete(o.Name, ktype, ns); err != nil {
			log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err)
		}
	}
	ktype = "namespace"
	for _, o := range c.Namespaces {
		if err := kubectlDelete(o.Name, ktype, ns); err != nil {
			log.Warn("Could not delete %s %s (Skipping): %s", ktype, o.Name, err)
		}
	}

	return nil
}