<commit_before>package auroraconfig

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/skatteetaten/ao/pkg/configuration"
	"github.com/skatteetaten/ao/pkg/openshift"
	"github.com/skatteetaten/ao/pkg/serverapi"
)

const GIT_URL_FORMAT = "https://%s@git.aurora.skead.no/scm/ac/%s.git"

// TODO: Add debug
func GitCommand(args ...string) (string, error) {
	command := exec.Command("git", args...)
	cmdReader, err := command.StdoutPipe()
	if err != nil {
		return "", err
	}

	scanner := bufio.NewScanner(cmdReader)

	err = command.Start()
	if err != nil {
		return "", errors.Wrap(err, "Failed to start git command")
	}

	message := ""
	for scanner.Scan() {
		message = fmt.Sprintf("%s%s\n", message, scanner.Text())
	}

	err = command.Wait()
	if err != nil {
		if message != "" {
			return "", errors.New(message)
		}
		return "", errors.Wrap(err, "Failed to wait for git command")
	}

	return message, nil
}

func Checkout(url string, outputPath string) (string, error) {
	return GitCommand("clone", url, outputPath)
}

func Pull() (string, error) {
	statuses, err := getStatuses()
	if err != nil {
		return "", err
	}

	if len(statuses) == 0 {
		return GitCommand("pull")
	}

	if _, err := GitCommand("stash"); err != nil {
		return "", err
	}
	if _, err := GitCommand("pull"); err != nil {
		return "", err
	}
	if _, err := GitCommand("stash", "pop"); err != nil {
		return "", err
	}

	return "", nil
}

func getStatuses() ([]string, error) {
	var statuses []string
	if status, err := GitCommand("status", "-s"); err != nil {
		return statuses, errors.Wrap(err, "Failed to get status from repo")
	} else {
		statuses = strings.Fields(status)
	}

	return statuses, nil
}

func Save(url string, config *configuration.ConfigurationClass) (string, error) {
	if err := ValidateRepo(url); err != nil {
		return "", err
	}

	statuses, err := getStatuses()
	if err != nil {
		return "", err
	}

	if !isCleanRepo() {
		fetchOrigin()
		if err := checkForNewCommits(); err != nil {
			return "", err
		}
	}

	if err := checkRepoForChanges(statuses); err != nil {
		return "", err
	}

	if err := handleAuroraConfigCommit(statuses, config); err != nil {
		return "", errors.Wrap(err, "Failed to save AuroraConfig")
	}

	// Delete untracked files
	if _, err := GitCommand("clean", "-fd"); err != nil {
		return "", errors.Wrap(err, "Failed to delete untracked files")
	}

	// Reset branch before pull
	if _, err := GitCommand("reset", "--hard"); err != nil {
		return "", errors.Wrap(err, "Failed to clean repo")
	}

	return Pull()
}

func isCleanRepo() bool {
	_, err := GitCommand("log")
	if err != nil {
		return true
	}

	return false
}

func UpdateLocalRepository(affiliation string, config *openshift.OpenshiftConfig) error {
	path := config.CheckoutPaths[affiliation]
	if path == "" {
		return errors.New("No local repository for affiliation " + affiliation)
	}

	wd, _ := os.Getwd()
	if err := os.Chdir(path); err != nil {
		return err
	}

	if _, err := Pull(); err != nil {
		return err
	}

	return os.Chdir(wd)
}

func ValidateRepo(expectedUrl string) error {
	output, err := GitCommand("remote", "-v")
	if err != nil {
		return err
	}

	extractAffiliation := func(url string) string {
		split := strings.Split(url, "/")
		length := len(split)
		if length == 0 {
			return ""
		}
		return strings.TrimSuffix(split[length-1], ".git")
	}

	remotes := strings.Fields(output)
	var repoUrl string
	for i, v := range remotes {
		if v == "origin" && len(remotes) > i+1 {
			repoUrl = remotes[i+1]
			break
		}
	}

	expectedAffiliation := extractAffiliation(expectedUrl)
	repoAffiliation := extractAffiliation(repoUrl)

	if expectedAffiliation != repoAffiliation {
		message := fmt.Sprintf(`Wrong repository.
Expected affliation to be %s, but was %s.`, expectedAffiliation, repoAffiliation)
		return errors.New(message)
	}

	return nil
}

func handleAuroraConfigCommit(statuses []string, config *configuration.ConfigurationClass) error {
	ac, err := GetAuroraConfig(config)

	if err != nil {
		return errors.Wrap(err, "Failed getting AuroraConfig")
	}

	if err = addFilesToAuroraConfig(&ac); err != nil {
		return errors.Wrap(err, "Failed adding files to AuroraConfig")
	}

	removeFilesFromAuroraConfig(statuses, &ac)

	if err = PutAuroraConfig(ac, config); err != nil {
		return errors.Wrap(err, "Failed committing AuroraConfig")
	}

	return nil
}

func checkRepoForChanges(statuses []string) error {
	if len(statuses) == 0 {
		return errors.New("Nothing to save")
	}

	return nil
}

func fetchOrigin() (string, error) {

	return GitCommand("fetch", "origin")
}

func checkForNewCommits() error {

	if err := compareGitLog("origin/master..HEAD"); err != nil {
		return errors.New(`You have committed local changes.
Please revert them with: git reset HEAD^`)
	}

	if err := compareGitLog("HEAD..origin/master"); err != nil {
		return errors.New(`Please update to latest configuration with: ao pull`)
	}

	return nil
}

func compareGitLog(compare string) error {
	output, err := GitCommand("log", compare, "--oneline")
	if err != nil {
		return err
	}

	if len(output) > 0 {
		return errors.New("new commits")
	}

	return nil
}

func FindGitPath(path string) (string, bool) {
	current := fmt.Sprintf("%s/.git", path)
	if _, err := os.Stat(current); err == nil {
		return path, true
	}

	paths := strings.Split(path, "/")
	length := len(paths)
	if length == 1 {
		return "", false
	}

	next := strings.Join(paths[:length-1], "/")
	return FindGitPath(next)
}

func addFilesToAuroraConfig(ac *serverapi.AuroraConfig) error {

	wd, _ := os.Getwd()
	gitRoot, found := FindGitPath(wd)
	if !found {
		return errors.New("Could not find git")
	}

	return filepath.Walk(gitRoot, func(path string, info os.FileInfo, err error) error {

		filename := strings.TrimPrefix(path, gitRoot+"/")

		if strings.Contains(filename, ".git") || strings.Contains(filename, ".secret") || info.IsDir() {
			return nil
		}

		file, err := ioutil.ReadFile(gitRoot + "/" + filename)

		if err != nil {
			return errors.Wrap(err, "Could not read file "+filename)
		}

		ac.Files[filename] = file

		return nil
	})
}

func removeFilesFromAuroraConfig(statuses []string, ac *serverapi.AuroraConfig) error {
	for i, v := range statuses {
		if v == "D" && len(statuses) > i+1 {
			delete(ac.Files, statuses[i+1])
		}
	}
	return nil
}
<commit_msg>Checking for legal json in each file before save, and displaying which file<commit_after>package auroraconfig

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
	"github.com/skatteetaten/ao/pkg/configuration"
	"github.com/skatteetaten/ao/pkg/jsonutil"
	"github.com/skatteetaten/ao/pkg/openshift"
	"github.com/skatteetaten/ao/pkg/serverapi"
)

const GIT_URL_FORMAT = "https://%s@git.aurora.skead.no/scm/ac/%s.git"

// TODO: Add debug
func GitCommand(args ...string) (string, error) {
	command := exec.Command("git", args...)
	cmdReader, err := command.StdoutPipe()
	if err != nil {
		return "", err
	}

	scanner := bufio.NewScanner(cmdReader)

	err = command.Start()
	if err != nil {
		return "", errors.Wrap(err, "Failed to start git command")
	}

	message := ""
	for scanner.Scan() {
		message = fmt.Sprintf("%s%s\n", message, scanner.Text())
	}

	err = command.Wait()
	if err != nil {
		if message != "" {
			return "", errors.New(message)
		}
		return "", errors.Wrap(err, "Failed to wait for git command")
	}

	return message, nil
}

func Checkout(url string, outputPath string) (string, error) {
	return GitCommand("clone", url, outputPath)
}

func Pull() (string, error) {
	statuses, err := getStatuses()
	if err != nil {
		return "", err
	}

	if len(statuses) == 0 {
		return GitCommand("pull")
	}

	if _, err := GitCommand("stash"); err != nil {
		return "", err
	}
	if _, err := GitCommand("pull"); err != nil {
		return "", err
	}
	if _, err := GitCommand("stash", "pop"); err != nil {
		return "", err
	}

	return "", nil
}

func getStatuses() ([]string, error) {
	var statuses []string
	if status, err := GitCommand("status", "-s"); err != nil {
		return statuses, errors.Wrap(err, "Failed to get status from repo")
	} else {
		statuses = strings.Fields(status)
	}

	return statuses, nil
}

func Save(url string, config *configuration.ConfigurationClass) (string, error) {
	if err := ValidateRepo(url); err != nil {
		return "", err
	}

	statuses, err := getStatuses()
	if err != nil {
		return "", err
	}

	if !isCleanRepo() {
		fetchOrigin()
		if err := checkForNewCommits(); err != nil {
			return "", err
		}
	}

	if err := checkRepoForChanges(statuses); err != nil {
		return "", err
	}

	if err := handleAuroraConfigCommit(statuses, config); err != nil {
		return "", errors.Wrap(err, "Failed to save AuroraConfig")
	}

	// Delete untracked files
	if _, err := GitCommand("clean", "-fd"); err != nil {
		return "", errors.Wrap(err, "Failed to delete untracked files")
	}

	// Reset branch before pull
	if _, err := GitCommand("reset", "--hard"); err != nil {
		return "", errors.Wrap(err, "Failed to clean repo")
	}

	return Pull()
}

func isCleanRepo() bool {
	_, err := GitCommand("log")
	if err != nil {
		return true
	}

	return false
}

func UpdateLocalRepository(affiliation string, config *openshift.OpenshiftConfig) error {
	path := config.CheckoutPaths[affiliation]
	if path == "" {
		return errors.New("No local repository for affiliation " + affiliation)
	}

	wd, _ := os.Getwd()
	if err := os.Chdir(path); err != nil {
		return err
	}

	if _, err := Pull(); err != nil {
		return err
	}

	return os.Chdir(wd)
}

func ValidateRepo(expectedUrl string) error {
	output, err := GitCommand("remote", "-v")
	if err != nil {
		return err
	}

	extractAffiliation := func(url string) string {
		split := strings.Split(url, "/")
		length := len(split)
		if length == 0 {
			return ""
		}
		return strings.TrimSuffix(split[length-1], ".git")
	}

	remotes := strings.Fields(output)
	var repoUrl string
	for i, v := range remotes {
		if v == "origin" && len(remotes) > i+1 {
			repoUrl = remotes[i+1]
			break
		}
	}

	expectedAffiliation := extractAffiliation(expectedUrl)
	repoAffiliation := extractAffiliation(repoUrl)

	if expectedAffiliation != repoAffiliation {
		message := fmt.Sprintf(`Wrong repository.
Expected affliation to be %s, but was %s.`, expectedAffiliation, repoAffiliation)
		return errors.New(message)
	}

	return nil
}

func handleAuroraConfigCommit(statuses []string, config *configuration.ConfigurationClass) error {
	ac, err := GetAuroraConfig(config)

	if err != nil {
		return errors.Wrap(err, "Failed getting AuroraConfig")
	}

	if err = addFilesToAuroraConfig(&ac); err != nil {
		return errors.Wrap(err, "Failed adding files to AuroraConfig")
	}

	removeFilesFromAuroraConfig(statuses, &ac)

	if err = PutAuroraConfig(ac, config); err != nil {
		return errors.Wrap(err, "Failed committing AuroraConfig")
	}

	return nil
}

func checkRepoForChanges(statuses []string) error {
	if len(statuses) == 0 {
		return errors.New("Nothing to save")
	}

	return nil
}

func fetchOrigin() (string, error) {

	return GitCommand("fetch", "origin")
}

func checkForNewCommits() error {

	if err := compareGitLog("origin/master..HEAD"); err != nil {
		return errors.New(`You have committed local changes.
Please revert them with: git reset HEAD^`)
	}

	if err := compareGitLog("HEAD..origin/master"); err != nil {
		return errors.New(`Please update to latest configuration with: ao pull`)
	}

	return nil
}

func compareGitLog(compare string) error {
	output, err := GitCommand("log", compare, "--oneline")
	if err != nil {
		return err
	}

	if len(output) > 0 {
		return errors.New("new commits")
	}

	return nil
}

func FindGitPath(path string) (string, bool) {
	current := fmt.Sprintf("%s/.git", path)
	if _, err := os.Stat(current); err == nil {
		return path, true
	}

	paths := strings.Split(path, "/")
	length := len(paths)
	if length == 1 {
		return "", false
	}

	next := strings.Join(paths[:length-1], "/")
	return FindGitPath(next)
}

func addFilesToAuroraConfig(ac *serverapi.AuroraConfig) error {

	wd, _ := os.Getwd()
	gitRoot, found := FindGitPath(wd)
	if !found {
		return errors.New("Could not find git")
	}

	return filepath.Walk(gitRoot, func(path string, info os.FileInfo, err error) error {

		filename := strings.TrimPrefix(path, gitRoot+"/")

		if strings.Contains(filename, ".git") || strings.Contains(filename, ".secret") || info.IsDir() {
			return nil
		}

		file, err := ioutil.ReadFile(gitRoot + "/" + filename)

		if err != nil {
			return errors.Wrap(err, "Could not read file "+filename)
		}

		if !jsonutil.IsLegalJson(string(file)) {
			err = errors.New("Illegal JSON in file " + filename)
			return err
		}

		ac.Files[filename] = file

		return nil
	})
}

func removeFilesFromAuroraConfig(statuses []string, ac *serverapi.AuroraConfig) error {
	for i, v := range statuses {
		if v == "D" && len(statuses) > i+1 {
			delete(ac.Files, statuses[i+1])
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package cgroup

import (
	"bufio"
	"os"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
)

var cgControllers = map[string]Backend{}
var cgNamespace bool

// Layout determines the cgroup layout on this system
type Layout int

const (
	// CgroupsDisabled indicates that cgroups are not supported
	CgroupsDisabled Layout = iota
	// CgroupsUnified indicates that this is a pure cgroup2 layout
	CgroupsUnified
	// CgroupsHybrid indicates that this is a mixed cgroup1 and cgroup2 layout
	CgroupsHybrid
	// CgroupsLegacy indicates that this is a pure cgroup1 layout
	CgroupsLegacy
)

var cgLayout Layout

// Info contains system cgroup information
type Info struct {
	// Layout is one of CgroupsDisabled, CgroupsUnified, CgroupsHybrid, CgroupsLegacy
	Layout Layout

	// Namespacing indicates support for the cgroup namespace
	Namespacing bool
}

// GetInfo returns basic system cgroup information
func GetInfo() Info {
	info := Info{}
	info.Namespacing = cgNamespace
	info.Layout = cgLayout

	return info
}

// Mode returns the cgroup layout name
func (info *Info) Mode() string {
	switch info.Layout {
	case CgroupsDisabled:
		return "disabled"
	case CgroupsUnified:
		return "cgroup2"
	case CgroupsHybrid:
		return "hybrid"
	case CgroupsLegacy:
		return "legacy"
	}

	return "unknown"
}

// Resource is a generic type used to abstract resource control features
// support for the legacy and unified hierarchy.
type Resource int

const (
	// Blkio resource control
	Blkio Resource = iota

	// BlkioWeight resource control
	BlkioWeight

	// CPU resource control
	CPU

	// CPUAcct resource control
	CPUAcct

	// CPUSet resource control
	CPUSet

	// Devices resource control
	Devices

	// Freezer resource control
	Freezer

	// Hugetlb resource control
	Hugetlb

	// Memory resource control
	Memory

	// MemoryMaxUsage resource control
	MemoryMaxUsage

	// MemorySwap resource control
	MemorySwap

	// MemorySwapMaxUsage resource control
	MemorySwapMaxUsage

	// MemorySwapUsage resource control
	MemorySwapUsage

	// MemorySwappiness resource control
	MemorySwappiness

	// NetPrio resource control
	NetPrio

	// Pids resource control
	Pids
)

// SupportsVersion indicates whether or not a given cgroup resource is
// controllable and in which type of cgroup filesystem.
func (info *Info) SupportsVersion(resource Resource) (Backend, bool) {
	switch resource {
	case Blkio:
		val, ok := cgControllers["blkio"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["io"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case BlkioWeight:
		val, ok := cgControllers["blkio.weight"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["io"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case CPU:
		val, ok := cgControllers["cpu"]
		return val, ok
	case CPUAcct:
		val, ok := cgControllers["cpuacct"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["cpu"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case CPUSet:
		val, ok := cgControllers["memory"]
		return val, ok
	case Devices:
		val, ok := cgControllers["devices"]
		return val, ok
	case Freezer:
		val, ok := cgControllers["freezer"]
		return val, ok
	case Hugetlb:
		val, ok := cgControllers["hugetlb"]
		return val, ok
	case Memory:
		val, ok := cgControllers["memory"]
		return val, ok
	case MemoryMaxUsage:
		val, ok := cgControllers["memory.max_usage_in_bytes"]
		return val, ok
	case MemorySwap:
		val, ok := cgControllers["memory.memsw.limit_in_bytes"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["memory.swap.max"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case MemorySwapMaxUsage:
		val, ok := cgControllers["memory.memsw.max_usage_in_bytes"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case MemorySwapUsage:
		val, ok := cgControllers["memory.memsw.usage_in_bytes"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["memory.swap.current"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case MemorySwappiness:
		val, ok := cgControllers["memory.swappiness"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case NetPrio:
		val, ok := cgControllers["net_prio"]
		return val, ok
	case Pids:
		val, ok := cgControllers["pids"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	}

	return Unavailable, false
}

// Supports indicates whether or not a given resource is controllable.
func (info *Info) Supports(resource Resource, cgroup *CGroup) bool {
	val, ok := info.SupportsVersion(resource)
	if val == V2 && cgroup != nil && !cgroup.UnifiedCapable {
		ok = false
	}

	return ok
}

// Log logs cgroup info
func (info *Info) Log() {
	logger.Infof(" - cgroup layout: %s", info.Mode())

	if !info.Supports(Blkio, nil) {
		logger.Warnf(" - Couldn't find the CGroup blkio, disk I/O limits will be ignored")
	}

	if !info.Supports(BlkioWeight, nil) {
		logger.Warnf(" - Couldn't find the CGroup blkio.weight, disk priority will be ignored")
	}

	if !info.Supports(CPU, nil) {
		logger.Warnf(" - Couldn't find the CGroup CPU controller, CPU time limits will be ignored")
	}

	if !info.Supports(CPUAcct, nil) {
		logger.Warnf(" - Couldn't find the CGroup CPUacct controller, CPU accounting will not be available")
	}

	if !info.Supports(CPUSet, nil) {
		logger.Warnf(" - Couldn't find the CGroup CPUset controller, CPU pinning will be ignored")
	}

	if !info.Supports(Devices, nil) {
		logger.Warnf(" - Couldn't find the CGroup devices controller, device access control won't work")
	}

	if !info.Supports(Freezer, nil) {
		logger.Warnf(" - Couldn't find the CGroup freezer controller, pausing/resuming containers won't work")
	}

	if !info.Supports(Hugetlb, nil) {
		logger.Warnf(" - Couldn't find the CGroup hugetlb controller, hugepage limits will be ignored")
	}

	if !info.Supports(Memory, nil) {
		logger.Warnf(" - Couldn't find the CGroup memory controller, memory limits will be ignored")
	}

	if !info.Supports(NetPrio, nil) {
		logger.Warnf(" - Couldn't find the CGroup network priority controller, network priority will be ignored")
	}

	if !info.Supports(Pids, nil) {
		logger.Warnf(" - Couldn't find the CGroup pids controller, process limits will be ignored")
	}

	if !info.Supports(MemorySwap, nil) {
		logger.Warnf(" - Couldn't find the CGroup memory swap accounting, swap limits will be ignored")
	}
}

func init() {
	_, err := os.Stat("/proc/self/ns/cgroup")
	if err == nil {
		cgNamespace = true
	}

	// Go through the list of resource controllers for LXD.
	selfCg, err := os.Open("/proc/self/cgroup")
	if err != nil {
		if os.IsNotExist(err) {
			logger.Warnf("System doesn't appear to support CGroups")
		} else {
			logger.Errorf("Unable to load list of cgroups: %v", err)
		}

		cgLayout = CgroupsDisabled
		return
	}
	defer selfCg.Close()

	hasV1 := false
	hasV2 := false
	// Go through the file line by line.
	scanSelfCg := bufio.NewScanner(selfCg)
	for scanSelfCg.Scan() {
		line := strings.TrimSpace(scanSelfCg.Text())
		fields := strings.SplitN(line, ":", 3)

		// Deal with the V1 controllers.
		if fields[1] != "" {
			controllers := strings.Split(fields[1], ",")
			for _, controller := range controllers {
				cgControllers[controller] = V1
			}

			hasV1 = true
			continue
		}

		// Parse V2 controllers.
		path := fields[2]
		hybridPath := filepath.Join(cgPath, "unified", path, "cgroup.controllers")
		dedicatedPath := ""

		controllers, err := os.Open(hybridPath)
		if err != nil {
			if !os.IsNotExist(err) {
				logger.Errorf("Unable to load cgroup.controllers")
				return
			}

			dedicatedPath = filepath.Join(cgPath, path, "cgroup.controllers")
			controllers, err = os.Open(dedicatedPath)
			if err != nil && !os.IsNotExist(err) {
				logger.Errorf("Unable to load cgroup.controllers")
				return
			}
		}

		if err == nil {
			unifiedControllers := map[string]Backend{}

			// Record the fact that V2 is present at all.
			unifiedControllers["unified"] = V2

			scanControllers := bufio.NewScanner(controllers)
			for scanControllers.Scan() {
				line := strings.TrimSpace(scanControllers.Text())
				for _, entry := range strings.Split(line, " ") {
					unifiedControllers[entry] = V2
				}
			}
			hasV2 = true

			if dedicatedPath != "" {
				cgControllers = unifiedControllers
				break
			} else {
				for k, v := range unifiedControllers {
					cgControllers[k] = v
				}
			}
		}
		controllers.Close()
	}

	// Check for additional legacy cgroup features
	val, ok := cgControllers["blkio"]
	if ok && val == V1 && shared.PathExists("/sys/fs/cgroup/blkio/blkio.weight") {
		cgControllers["blkio.weight"] = V1
	} else {
		val, ok := cgControllers["blkio"]
		if ok && val == V1 && shared.PathExists("/sys/fs/cgroup/blkio/blkio.bfq.weight") {
			cgControllers["blkio.weight"] = V1
		}
	}

	val, ok = cgControllers["memory"]
	if ok && val == V1 {
		if shared.PathExists("/sys/fs/cgroup/memory/memory.max_usage_in_bytes") {
			cgControllers["memory.max_usage_in_bytes"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.swappiness") {
			cgControllers["memory.swappiness"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") {
			cgControllers["memory.memsw.limit_in_bytes"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.usage_in_bytes") {
			cgControllers["memory.memsw.usage_in_bytes"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.max_usage_in_bytes") {
			cgControllers["memory.memsw.max_usage_in_bytes"] = V1
		}
	}

	val, ok = cgControllers["memory"]
	if ok && val == V2 {
		if shared.PathExists("/sys/fs/cgroup/init.scope/memory.swap.max") {
			cgControllers["memory.swap.max"] = V2
		}

		if shared.PathExists("/sys/fs/cgroup/init.scope/memory.swap.current") {
			cgControllers["memory.swap.current"] = V2
		}
	}

	if hasV1 && hasV2 {
		cgLayout = CgroupsHybrid
	} else if hasV1 {
		cgLayout = CgroupsLegacy
	} else if hasV2 {
		cgLayout = CgroupsUnified
	}

	// "io" and "blkio" controllers are the same thing.
	val, ok = cgControllers["io"]
	if ok {
		cgControllers["blkio"] = val
	}

	if cgLayout == CgroupsUnified {
		// With Cgroup2 devices is built-in (through eBPF).
		cgControllers["devices"] = V2

		// With Cgroup2 freezer is built-in.
		cgControllers["freezer"] = V2
	}
}
<commit_msg>lxd/cgroup: Replace Log() with Warnings()<commit_after>package cgroup

import (
	"bufio"
	"os"
	"path/filepath"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
)

var cgControllers = map[string]Backend{}
var cgNamespace bool

// Layout determines the cgroup layout on this system
type Layout int

const (
	// CgroupsDisabled indicates that cgroups are not supported
	CgroupsDisabled Layout = iota
	// CgroupsUnified indicates that this is a pure cgroup2 layout
	CgroupsUnified
	// CgroupsHybrid indicates that this is a mixed cgroup1 and cgroup2 layout
	CgroupsHybrid
	// CgroupsLegacy indicates that this is a pure cgroup1 layout
	CgroupsLegacy
)

var cgLayout Layout

// Info contains system cgroup information
type Info struct {
	// Layout is one of CgroupsDisabled, CgroupsUnified, CgroupsHybrid, CgroupsLegacy
	Layout Layout

	// Namespacing indicates support for the cgroup namespace
	Namespacing bool
}

// GetInfo returns basic system cgroup information
func GetInfo() Info {
	info := Info{}
	info.Namespacing = cgNamespace
	info.Layout = cgLayout

	return info
}

// Mode returns the cgroup layout name
func (info *Info) Mode() string {
	switch info.Layout {
	case CgroupsDisabled:
		return "disabled"
	case CgroupsUnified:
		return "cgroup2"
	case CgroupsHybrid:
		return "hybrid"
	case CgroupsLegacy:
		return "legacy"
	}

	return "unknown"
}

// Resource is a generic type used to abstract resource control features
// support for the legacy and unified hierarchy.
type Resource int

const (
	// Blkio resource control
	Blkio Resource = iota

	// BlkioWeight resource control
	BlkioWeight

	// CPU resource control
	CPU

	// CPUAcct resource control
	CPUAcct

	// CPUSet resource control
	CPUSet

	// Devices resource control
	Devices

	// Freezer resource control
	Freezer

	// Hugetlb resource control
	Hugetlb

	// Memory resource control
	Memory

	// MemoryMaxUsage resource control
	MemoryMaxUsage

	// MemorySwap resource control
	MemorySwap

	// MemorySwapMaxUsage resource control
	MemorySwapMaxUsage

	// MemorySwapUsage resource control
	MemorySwapUsage

	// MemorySwappiness resource control
	MemorySwappiness

	// NetPrio resource control
	NetPrio

	// Pids resource control
	Pids
)

// SupportsVersion indicates whether or not a given cgroup resource is
// controllable and in which type of cgroup filesystem.
func (info *Info) SupportsVersion(resource Resource) (Backend, bool) {
	switch resource {
	case Blkio:
		val, ok := cgControllers["blkio"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["io"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case BlkioWeight:
		val, ok := cgControllers["blkio.weight"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["io"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case CPU:
		val, ok := cgControllers["cpu"]
		return val, ok
	case CPUAcct:
		val, ok := cgControllers["cpuacct"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["cpu"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case CPUSet:
		val, ok := cgControllers["memory"]
		return val, ok
	case Devices:
		val, ok := cgControllers["devices"]
		return val, ok
	case Freezer:
		val, ok := cgControllers["freezer"]
		return val, ok
	case Hugetlb:
		val, ok := cgControllers["hugetlb"]
		return val, ok
	case Memory:
		val, ok := cgControllers["memory"]
		return val, ok
	case MemoryMaxUsage:
		val, ok := cgControllers["memory.max_usage_in_bytes"]
		return val, ok
	case MemorySwap:
		val, ok := cgControllers["memory.memsw.limit_in_bytes"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["memory.swap.max"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case MemorySwapMaxUsage:
		val, ok := cgControllers["memory.memsw.max_usage_in_bytes"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case MemorySwapUsage:
		val, ok := cgControllers["memory.memsw.usage_in_bytes"]
		if ok {
			return val, ok
		}

		val, ok = cgControllers["memory.swap.current"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case MemorySwappiness:
		val, ok := cgControllers["memory.swappiness"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	case NetPrio:
		val, ok := cgControllers["net_prio"]
		return val, ok
	case Pids:
		val, ok := cgControllers["pids"]
		if ok {
			return val, ok
		}

		return Unavailable, false
	}

	return Unavailable, false
}

// Supports indicates whether or not a given resource is controllable.
func (info *Info) Supports(resource Resource, cgroup *CGroup) bool {
	val, ok := info.SupportsVersion(resource)
	if val == V2 && cgroup != nil && !cgroup.UnifiedCapable {
		ok = false
	}

	return ok
}

// Warnings returns a list of CGroup warnings.
func (info *Info) Warnings() []db.Warning {
	warnings := []db.Warning{}

	if !info.Supports(Blkio, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupBlkio),
			LastMessage: "disk I/O limits will be ignored",
		})
	}

	if !info.Supports(BlkioWeight, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupBlkioWeight),
			LastMessage: "disk priority will be ignored",
		})
	}

	if !info.Supports(CPU, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupCPUController),
			LastMessage: "CPU time limits will be ignored",
		})
	}

	if !info.Supports(CPUAcct, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupCPUacctController),
			LastMessage: "CPU accounting will not be available",
		})
	}

	if !info.Supports(CPUSet, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupCPUController),
			LastMessage: "CPU pinning will be ignored",
		})
	}

	if !info.Supports(Devices, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupDevicesController),
			LastMessage: "device access control won't work",
		})
	}

	if !info.Supports(Freezer, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupFreezerController),
			LastMessage: "pausing/resuming containers won't work",
		})
	}

	if !info.Supports(Hugetlb, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupHugetlbController),
			LastMessage: "hugepage limits will be ignored",
		})
	}

	if !info.Supports(Memory, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupMemoryController),
			LastMessage: "memory limits will be ignored",
		})
	}

	if !info.Supports(NetPrio, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupNetworkPriorityController),
			LastMessage: "network priority will be ignored",
		})
	}

	if !info.Supports(Pids, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupPidsController),
			LastMessage: "process limits will be ignored",
		})
	}

	if !info.Supports(MemorySwap, nil) {
		warnings = append(warnings, db.Warning{
			TypeCode:    int(db.WarningMissingCGroupMemorySwapAccounting),
			LastMessage: "swap limits will be ignored",
		})
	}

	return warnings
}

func init() {
	_, err := os.Stat("/proc/self/ns/cgroup")
	if err == nil {
		cgNamespace = true
	}

	// Go through the list of resource controllers for LXD.
	selfCg, err := os.Open("/proc/self/cgroup")
	if err != nil {
		if os.IsNotExist(err) {
			logger.Warnf("System doesn't appear to support CGroups")
		} else {
			logger.Errorf("Unable to load list of cgroups: %v", err)
		}

		cgLayout = CgroupsDisabled
		return
	}
	defer selfCg.Close()

	hasV1 := false
	hasV2 := false
	// Go through the file line by line.
	scanSelfCg := bufio.NewScanner(selfCg)
	for scanSelfCg.Scan() {
		line := strings.TrimSpace(scanSelfCg.Text())
		fields := strings.SplitN(line, ":", 3)

		// Deal with the V1 controllers.
		if fields[1] != "" {
			controllers := strings.Split(fields[1], ",")
			for _, controller := range controllers {
				cgControllers[controller] = V1
			}

			hasV1 = true
			continue
		}

		// Parse V2 controllers.
		path := fields[2]
		hybridPath := filepath.Join(cgPath, "unified", path, "cgroup.controllers")
		dedicatedPath := ""

		controllers, err := os.Open(hybridPath)
		if err != nil {
			if !os.IsNotExist(err) {
				logger.Errorf("Unable to load cgroup.controllers")
				return
			}

			dedicatedPath = filepath.Join(cgPath, path, "cgroup.controllers")
			controllers, err = os.Open(dedicatedPath)
			if err != nil && !os.IsNotExist(err) {
				logger.Errorf("Unable to load cgroup.controllers")
				return
			}
		}

		if err == nil {
			unifiedControllers := map[string]Backend{}

			// Record the fact that V2 is present at all.
			unifiedControllers["unified"] = V2

			scanControllers := bufio.NewScanner(controllers)
			for scanControllers.Scan() {
				line := strings.TrimSpace(scanControllers.Text())
				for _, entry := range strings.Split(line, " ") {
					unifiedControllers[entry] = V2
				}
			}
			hasV2 = true

			if dedicatedPath != "" {
				cgControllers = unifiedControllers
				break
			} else {
				for k, v := range unifiedControllers {
					cgControllers[k] = v
				}
			}
		}
		controllers.Close()
	}

	// Check for additional legacy cgroup features
	val, ok := cgControllers["blkio"]
	if ok && val == V1 && shared.PathExists("/sys/fs/cgroup/blkio/blkio.weight") {
		cgControllers["blkio.weight"] = V1
	} else {
		val, ok := cgControllers["blkio"]
		if ok && val == V1 && shared.PathExists("/sys/fs/cgroup/blkio/blkio.bfq.weight") {
			cgControllers["blkio.weight"] = V1
		}
	}

	val, ok = cgControllers["memory"]
	if ok && val == V1 {
		if shared.PathExists("/sys/fs/cgroup/memory/memory.max_usage_in_bytes") {
			cgControllers["memory.max_usage_in_bytes"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.swappiness") {
			cgControllers["memory.swappiness"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") {
			cgControllers["memory.memsw.limit_in_bytes"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.usage_in_bytes") {
			cgControllers["memory.memsw.usage_in_bytes"] = V1
		}

		if shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.max_usage_in_bytes") {
			cgControllers["memory.memsw.max_usage_in_bytes"] = V1
		}
	}

	val, ok = cgControllers["memory"]
	if ok && val == V2 {
		if shared.PathExists("/sys/fs/cgroup/init.scope/memory.swap.max") {
			cgControllers["memory.swap.max"] = V2
		}

		if shared.PathExists("/sys/fs/cgroup/init.scope/memory.swap.current") {
			cgControllers["memory.swap.current"] = V2
		}
	}

	if hasV1 && hasV2 {
		cgLayout = CgroupsHybrid
	} else if hasV1 {
		cgLayout = CgroupsLegacy
	} else if hasV2 {
		cgLayout = CgroupsUnified
	}

	// "io" and "blkio" controllers are the same thing.
	val, ok = cgControllers["io"]
	if ok {
		cgControllers["blkio"] = val
	}

	if cgLayout == CgroupsUnified {
		// With Cgroup2 devices is built-in (through eBPF).
		cgControllers["devices"] = V2

		// With Cgroup2 freezer is built-in.
		cgControllers["freezer"] = V2
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Nuclio Authors.

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 resource

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"runtime/debug"
	"strings"
	"time"

	"github.com/nuclio/nuclio/pkg/dashboard"
	"github.com/nuclio/nuclio/pkg/errors"
	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/platform"
	"github.com/nuclio/nuclio/pkg/restful"

	"github.com/nuclio/nuclio-sdk-go"
	"k8s.io/apimachinery/pkg/util/validation"
)

type functionResource struct {
	*resource
}

type functionInfo struct {
	Meta   *functionconfig.Meta   `json:"metadata,omitempty"`
	Spec   *functionconfig.Spec   `json:"spec,omitempty"`
	Status *functionconfig.Status `json:"status,omitempty"`
}

// GetAll returns all functions
func (fr *functionResource) GetAll(request *http.Request) (map[string]restful.Attributes, error) {
	response := map[string]restful.Attributes{}

	// get namespace
	namespace := fr.getNamespaceFromRequest(request)
	if namespace == "" {
		return nil, nuclio.NewErrBadRequest("Namespace must exist")
	}

	getFunctionsOptions := &platform.GetFunctionsOptions{
		Name:      request.Header.Get("x-nuclio-function-name"),
		Namespace: fr.getNamespaceFromRequest(request),
	}

	// if the user wants to filter by project, do that
	projectNameFilter := request.Header.Get("x-nuclio-project-name")
	if projectNameFilter != "" {
		getFunctionsOptions.Labels = fmt.Sprintf("nuclio.io/project-name=%s", projectNameFilter)
	}

	functions, err := fr.getPlatform().GetFunctions(getFunctionsOptions)

	if err != nil {
		return nil, errors.Wrap(err, "Failed to get functions")
	}

	// create a map of attributes keyed by the function id (name)
	for _, function := range functions {
		response[function.GetConfig().Meta.Name] = fr.functionToAttributes(function)
	}

	return response, nil
}

// GetByID returns a specific function by id
func (fr *functionResource) GetByID(request *http.Request, id string) (restful.Attributes, error) {

	// get namespace
	namespace := fr.getNamespaceFromRequest(request)
	if namespace == "" {
		return nil, nuclio.NewErrBadRequest("Namespace must exist")
	}

	function, err := fr.getPlatform().GetFunctions(&platform.GetFunctionsOptions{
		Namespace: fr.getNamespaceFromRequest(request),
		Name:      id,
	})

	if err != nil {
		return nil, errors.Wrap(err, "Failed to get functions")
	}

	if len(function) == 0 {
		return nil, nil
	}

	return fr.functionToAttributes(function[0]), nil
}

// Create and deploy a function
func (fr *functionResource) Create(request *http.Request) (id string, attributes restful.Attributes, responseErr error) {
	functionInfo, responseErr := fr.getFunctionInfoFromRequest(request)
	if responseErr != nil {
		return
	}

	// validate there are no 2 functions with the same name
	getFunctionsOptions := &platform.GetFunctionsOptions{
		Name:      functionInfo.Meta.Name,
		Namespace: fr.getNamespaceFromRequest(request),
	}

	// TODO: Add a lock to prevent race conditions here (prevent 2 functions created with the same name)
	functions, err := fr.getPlatform().GetFunctions(getFunctionsOptions)
	if err != nil {
		responseErr = nuclio.WrapErrInternalServerError(errors.Wrap(err, "Failed to get functions"))
		return
	}

	if len(functions) > 0 {
		responseErr = nuclio.NewErrConflict("Cannot create two functions with the same name")
		return
	}

	// validation finished successfully - store and deploy the given function
	if responseErr = fr.storeAndDeployFunction(functionInfo, request); responseErr != nil {
		return
	}

	responseErr = nuclio.ErrAccepted
	return
}

// Update and deploy a function
func (fr *functionResource) Update(request *http.Request, id string) (attributes restful.Attributes, responseErr error) {
	functionInfo, responseErr := fr.getFunctionInfoFromRequest(request)
	if responseErr != nil {
		return
	}

	if responseErr = fr.storeAndDeployFunction(functionInfo, request); responseErr != nil {
		return
	}

	return nil, nuclio.ErrAccepted
}

// returns a list of custom routes for the resource
func (fr *functionResource) GetCustomRoutes() ([]restful.CustomRoute, error) {

	// since delete and update by default assume /resource/{id} and we want to get the id/namespace from the body
	// we need to register custom routes
	return []restful.CustomRoute{
		{
			Pattern:   "/",
			Method:    http.MethodDelete,
			RouteFunc: fr.deleteFunction,
		},
	}, nil
}

func (fr *functionResource) storeAndDeployFunction(functionInfo *functionInfo, request *http.Request) error {

	creationStateUpdatedTimeout := 45 * time.Second

	doneChan := make(chan bool, 1)
	creationStateUpdatedChan := make(chan bool, 1)
	errDeployingChan := make(chan error, 1)

	// get the authentication configuration for the request
	authConfig, err := fr.getRequestAuthConfig(request)
	if err != nil {
		return err
	}

	// asynchronously, do the deploy so that the user doesn't wait
	go func() {
		defer func() {
			if err := recover(); err != nil {
				callStack := debug.Stack()

				fr.Logger.ErrorWith("Panic caught while creating function",
					"err",
					err,
					"stack",
					string(callStack))
			}
		}()

		dashboardServer := fr.GetServer().(*dashboard.Server)

		// if registry / run-registry aren't set - use dashboard settings
		if functionInfo.Spec.Build.Registry == "" {
			functionInfo.Spec.Build.Registry = dashboardServer.GetRegistryURL()
		}

		if functionInfo.Spec.RunRegistry == "" {
			functionInfo.Spec.RunRegistry = dashboardServer.GetRunRegistryURL()
		}

		functionInfo.Spec.Build.NoBaseImagesPull = dashboardServer.NoPullBaseImages
		functionInfo.Spec.Build.Offline = dashboardServer.Offline

		// just deploy. the status is async through polling
		_, err := fr.getPlatform().CreateFunction(&platform.CreateFunctionOptions{
			Logger: fr.Logger,
			FunctionConfig: functionconfig.Config{
				Meta: *functionInfo.Meta,
				Spec: *functionInfo.Spec,
			},
			CreationStateUpdated:       creationStateUpdatedChan,
			AuthConfig:                 authConfig,
			DependantImagesRegistryURL: fr.GetServer().(*dashboard.Server).GetDependantImagesRegistryURL(),
		})

		if err != nil {
			fr.Logger.WarnWith("Failed to deploy function", "err", err)
			errDeployingChan <- err
		}

		doneChan <- true
	}()

	// wait until the function is in "creating" state. we must return only once the correct function state
	// will be returned on an immediate get. for example, if the function exists and is in "ready" state, we don't
	// want to return before the function's state is in "building"
	select {
	case <-creationStateUpdatedChan:
		break
	case errDeploying := <-errDeployingChan:
		return errDeploying
	case <-time.After(creationStateUpdatedTimeout):
		return nuclio.NewErrInternalServerError("Timed out waiting for creation state to be set")
	}

	// mostly for testing, but can also be for clients that want to wait for some reason
	if request.Header.Get("x-nuclio-wait-function-action") == "true" {
		<-doneChan
	}

	return nil
}

func (fr *functionResource) deleteFunction(request *http.Request) (*restful.CustomRouteFuncResponse, error) {

	// get function config and status from body
	functionInfo, err := fr.getFunctionInfoFromRequest(request)
	if err != nil {
		fr.Logger.WarnWith("Failed to get function config and status from body", "err", err)

		return &restful.CustomRouteFuncResponse{
			Single:     true,
			StatusCode: http.StatusBadRequest,
		}, err
	}

	// get the authentication configuration for the request
	authConfig, err := fr.getRequestAuthConfig(request)
	if err != nil {

		// get error
		if errWithStatus, ok := err.(*nuclio.ErrorWithStatusCode); ok {
			return &restful.CustomRouteFuncResponse{
				Single:     true,
				StatusCode: errWithStatus.StatusCode(),
			}, err
		}

		return &restful.CustomRouteFuncResponse{
			Single:     true,
			StatusCode: http.StatusInternalServerError,
		}, err
	}

	deleteFunctionOptions := platform.DeleteFunctionOptions{
		AuthConfig: authConfig,
	}

	deleteFunctionOptions.FunctionConfig.Meta = *functionInfo.Meta

	err = fr.getPlatform().DeleteFunction(&deleteFunctionOptions)
	if err != nil {
		return &restful.CustomRouteFuncResponse{
			Single:     true,
			StatusCode: http.StatusInternalServerError,
		}, err
	}

	return &restful.CustomRouteFuncResponse{
		ResourceType: "function",
		Single:       true,
		StatusCode:   http.StatusNoContent,
	}, err
}

func (fr *functionResource) functionToAttributes(function platform.Function) restful.Attributes {
	functionSpec := function.GetConfig().Spec

	// artifacts are created unique to the cluster not needed to be returned to any client of nuclio REST API
	functionSpec.RunRegistry = ""
	functionSpec.Build.Registry = ""
	if functionSpec.Build.FunctionSourceCode != "" {
		functionSpec.Image = ""
	}

	attributes := restful.Attributes{
		"metadata": function.GetConfig().Meta,
		"spec":     functionSpec,
	}

	status := function.GetStatus()
	if status != nil {
		attributes["status"] = status
	}

	return attributes
}

func (fr *functionResource) getNamespaceFromRequest(request *http.Request) string {

	// get the namespace provided by the user or the default namespace
	return fr.getNamespaceOrDefault(request.Header.Get("x-nuclio-function-namespace"))
}

func (fr *functionResource) getFunctionInfoFromRequest(request *http.Request) (*functionInfo, error) {

	// read body
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, nuclio.WrapErrInternalServerError(errors.Wrap(err, "Failed to read body"))
	}

	functionInfoInstance := functionInfo{}
	err = json.Unmarshal(body, &functionInfoInstance)
	if err != nil {
		return nil, nuclio.WrapErrBadRequest(errors.Wrap(err, "Failed to parse JSON body"))
	}

	// override namespace if applicable
	if functionInfoInstance.Meta != nil {
		functionInfoInstance.Meta.Namespace = fr.getNamespaceOrDefault(functionInfoInstance.Meta.Namespace)
	}

	// meta must exist
	if functionInfoInstance.Meta == nil ||
		functionInfoInstance.Meta.Name == "" ||
		functionInfoInstance.Meta.Namespace == "" {
		err := errors.New("Function name must be provided in metadata")

		return nil, nuclio.WrapErrBadRequest(err)
	}

	// validate function name is according to k8s convention
	errorMessages := validation.IsQualifiedName(functionInfoInstance.Meta.Name)
	if len(errorMessages) != 0 {
		joinedErrorMessage := strings.Join(errorMessages, ", ")
		return nil, nuclio.NewErrBadRequest("Function name doesn't conform to k8s naming convention. Errors: " + joinedErrorMessage)
	}

	// add project name label if given via header
	projectName := request.Header.Get("x-nuclio-project-name")
	if projectName != "" {
		if functionInfoInstance.Meta.Labels == nil {
			functionInfoInstance.Meta.Labels = map[string]string{}
		}

		functionInfoInstance.Meta.Labels["nuclio.io/project-name"] = projectName
	}

	return &functionInfoInstance, nil
}

// register the resource
var functionResourceInstance = &functionResource{
	resource: newResource("api/functions", []restful.ResourceMethod{
		restful.ResourceMethodGetList,
		restful.ResourceMethodGetDetail,
		restful.ResourceMethodCreate,
		restful.ResourceMethodUpdate,
	}),
}

func init() {
	functionResourceInstance.Resource = functionResourceInstance
	functionResourceInstance.Register(dashboard.DashboardResourceRegistrySingleton)
}
<commit_msg>Fix - RESTapi - get detail function to return NotFound on non existent function (#1483)<commit_after>/*
Copyright 2017 The Nuclio Authors.

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 resource

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"runtime/debug"
	"strings"
	"time"

	"github.com/nuclio/nuclio/pkg/dashboard"
	"github.com/nuclio/nuclio/pkg/errors"
	"github.com/nuclio/nuclio/pkg/functionconfig"
	"github.com/nuclio/nuclio/pkg/platform"
	"github.com/nuclio/nuclio/pkg/restful"

	"github.com/nuclio/nuclio-sdk-go"
	"k8s.io/apimachinery/pkg/util/validation"
)

type functionResource struct {
	*resource
}

type functionInfo struct {
	Meta   *functionconfig.Meta   `json:"metadata,omitempty"`
	Spec   *functionconfig.Spec   `json:"spec,omitempty"`
	Status *functionconfig.Status `json:"status,omitempty"`
}

// GetAll returns all functions
func (fr *functionResource) GetAll(request *http.Request) (map[string]restful.Attributes, error) {
	response := map[string]restful.Attributes{}

	// get namespace
	namespace := fr.getNamespaceFromRequest(request)
	if namespace == "" {
		return nil, nuclio.NewErrBadRequest("Namespace must exist")
	}

	getFunctionsOptions := &platform.GetFunctionsOptions{
		Name:      request.Header.Get("x-nuclio-function-name"),
		Namespace: fr.getNamespaceFromRequest(request),
	}

	// if the user wants to filter by project, do that
	projectNameFilter := request.Header.Get("x-nuclio-project-name")
	if projectNameFilter != "" {
		getFunctionsOptions.Labels = fmt.Sprintf("nuclio.io/project-name=%s", projectNameFilter)
	}

	functions, err := fr.getPlatform().GetFunctions(getFunctionsOptions)

	if err != nil {
		return nil, errors.Wrap(err, "Failed to get functions")
	}

	// create a map of attributes keyed by the function id (name)
	for _, function := range functions {
		response[function.GetConfig().Meta.Name] = fr.functionToAttributes(function)
	}

	return response, nil
}

// GetByID returns a specific function by id
func (fr *functionResource) GetByID(request *http.Request, id string) (restful.Attributes, error) {

	// get namespace
	namespace := fr.getNamespaceFromRequest(request)
	if namespace == "" {
		return nil, nuclio.NewErrBadRequest("Namespace must exist")
	}

	function, err := fr.getPlatform().GetFunctions(&platform.GetFunctionsOptions{
		Namespace: fr.getNamespaceFromRequest(request),
		Name:      id,
	})

	if err != nil {
		return nil, errors.Wrap(err, "Failed to get functions")
	}

	if len(function) == 0 {
		return nil, nuclio.ErrNotFound
	}

	return fr.functionToAttributes(function[0]), nil
}

// Create and deploy a function
func (fr *functionResource) Create(request *http.Request) (id string, attributes restful.Attributes, responseErr error) {
	functionInfo, responseErr := fr.getFunctionInfoFromRequest(request)
	if responseErr != nil {
		return
	}

	// validate there are no 2 functions with the same name
	getFunctionsOptions := &platform.GetFunctionsOptions{
		Name:      functionInfo.Meta.Name,
		Namespace: fr.getNamespaceFromRequest(request),
	}

	// TODO: Add a lock to prevent race conditions here (prevent 2 functions created with the same name)
	functions, err := fr.getPlatform().GetFunctions(getFunctionsOptions)
	if err != nil {
		responseErr = nuclio.WrapErrInternalServerError(errors.Wrap(err, "Failed to get functions"))
		return
	}

	if len(functions) > 0 {
		responseErr = nuclio.NewErrConflict("Cannot create two functions with the same name")
		return
	}

	// validation finished successfully - store and deploy the given function
	if responseErr = fr.storeAndDeployFunction(functionInfo, request); responseErr != nil {
		return
	}

	responseErr = nuclio.ErrAccepted
	return
}

// Update and deploy a function
func (fr *functionResource) Update(request *http.Request, id string) (attributes restful.Attributes, responseErr error) {
	functionInfo, responseErr := fr.getFunctionInfoFromRequest(request)
	if responseErr != nil {
		return
	}

	if responseErr = fr.storeAndDeployFunction(functionInfo, request); responseErr != nil {
		return
	}

	return nil, nuclio.ErrAccepted
}

// returns a list of custom routes for the resource
func (fr *functionResource) GetCustomRoutes() ([]restful.CustomRoute, error) {

	// since delete and update by default assume /resource/{id} and we want to get the id/namespace from the body
	// we need to register custom routes
	return []restful.CustomRoute{
		{
			Pattern:   "/",
			Method:    http.MethodDelete,
			RouteFunc: fr.deleteFunction,
		},
	}, nil
}

func (fr *functionResource) storeAndDeployFunction(functionInfo *functionInfo, request *http.Request) error {

	creationStateUpdatedTimeout := 45 * time.Second

	doneChan := make(chan bool, 1)
	creationStateUpdatedChan := make(chan bool, 1)
	errDeployingChan := make(chan error, 1)

	// get the authentication configuration for the request
	authConfig, err := fr.getRequestAuthConfig(request)
	if err != nil {
		return err
	}

	// asynchronously, do the deploy so that the user doesn't wait
	go func() {
		defer func() {
			if err := recover(); err != nil {
				callStack := debug.Stack()

				fr.Logger.ErrorWith("Panic caught while creating function",
					"err",
					err,
					"stack",
					string(callStack))
			}
		}()

		dashboardServer := fr.GetServer().(*dashboard.Server)

		// if registry / run-registry aren't set - use dashboard settings
		if functionInfo.Spec.Build.Registry == "" {
			functionInfo.Spec.Build.Registry = dashboardServer.GetRegistryURL()
		}

		if functionInfo.Spec.RunRegistry == "" {
			functionInfo.Spec.RunRegistry = dashboardServer.GetRunRegistryURL()
		}

		functionInfo.Spec.Build.NoBaseImagesPull = dashboardServer.NoPullBaseImages
		functionInfo.Spec.Build.Offline = dashboardServer.Offline

		// just deploy. the status is async through polling
		_, err := fr.getPlatform().CreateFunction(&platform.CreateFunctionOptions{
			Logger: fr.Logger,
			FunctionConfig: functionconfig.Config{
				Meta: *functionInfo.Meta,
				Spec: *functionInfo.Spec,
			},
			CreationStateUpdated:       creationStateUpdatedChan,
			AuthConfig:                 authConfig,
			DependantImagesRegistryURL: fr.GetServer().(*dashboard.Server).GetDependantImagesRegistryURL(),
		})

		if err != nil {
			fr.Logger.WarnWith("Failed to deploy function", "err", err)
			errDeployingChan <- err
		}

		doneChan <- true
	}()

	// wait until the function is in "creating" state. we must return only once the correct function state
	// will be returned on an immediate get. for example, if the function exists and is in "ready" state, we don't
	// want to return before the function's state is in "building"
	select {
	case <-creationStateUpdatedChan:
		break
	case errDeploying := <-errDeployingChan:
		return errDeploying
	case <-time.After(creationStateUpdatedTimeout):
		return nuclio.NewErrInternalServerError("Timed out waiting for creation state to be set")
	}

	// mostly for testing, but can also be for clients that want to wait for some reason
	if request.Header.Get("x-nuclio-wait-function-action") == "true" {
		<-doneChan
	}

	return nil
}

func (fr *functionResource) deleteFunction(request *http.Request) (*restful.CustomRouteFuncResponse, error) {

	// get function config and status from body
	functionInfo, err := fr.getFunctionInfoFromRequest(request)
	if err != nil {
		fr.Logger.WarnWith("Failed to get function config and status from body", "err", err)

		return &restful.CustomRouteFuncResponse{
			Single:     true,
			StatusCode: http.StatusBadRequest,
		}, err
	}

	// get the authentication configuration for the request
	authConfig, err := fr.getRequestAuthConfig(request)
	if err != nil {

		// get error
		if errWithStatus, ok := err.(*nuclio.ErrorWithStatusCode); ok {
			return &restful.CustomRouteFuncResponse{
				Single:     true,
				StatusCode: errWithStatus.StatusCode(),
			}, err
		}

		return &restful.CustomRouteFuncResponse{
			Single:     true,
			StatusCode: http.StatusInternalServerError,
		}, err
	}

	deleteFunctionOptions := platform.DeleteFunctionOptions{
		AuthConfig: authConfig,
	}

	deleteFunctionOptions.FunctionConfig.Meta = *functionInfo.Meta

	err = fr.getPlatform().DeleteFunction(&deleteFunctionOptions)
	if err != nil {
		return &restful.CustomRouteFuncResponse{
			Single:     true,
			StatusCode: http.StatusInternalServerError,
		}, err
	}

	return &restful.CustomRouteFuncResponse{
		ResourceType: "function",
		Single:       true,
		StatusCode:   http.StatusNoContent,
	}, err
}

func (fr *functionResource) functionToAttributes(function platform.Function) restful.Attributes {
	functionSpec := function.GetConfig().Spec

	// artifacts are created unique to the cluster not needed to be returned to any client of nuclio REST API
	functionSpec.RunRegistry = ""
	functionSpec.Build.Registry = ""
	if functionSpec.Build.FunctionSourceCode != "" {
		functionSpec.Image = ""
	}

	attributes := restful.Attributes{
		"metadata": function.GetConfig().Meta,
		"spec":     functionSpec,
	}

	status := function.GetStatus()
	if status != nil {
		attributes["status"] = status
	}

	return attributes
}

func (fr *functionResource) getNamespaceFromRequest(request *http.Request) string {

	// get the namespace provided by the user or the default namespace
	return fr.getNamespaceOrDefault(request.Header.Get("x-nuclio-function-namespace"))
}

func (fr *functionResource) getFunctionInfoFromRequest(request *http.Request) (*functionInfo, error) {

	// read body
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, nuclio.WrapErrInternalServerError(errors.Wrap(err, "Failed to read body"))
	}

	functionInfoInstance := functionInfo{}
	err = json.Unmarshal(body, &functionInfoInstance)
	if err != nil {
		return nil, nuclio.WrapErrBadRequest(errors.Wrap(err, "Failed to parse JSON body"))
	}

	// override namespace if applicable
	if functionInfoInstance.Meta != nil {
		functionInfoInstance.Meta.Namespace = fr.getNamespaceOrDefault(functionInfoInstance.Meta.Namespace)
	}

	// meta must exist
	if functionInfoInstance.Meta == nil ||
		functionInfoInstance.Meta.Name == "" ||
		functionInfoInstance.Meta.Namespace == "" {
		err := errors.New("Function name must be provided in metadata")

		return nil, nuclio.WrapErrBadRequest(err)
	}

	// validate function name is according to k8s convention
	errorMessages := validation.IsQualifiedName(functionInfoInstance.Meta.Name)
	if len(errorMessages) != 0 {
		joinedErrorMessage := strings.Join(errorMessages, ", ")
		return nil, nuclio.NewErrBadRequest("Function name doesn't conform to k8s naming convention. Errors: " + joinedErrorMessage)
	}

	// add project name label if given via header
	projectName := request.Header.Get("x-nuclio-project-name")
	if projectName != "" {
		if functionInfoInstance.Meta.Labels == nil {
			functionInfoInstance.Meta.Labels = map[string]string{}
		}

		functionInfoInstance.Meta.Labels["nuclio.io/project-name"] = projectName
	}

	return &functionInfoInstance, nil
}

// register the resource
var functionResourceInstance = &functionResource{
	resource: newResource("api/functions", []restful.ResourceMethod{
		restful.ResourceMethodGetList,
		restful.ResourceMethodGetDetail,
		restful.ResourceMethodCreate,
		restful.ResourceMethodUpdate,
	}),
}

func init() {
	functionResourceInstance.Resource = functionResourceInstance
	functionResourceInstance.Register(dashboard.DashboardResourceRegistrySingleton)
}
<|endoftext|>


<commit_before>package dashboard

import (
	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/rancher/pkg/wrangler"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func addLocalCluster(embedded bool, wrangler *wrangler.Context) error {
	c := &v3.Cluster{
		ObjectMeta: v1.ObjectMeta{
			Name: "local",
		},
		Spec: v32.ClusterSpec{
			Internal:           true,
			DisplayName:        "local",
			FleetWorkspaceName: "fleet-local",
			ClusterSpecBase: v32.ClusterSpecBase{
				DockerRootDir: settings.InitialDockerRootDir.Get(),
			},
		},
		Status: v32.ClusterStatus{
			Driver: v32.ClusterDriverImported,
			Conditions: []v32.ClusterCondition{
				{
					Type:   "Ready",
					Status: corev1.ConditionTrue,
				},
			},
		},
	}
	if embedded {
		c.Status.Driver = v32.ClusterDriverLocal
	}

	// Ignore error
	_, err := wrangler.Mgmt.Cluster().Create(c)
	if apierrors.IsAlreadyExists(err) {
		return nil
	}
	return err
}

func removeLocalCluster(wrangler *wrangler.Context) error {
	// Ignore error
	_ = wrangler.Mgmt.Cluster().Delete("local", &v1.DeleteOptions{})
	return nil
}
<commit_msg>Always create "local" namespace, even when mcm is disabled<commit_after>package dashboard

import (
	v32 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v3 "github.com/rancher/rancher/pkg/generated/norman/management.cattle.io/v3"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/rancher/pkg/wrangler"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func addLocalCluster(embedded bool, wrangler *wrangler.Context) error {
	c := &v3.Cluster{
		ObjectMeta: v1.ObjectMeta{
			Name: "local",
		},
		Spec: v32.ClusterSpec{
			Internal:           true,
			DisplayName:        "local",
			FleetWorkspaceName: "fleet-local",
			ClusterSpecBase: v32.ClusterSpecBase{
				DockerRootDir: settings.InitialDockerRootDir.Get(),
			},
		},
		Status: v32.ClusterStatus{
			Driver: v32.ClusterDriverImported,
			Conditions: []v32.ClusterCondition{
				{
					Type:   "Ready",
					Status: corev1.ConditionTrue,
				},
			},
		},
	}
	if embedded {
		c.Status.Driver = v32.ClusterDriverLocal
	}

	// Ignore error
	_, _ = wrangler.Core.Namespace().Create(&corev1.Namespace{
		ObjectMeta: v1.ObjectMeta{
			Name: "local",
		},
	})

	// Ignore error
	_, err := wrangler.Mgmt.Cluster().Create(c)
	if apierrors.IsAlreadyExists(err) {
		return nil
	}

	return err
}

func removeLocalCluster(wrangler *wrangler.Context) error {
	// Ignore error
	_ = wrangler.Mgmt.Cluster().Delete("local", &v1.DeleteOptions{})
	return nil
}
<|endoftext|>


<commit_before>package image

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

	"github.com/docker/docker/api/types"

	"github.com/flant/logboek"
	"github.com/flant/werf/pkg/docker"
	"github.com/flant/werf/pkg/stapel"
	"github.com/flant/werf/pkg/util"
)

type StageImageContainer struct {
	image                      *StageImage
	name                       string
	runCommands                []string
	serviceRunCommands         []string
	runOptions                 *StageImageContainerOptions
	commitChangeOptions        *StageImageContainerOptions
	serviceCommitChangeOptions *StageImageContainerOptions
}

func newStageImageContainer(image *StageImage) *StageImageContainer {
	c := &StageImageContainer{}
	c.image = image
	c.name = fmt.Sprintf("%s%v", StageContainerNamePrefix, util.GenerateConsistentRandomString(10))
	c.runOptions = newStageContainerOptions()
	c.commitChangeOptions = newStageContainerOptions()
	c.serviceCommitChangeOptions = newStageContainerOptions()
	return c
}

func (c *StageImageContainer) Name() string {
	return c.name
}

func (c *StageImageContainer) UserCommitChanges() []string {
	return c.commitChangeOptions.toCommitChanges()
}

func (c *StageImageContainer) UserRunCommands() []string {
	return c.runCommands
}

func (c *StageImageContainer) AddRunCommands(commands ...string) {
	c.runCommands = append(c.runCommands, commands...)
}

func (c *StageImageContainer) AddServiceRunCommands(commands ...string) {
	c.serviceRunCommands = append(c.serviceRunCommands, commands...)
}

func (c *StageImageContainer) RunOptions() ContainerOptions {
	return c.runOptions
}

func (c *StageImageContainer) CommitChangeOptions() ContainerOptions {
	return c.commitChangeOptions
}

func (c *StageImageContainer) ServiceCommitChangeOptions() ContainerOptions {
	return c.serviceCommitChangeOptions
}

func (c *StageImageContainer) prepareRunArgs() ([]string, error) {
	var args []string
	args = append(args, fmt.Sprintf("--name=%s", c.name))

	runOptions, err := c.prepareRunOptions()
	if err != nil {
		return nil, err
	}

	runArgs, err := runOptions.toRunArgs()
	if err != nil {
		return nil, err
	}

	setColumnsEnv := fmt.Sprintf("--env=COLUMNS=%d", logboek.ContentWidth())
	runArgs = append(runArgs, setColumnsEnv)

	fromImageId, err := c.image.fromImage.MustGetId()
	if err != nil {
		return nil, err
	}

	args = append(args, runArgs...)
	args = append(args, fromImageId)
	args = append(args, "-ec")
	args = append(args, c.prepareRunCommand())

	return args, nil
}

func (c *StageImageContainer) prepareRunCommand() string {
	return ShelloutPack(strings.Join(c.prepareRunCommands(), " && "))
}

func (c *StageImageContainer) prepareRunCommands() []string {
	runCommands := c.prepareAllRunCommands()
	if len(runCommands) != 0 {
		return runCommands
	} else {
		return []string{stapel.TrueBinPath()}
	}
}

func (c *StageImageContainer) prepareAllRunCommands() []string {
	return append(c.serviceRunCommands, c.runCommands...)
}

func ShelloutPack(command string) string {
	return fmt.Sprintf("eval $(echo %s | %s --decode)", base64.StdEncoding.EncodeToString([]byte(command)), stapel.Base64BinPath())
}

func (c *StageImageContainer) prepareIntrospectBeforeArgs() ([]string, error) {
	args, err := c.prepareIntrospectArgsBase()
	if err != nil {
		return nil, err
	}

	fromImageId, err := c.image.fromImage.MustGetId()
	if err != nil {
		return nil, err
	}

	args = append(args, fromImageId)
	args = append(args, "-ec")
	args = append(args, stapel.BashBinPath())

	return args, nil
}

func (c *StageImageContainer) prepareIntrospectArgs() ([]string, error) {
	args, err := c.prepareIntrospectArgsBase()
	if err != nil {
		return nil, err
	}

	imageId, err := c.image.MustGetId()
	if err != nil {
		return nil, err
	}

	args = append(args, imageId)
	args = append(args, "-ec")
	args = append(args, stapel.BashBinPath())

	return args, nil
}

func (c *StageImageContainer) prepareIntrospectArgsBase() ([]string, error) {
	var args []string

	runOptions, err := c.prepareIntrospectOptions()
	if err != nil {
		return nil, err
	}

	runArgs, err := runOptions.toRunArgs()
	if err != nil {
		return nil, err
	}

	args = append(args, []string{"-ti", "--rm"}...)
	args = append(args, runArgs...)

	return args, nil
}

func (c *StageImageContainer) prepareRunOptions() (*StageImageContainerOptions, error) {
	serviceRunOptions, err := c.prepareServiceRunOptions()
	if err != nil {
		return nil, err
	}
	return serviceRunOptions.merge(c.runOptions), nil
}

func (c *StageImageContainer) prepareServiceRunOptions() (*StageImageContainerOptions, error) {
	serviceRunOptions := newStageContainerOptions()
	serviceRunOptions.Workdir = "/"
	serviceRunOptions.Entrypoint = []string{stapel.BashBinPath()}
	serviceRunOptions.User = "0:0"

	stapelContainerName, err := stapel.GetOrCreateContainer()
	if err != nil {
		return nil, err
	}

	serviceRunOptions.VolumesFrom = []string{stapelContainerName}

	return serviceRunOptions, nil
}

func (c *StageImageContainer) prepareIntrospectOptions() (*StageImageContainerOptions, error) {
	return c.prepareRunOptions()
}

func (c *StageImageContainer) prepareCommitChanges() ([]string, error) {
	commitOptions, err := c.prepareCommitOptions()
	if err != nil {
		return nil, err
	}

	commitChanges, err := commitOptions.prepareCommitChanges()
	if err != nil {
		return nil, err
	}
	return commitChanges, nil
}

func (c *StageImageContainer) prepareCommitOptions() (*StageImageContainerOptions, error) {
	inheritedCommitOptions, err := c.prepareInheritedCommitOptions()
	if err != nil {
		return nil, err
	}

	commitOptions := inheritedCommitOptions.merge(c.serviceCommitChangeOptions.merge(c.commitChangeOptions))
	return commitOptions, nil
}

func (c *StageImageContainer) prepareInheritedCommitOptions() (*StageImageContainerOptions, error) {
	inheritedOptions := newStageContainerOptions()

	if c.image.fromImage == nil {
		panic(fmt.Sprintf("runtime error: FromImage should be (%s)", c.image.name))
	}

	fromImageInspect, err := c.image.fromImage.MustGetInspect()
	if err != nil {
		return nil, err
	}

	inheritedOptions.Entrypoint = fromImageInspect.Config.Entrypoint
	inheritedOptions.Cmd = fromImageInspect.Config.Cmd
	inheritedOptions.User = fromImageInspect.Config.User
	if fromImageInspect.Config.WorkingDir != "" {
		inheritedOptions.Workdir = fromImageInspect.Config.WorkingDir
	} else {
		inheritedOptions.Workdir = "/"
	}
	return inheritedOptions, nil
}

func (c *StageImageContainer) run() error {
	runArgs, err := c.prepareRunArgs()
	if err != nil {
		return err
	}

	if err := docker.CliRun(runArgs...); err != nil {
		return fmt.Errorf("container run failed: %s", err.Error())
	}

	return nil
}

func (c *StageImageContainer) introspect() error {
	runArgs, err := c.prepareIntrospectArgs()
	if err != nil {
		return err
	}

	if err := docker.CliRun(runArgs...); err != nil {
		return err
	}

	return nil
}

func (c *StageImageContainer) introspectBefore() error {
	runArgs, err := c.prepareIntrospectBeforeArgs()
	if err != nil {
		return err
	}

	if err := docker.CliRun(runArgs...); err != nil {
		return err
	}

	return nil
}

func (c *StageImageContainer) commit() (string, error) {
	commitChanges, err := c.prepareCommitChanges()
	if err != nil {
		return "", err
	}

	commitOptions := types.ContainerCommitOptions{Changes: commitChanges}
	id, err := docker.ContainerCommit(c.name, commitOptions)
	if err != nil {
		return "", err
	}

	return id, nil
}

func (c *StageImageContainer) rm() error {
	return docker.ContainerRemove(c.name, types.ContainerRemoveOptions{})
}
<commit_msg>[introspection] Ignore user errors during introspection<commit_after>package image

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

	"github.com/docker/docker/api/types"

	"github.com/flant/logboek"
	"github.com/flant/werf/pkg/docker"
	"github.com/flant/werf/pkg/stapel"
	"github.com/flant/werf/pkg/util"
)

type StageImageContainer struct {
	image                      *StageImage
	name                       string
	runCommands                []string
	serviceRunCommands         []string
	runOptions                 *StageImageContainerOptions
	commitChangeOptions        *StageImageContainerOptions
	serviceCommitChangeOptions *StageImageContainerOptions
}

func newStageImageContainer(image *StageImage) *StageImageContainer {
	c := &StageImageContainer{}
	c.image = image
	c.name = fmt.Sprintf("%s%v", StageContainerNamePrefix, util.GenerateConsistentRandomString(10))
	c.runOptions = newStageContainerOptions()
	c.commitChangeOptions = newStageContainerOptions()
	c.serviceCommitChangeOptions = newStageContainerOptions()
	return c
}

func (c *StageImageContainer) Name() string {
	return c.name
}

func (c *StageImageContainer) UserCommitChanges() []string {
	return c.commitChangeOptions.toCommitChanges()
}

func (c *StageImageContainer) UserRunCommands() []string {
	return c.runCommands
}

func (c *StageImageContainer) AddRunCommands(commands ...string) {
	c.runCommands = append(c.runCommands, commands...)
}

func (c *StageImageContainer) AddServiceRunCommands(commands ...string) {
	c.serviceRunCommands = append(c.serviceRunCommands, commands...)
}

func (c *StageImageContainer) RunOptions() ContainerOptions {
	return c.runOptions
}

func (c *StageImageContainer) CommitChangeOptions() ContainerOptions {
	return c.commitChangeOptions
}

func (c *StageImageContainer) ServiceCommitChangeOptions() ContainerOptions {
	return c.serviceCommitChangeOptions
}

func (c *StageImageContainer) prepareRunArgs() ([]string, error) {
	var args []string
	args = append(args, fmt.Sprintf("--name=%s", c.name))

	runOptions, err := c.prepareRunOptions()
	if err != nil {
		return nil, err
	}

	runArgs, err := runOptions.toRunArgs()
	if err != nil {
		return nil, err
	}

	setColumnsEnv := fmt.Sprintf("--env=COLUMNS=%d", logboek.ContentWidth())
	runArgs = append(runArgs, setColumnsEnv)

	fromImageId, err := c.image.fromImage.MustGetId()
	if err != nil {
		return nil, err
	}

	args = append(args, runArgs...)
	args = append(args, fromImageId)
	args = append(args, "-ec")
	args = append(args, c.prepareRunCommand())

	return args, nil
}

func (c *StageImageContainer) prepareRunCommand() string {
	return ShelloutPack(strings.Join(c.prepareRunCommands(), " && "))
}

func (c *StageImageContainer) prepareRunCommands() []string {
	runCommands := c.prepareAllRunCommands()
	if len(runCommands) != 0 {
		return runCommands
	} else {
		return []string{stapel.TrueBinPath()}
	}
}

func (c *StageImageContainer) prepareAllRunCommands() []string {
	return append(c.serviceRunCommands, c.runCommands...)
}

func ShelloutPack(command string) string {
	return fmt.Sprintf("eval $(echo %s | %s --decode)", base64.StdEncoding.EncodeToString([]byte(command)), stapel.Base64BinPath())
}

func (c *StageImageContainer) prepareIntrospectBeforeArgs() ([]string, error) {
	args, err := c.prepareIntrospectArgsBase()
	if err != nil {
		return nil, err
	}

	fromImageId, err := c.image.fromImage.MustGetId()
	if err != nil {
		return nil, err
	}

	args = append(args, fromImageId)
	args = append(args, "-ec")
	args = append(args, stapel.BashBinPath())

	return args, nil
}

func (c *StageImageContainer) prepareIntrospectArgs() ([]string, error) {
	args, err := c.prepareIntrospectArgsBase()
	if err != nil {
		return nil, err
	}

	imageId, err := c.image.MustGetId()
	if err != nil {
		return nil, err
	}

	args = append(args, imageId)
	args = append(args, "-ec")
	args = append(args, stapel.BashBinPath())

	return args, nil
}

func (c *StageImageContainer) prepareIntrospectArgsBase() ([]string, error) {
	var args []string

	runOptions, err := c.prepareIntrospectOptions()
	if err != nil {
		return nil, err
	}

	runArgs, err := runOptions.toRunArgs()
	if err != nil {
		return nil, err
	}

	args = append(args, []string{"-ti", "--rm"}...)
	args = append(args, runArgs...)

	return args, nil
}

func (c *StageImageContainer) prepareRunOptions() (*StageImageContainerOptions, error) {
	serviceRunOptions, err := c.prepareServiceRunOptions()
	if err != nil {
		return nil, err
	}
	return serviceRunOptions.merge(c.runOptions), nil
}

func (c *StageImageContainer) prepareServiceRunOptions() (*StageImageContainerOptions, error) {
	serviceRunOptions := newStageContainerOptions()
	serviceRunOptions.Workdir = "/"
	serviceRunOptions.Entrypoint = []string{stapel.BashBinPath()}
	serviceRunOptions.User = "0:0"

	stapelContainerName, err := stapel.GetOrCreateContainer()
	if err != nil {
		return nil, err
	}

	serviceRunOptions.VolumesFrom = []string{stapelContainerName}

	return serviceRunOptions, nil
}

func (c *StageImageContainer) prepareIntrospectOptions() (*StageImageContainerOptions, error) {
	return c.prepareRunOptions()
}

func (c *StageImageContainer) prepareCommitChanges() ([]string, error) {
	commitOptions, err := c.prepareCommitOptions()
	if err != nil {
		return nil, err
	}

	commitChanges, err := commitOptions.prepareCommitChanges()
	if err != nil {
		return nil, err
	}
	return commitChanges, nil
}

func (c *StageImageContainer) prepareCommitOptions() (*StageImageContainerOptions, error) {
	inheritedCommitOptions, err := c.prepareInheritedCommitOptions()
	if err != nil {
		return nil, err
	}

	commitOptions := inheritedCommitOptions.merge(c.serviceCommitChangeOptions.merge(c.commitChangeOptions))
	return commitOptions, nil
}

func (c *StageImageContainer) prepareInheritedCommitOptions() (*StageImageContainerOptions, error) {
	inheritedOptions := newStageContainerOptions()

	if c.image.fromImage == nil {
		panic(fmt.Sprintf("runtime error: FromImage should be (%s)", c.image.name))
	}

	fromImageInspect, err := c.image.fromImage.MustGetInspect()
	if err != nil {
		return nil, err
	}

	inheritedOptions.Entrypoint = fromImageInspect.Config.Entrypoint
	inheritedOptions.Cmd = fromImageInspect.Config.Cmd
	inheritedOptions.User = fromImageInspect.Config.User
	if fromImageInspect.Config.WorkingDir != "" {
		inheritedOptions.Workdir = fromImageInspect.Config.WorkingDir
	} else {
		inheritedOptions.Workdir = "/"
	}
	return inheritedOptions, nil
}

func (c *StageImageContainer) run() error {
	runArgs, err := c.prepareRunArgs()
	if err != nil {
		return err
	}

	if err := docker.CliRun(runArgs...); err != nil {
		return fmt.Errorf("container run failed: %s", err.Error())
	}

	return nil
}

func (c *StageImageContainer) introspect() error {
	runArgs, err := c.prepareIntrospectArgs()
	if err != nil {
		return err
	}

	if err := docker.CliRun(runArgs...); err != nil {
		if !strings.Contains(err.Error(), "Code: ") || IsStartContainerErr(err) {
			return err
		}
	}

	return nil
}

func (c *StageImageContainer) introspectBefore() error {
	runArgs, err := c.prepareIntrospectBeforeArgs()
	if err != nil {
		return err
	}

	if err := docker.CliRun(runArgs...); err != nil {
		if !strings.Contains(err.Error(), "Code: ") || IsStartContainerErr(err) {
			return err
		}
	}

	return nil
}

// https://docs.docker.com/engine/reference/run/#exit-status
func IsStartContainerErr(err error) bool {
	for _, code := range []string{"125", "126", "127"} {
		if strings.HasPrefix(err.Error(), fmt.Sprintf("Code: %s", code)) {
			return true
		}
	}

	return false
}

func (c *StageImageContainer) commit() (string, error) {
	commitChanges, err := c.prepareCommitChanges()
	if err != nil {
		return "", err
	}

	commitOptions := types.ContainerCommitOptions{Changes: commitChanges}
	id, err := docker.ContainerCommit(c.name, commitOptions)
	if err != nil {
		return "", err
	}

	return id, nil
}

func (c *StageImageContainer) rm() error {
	return docker.ContainerRemove(c.name, types.ContainerRemoveOptions{})
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Rook Authors. All rights reserved.

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 object

import (
	"fmt"
	"path"
	"strconv"

	cephconfig "github.com/rook/rook/pkg/operator/ceph/config"
	"github.com/rook/rook/pkg/operator/ceph/config/keyring"
	cephver "github.com/rook/rook/pkg/operator/ceph/version"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	keyringTemplate = `
[client.radosgw.gateway]
key = %s
caps mon = "allow rw"
caps osd = "allow rwx"
`

	certVolumeName = "rook-ceph-rgw-cert"
	certDir        = "/etc/ceph/private"
	certKeyName    = "cert"
	certFilename   = "rgw-cert.pem"
)

var (
	rgwFrontendName = "civetweb"
)

func rgwFrontend(v cephver.CephVersion) string {
	if v.IsAtLeastNautilus() {
		rgwFrontendName = "beast"
	}

	return rgwFrontendName
}

// TODO: these should be set in the mon's central kv store for mimic+
func (c *clusterConfig) defaultSettings() *cephconfig.Config {
	s := cephconfig.NewConfig()
	s.Section("global").
		Set("rgw log nonexistent bucket", "true").
		Set("rgw intent log object name utc", "true").
		Set("rgw enable usage log", "true").
		Set("rgw frontends", fmt.Sprintf("%s port=%s", rgwFrontend(c.clusterInfo.CephVersion), c.portString())).
		Set("rgw zone", c.store.Name).
		Set("rgw zonegroup", c.store.Name)
	return s
}

func (c *clusterConfig) portString() string {
	var portString string
	port := c.store.Spec.Gateway.Port
	if port != 0 {
		portString = strconv.Itoa(int(port))
	}
	if c.store.Spec.Gateway.SecurePort != 0 && c.store.Spec.Gateway.SSLCertificateRef != "" {
		var separator string
		if port != 0 {
			separator = "+"
		}
		certPath := path.Join(certDir, certFilename)
		// with ssl enabled, the port number must end with the letter s.
		// e.g., "443s ssl_certificate=/etc/ceph/private/keyandcert.pem"
		portString = fmt.Sprintf("%s%s%ds ssl_certificate=%s",
			portString, separator, c.store.Spec.Gateway.SecurePort, certPath)
	}
	return portString
}

func (c *clusterConfig) generateKeyring(replicationControllerOwnerRef *metav1.OwnerReference) error {
	user := "client.radosgw.gateway"
	/* TODO: this says `osd allow rwx` while template says `osd allow *`; which is correct? */
	access := []string{"osd", "allow rwx", "mon", "allow rw"}
	s := keyring.GetSecretStore(c.context, c.store.Namespace, replicationControllerOwnerRef)

	key, err := s.GenerateKey(c.instanceName(), user, access)
	if err != nil {
		return err
	}

	// Delete legacy key store for upgrade from Rook v0.9.x to v1.0.x
	err = c.context.Clientset.CoreV1().Secrets(c.store.Namespace).Delete(c.instanceName(), &metav1.DeleteOptions{})
	if err != nil {
		if errors.IsNotFound(err) {
			logger.Debugf("legacy rgw key %s is already removed", c.instanceName())
		} else {
			logger.Warningf("legacy rgw key %s could not be removed. %+v", c.instanceName(), err)
		}
	}

	keyring := fmt.Sprintf(keyringTemplate, key)
	return s.CreateOrUpdate(c.instanceName(), keyring)
}
<commit_msg>rgw: remove legacy code<commit_after>/*
Copyright 2019 The Rook Authors. All rights reserved.

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 object

import (
	"fmt"
	"path"
	"strconv"

	cephconfig "github.com/rook/rook/pkg/operator/ceph/config"
	"github.com/rook/rook/pkg/operator/ceph/config/keyring"
	cephver "github.com/rook/rook/pkg/operator/ceph/version"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	keyringTemplate = `
[client.radosgw.gateway]
key = %s
caps mon = "allow rw"
caps osd = "allow rwx"
`

	certVolumeName = "rook-ceph-rgw-cert"
	certDir        = "/etc/ceph/private"
	certKeyName    = "cert"
	certFilename   = "rgw-cert.pem"
)

var (
	rgwFrontendName = "civetweb"
)

func rgwFrontend(v cephver.CephVersion) string {
	if v.IsAtLeastNautilus() {
		rgwFrontendName = "beast"
	}

	return rgwFrontendName
}

// TODO: these should be set in the mon's central kv store for mimic+
func (c *clusterConfig) defaultSettings() *cephconfig.Config {
	s := cephconfig.NewConfig()
	s.Section("global").
		Set("rgw log nonexistent bucket", "true").
		Set("rgw intent log object name utc", "true").
		Set("rgw enable usage log", "true").
		Set("rgw frontends", fmt.Sprintf("%s port=%s", rgwFrontend(c.clusterInfo.CephVersion), c.portString())).
		Set("rgw zone", c.store.Name).
		Set("rgw zonegroup", c.store.Name)
	return s
}

func (c *clusterConfig) portString() string {
	var portString string
	port := c.store.Spec.Gateway.Port
	if port != 0 {
		portString = strconv.Itoa(int(port))
	}
	if c.store.Spec.Gateway.SecurePort != 0 && c.store.Spec.Gateway.SSLCertificateRef != "" {
		var separator string
		if port != 0 {
			separator = "+"
		}
		certPath := path.Join(certDir, certFilename)
		// with ssl enabled, the port number must end with the letter s.
		// e.g., "443s ssl_certificate=/etc/ceph/private/keyandcert.pem"
		portString = fmt.Sprintf("%s%s%ds ssl_certificate=%s",
			portString, separator, c.store.Spec.Gateway.SecurePort, certPath)
	}
	return portString
}

func (c *clusterConfig) generateKeyring(replicationControllerOwnerRef *metav1.OwnerReference) error {
	user := "client.radosgw.gateway"
	/* TODO: this says `osd allow rwx` while template says `osd allow *`; which is correct? */
	access := []string{"osd", "allow rwx", "mon", "allow rw"}
	s := keyring.GetSecretStore(c.context, c.store.Namespace, replicationControllerOwnerRef)

	key, err := s.GenerateKey(c.instanceName(), user, access)
	if err != nil {
		return err
	}

	keyring := fmt.Sprintf(keyringTemplate, key)
	return s.CreateOrUpdate(c.instanceName(), keyring)
}
<|endoftext|>


<commit_before>package pushhttp

import (
	"context"
	"errors"
	"net/http"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/registry"
	"github.com/grafana/grafana/pkg/services/live"
	"github.com/grafana/grafana/pkg/services/live/convert"
	"github.com/grafana/grafana/pkg/services/live/pushurl"
	"github.com/grafana/grafana/pkg/setting"
)

var (
	logger = log.New("live.push_http")
)

func init() {
	registry.RegisterServiceWithPriority(&Gateway{}, registry.Low)
}

// Gateway receives data and translates it to Grafana Live publications.
type Gateway struct {
	Cfg         *setting.Cfg      `inject:""`
	GrafanaLive *live.GrafanaLive `inject:""`

	converter *convert.Converter
}

// Init Gateway.
func (g *Gateway) Init() error {
	logger.Info("Telemetry Gateway initialization")

	g.converter = convert.NewConverter()
	return nil
}

// Run Gateway.
func (g *Gateway) Run(ctx context.Context) error {
	<-ctx.Done()
	return ctx.Err()
}

func (g *Gateway) Handle(ctx *models.ReqContext) {
	streamID := ctx.Params(":streamId")

	stream, err := g.GrafanaLive.ManagedStreamRunner.GetOrCreateStream(ctx.SignedInUser.OrgId, streamID)
	if err != nil {
		logger.Error("Error getting stream", "error", err)
		ctx.Resp.WriteHeader(http.StatusInternalServerError)
		return
	}

	// TODO Grafana 8: decide which formats to use or keep all.
	urlValues := ctx.Req.URL.Query()
	frameFormat := pushurl.FrameFormatFromValues(urlValues)

	body, err := ctx.Req.Body().Bytes()
	if err != nil {
		logger.Error("Error reading body", "error", err)
		ctx.Resp.WriteHeader(http.StatusInternalServerError)
		return
	}
	logger.Debug("Live Push request",
		"protocol", "http",
		"streamId", streamID,
		"bodyLength", len(body),
		"frameFormat", frameFormat,
	)

	metricFrames, err := g.converter.Convert(body, frameFormat)
	if err != nil {
		logger.Error("Error converting metrics", "error", err, "frameFormat", frameFormat)
		if errors.Is(err, convert.ErrUnsupportedFrameFormat) {
			ctx.Resp.WriteHeader(http.StatusBadRequest)
		} else {
			ctx.Resp.WriteHeader(http.StatusInternalServerError)
		}
		return
	}

	// TODO -- make sure all packets are combined together!
	// interval = "1s" vs flush_interval = "5s"

	for _, mf := range metricFrames {
		err := stream.Push(ctx.SignedInUser.OrgId, mf.Key(), mf.Frame())
		if err != nil {
			ctx.Resp.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
}
<commit_msg>live: fix log message (#34879)<commit_after>package pushhttp

import (
	"context"
	"errors"
	"net/http"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/registry"
	"github.com/grafana/grafana/pkg/services/live"
	"github.com/grafana/grafana/pkg/services/live/convert"
	"github.com/grafana/grafana/pkg/services/live/pushurl"
	"github.com/grafana/grafana/pkg/setting"
)

var (
	logger = log.New("live.push_http")
)

func init() {
	registry.RegisterServiceWithPriority(&Gateway{}, registry.Low)
}

// Gateway receives data and translates it to Grafana Live publications.
type Gateway struct {
	Cfg         *setting.Cfg      `inject:""`
	GrafanaLive *live.GrafanaLive `inject:""`

	converter *convert.Converter
}

// Init Gateway.
func (g *Gateway) Init() error {
	logger.Info("Live Push Gateway initialization")

	g.converter = convert.NewConverter()
	return nil
}

// Run Gateway.
func (g *Gateway) Run(ctx context.Context) error {
	<-ctx.Done()
	return ctx.Err()
}

func (g *Gateway) Handle(ctx *models.ReqContext) {
	streamID := ctx.Params(":streamId")

	stream, err := g.GrafanaLive.ManagedStreamRunner.GetOrCreateStream(ctx.SignedInUser.OrgId, streamID)
	if err != nil {
		logger.Error("Error getting stream", "error", err)
		ctx.Resp.WriteHeader(http.StatusInternalServerError)
		return
	}

	// TODO Grafana 8: decide which formats to use or keep all.
	urlValues := ctx.Req.URL.Query()
	frameFormat := pushurl.FrameFormatFromValues(urlValues)

	body, err := ctx.Req.Body().Bytes()
	if err != nil {
		logger.Error("Error reading body", "error", err)
		ctx.Resp.WriteHeader(http.StatusInternalServerError)
		return
	}
	logger.Debug("Live Push request",
		"protocol", "http",
		"streamId", streamID,
		"bodyLength", len(body),
		"frameFormat", frameFormat,
	)

	metricFrames, err := g.converter.Convert(body, frameFormat)
	if err != nil {
		logger.Error("Error converting metrics", "error", err, "frameFormat", frameFormat)
		if errors.Is(err, convert.ErrUnsupportedFrameFormat) {
			ctx.Resp.WriteHeader(http.StatusBadRequest)
		} else {
			ctx.Resp.WriteHeader(http.StatusInternalServerError)
		}
		return
	}

	// TODO -- make sure all packets are combined together!
	// interval = "1s" vs flush_interval = "5s"

	for _, mf := range metricFrames {
		err := stream.Push(ctx.SignedInUser.OrgId, mf.Key(), mf.Frame())
		if err != nil {
			ctx.Resp.WriteHeader(http.StatusInternalServerError)
			return
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 driver

import (
	"reflect"
	"testing"

	rspb "k8s.io/helm/pkg/proto/hapi/release"
	"k8s.io/kubernetes/pkg/client/unversioned/testclient"
	"k8s.io/kubernetes/pkg/runtime"
)

func TestConfigMapGet(t *testing.T) {
	// test release
	key := "key-1"
	rls := newTestRelease(key, 1, rspb.Status_DEPLOYED)

	// create test fixture
	cfgmaps := newTestFixture(t, rls)

	// get the release from configmaps
	got, err := cfgmaps.Get(key)
	if err != nil {
		t.Fatalf("failed to get release with key %q. %s", key, err)
	}

	// compare fetched release with original
	if !reflect.DeepEqual(rls, got) {
		t.Errorf("expected {%q}, got {%q}", rls, got)
	}
}

func TestConfigMapList(t *testing.T) {
	t.Skip("ConfigMapList")
}

func TestConfigMapCreate(t *testing.T) {
	// setup
	key := "key-1"
	rls := newTestRelease(key, 1, rspb.Status_DEPLOYED)

	// create test fixture
	cfgmaps := newTestFixture(t, rls)

	// store the release in a configmap
	if err := cfgmaps.Create(rls); err != nil {
		t.Fatalf("failed to create release with key %q: %s", key, err)
	}

	// get the release back
	got, err := cfgmaps.Get(key)
	if err != nil {
		t.Fatalf("failed to get release with key %q: %s", key, err)
	}

	// compare created release with original
	if !reflect.DeepEqual(rls, got) {
		t.Errorf("expected {%q}, got {%q}", rls, got)
	}
}

func TestConfigMapDelete(t *testing.T) {
	// setup
	key := "key-1"
	rls := newTestRelease(key, 1, rspb.Status_DELETED)

	// create test fixture
	cfgmaps := newTestFixture(t, rls)

	// delete the release
	got, err := cfgmaps.Delete(key)
	if err != nil {
		t.Fatalf("failed to delete release with key %q: %s", key, err)
	}

	// compare deleted release with original
	if !reflect.DeepEqual(rls, got) {
		t.Errorf("expected {%q}, got {%q}", rls, got)
	}
}

func TestConfigMapUpdate(t *testing.T) {
	// setup
	key := "key-1"
	rls := newTestRelease(key, 1, rspb.Status_SUPERSEDED)

	// create test fixture
	cfgmaps := newTestFixture(t, rls)

	// update release version
	rls.Version = 2

	// update the release
	if err := cfgmaps.Update(rls); err != nil {
		t.Fatalf("failed to update release with key %q: %s", key, err)
	}

	// fetch the updated release
	got, err := cfgmaps.Get(key)
	if err != nil {
		t.Fatalf("failed to get release with key %q: %s", key, err)
	}

	_ = got
	//TODO: validate the version was update correctly
	//if rls.Version != got.Version {
	//	t.Fatalf("expected version %d, got version %d", rls.Version, got.Version)
	//}
}

// newTestFixture prepopulates a mock implementation of a kubernetes
// ConfigMapsInterface returning an initialized driver.ConfigMaps.
func newTestFixture(t *testing.T, list ...*rspb.Release) *ConfigMaps {
	var objs []runtime.Object

	for i := range list {
		obj, err := newConfigMapsObject(list[i], nil)
		if err != nil {
			t.Fatalf("failed to create object: %s", err)
		}
		objs = append(objs, obj)
	}

	return NewConfigMaps(&testclient.FakeConfigMaps{
		Fake: testclient.NewSimpleFake(objs...),
	})
}

// newTestRelease creates a release object for testing
func newTestRelease(key string, version int32, status rspb.Status_Code) *rspb.Release {
	return &rspb.Release{Name: key, Info: &rspb.Info{Status: &rspb.Status{Code: status}}, Version: version}
}
<commit_msg>basic cfgmaps driver tests<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 driver

import (
	"reflect"
	"testing"

	rspb "k8s.io/helm/pkg/proto/hapi/release"

	"k8s.io/kubernetes/pkg/api"
	kberrs "k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/client/unversioned"
)

func TestConfigMapGet(t *testing.T) {
	key := "key-1"
	rel := newTestRelease(key, 1, rspb.Status_DEPLOYED)

	cfgmaps := newTestFixture(t, []*rspb.Release{rel}...)

	// get release with key
	got, err := cfgmaps.Get(key)
	if err != nil {
		t.Fatalf("Failed to get release: %s", err)
	}
	// compare fetched release with original
	if !reflect.DeepEqual(rel, got) {
		t.Errorf("Expected {%q}, got {%q}", rel, got)
	}
}

func TestConfigMapList(t *testing.T) {
	cfgmaps := newTestFixture(t, []*rspb.Release{
		newTestRelease("key-1", 1, rspb.Status_DELETED),
		newTestRelease("key-2", 1, rspb.Status_DELETED),
		newTestRelease("key-3", 1, rspb.Status_DEPLOYED),
		newTestRelease("key-4", 1, rspb.Status_DEPLOYED),
		newTestRelease("key-5", 1, rspb.Status_SUPERSEDED),
		newTestRelease("key-6", 1, rspb.Status_SUPERSEDED),
	}...)

	// list all deleted releases
	del, err := cfgmaps.List(func(rel *rspb.Release) bool {
		return rel.Info.Status.Code == rspb.Status_DELETED
	})
	// check
	if err != nil {
		t.Errorf("Failed to list deleted: %s", err)
	}
	if len(del) != 2 {
		t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del)
	}

	// list all deployed releases
	dpl, err := cfgmaps.List(func(rel *rspb.Release) bool {
		return rel.Info.Status.Code == rspb.Status_DEPLOYED
	})
	// check
	if err != nil {
		t.Errorf("Failed to list deployed: %s", err)
	}
	if len(dpl) != 2 {
		t.Errorf("Expected 2 deployed, got %d", len(dpl))
	}

	// list all superseded releases
	ssd, err := cfgmaps.List(func(rel *rspb.Release) bool {
		return rel.Info.Status.Code == rspb.Status_SUPERSEDED
	})
	// check
	if err != nil {
		t.Errorf("Failed to list superseded: %s", err)
	}
	if len(ssd) != 2 {
		t.Errorf("Expected 2 superseded, got %d", len(ssd))
	}
}

func TestConfigMapCreate(t *testing.T) {
	cfgmaps := newTestFixture(t)

	key := "key-1"
	rel := newTestRelease(key, 1, rspb.Status_DEPLOYED)

	// store the release in a configmap
	if err := cfgmaps.Create(rel); err != nil {
		t.Fatalf("Failed to create release with key %q: %s", key, err)
	}

	// get the release back
	got, err := cfgmaps.Get(key)
	if err != nil {
		t.Fatalf("Failed to get release with key %q: %s", key, err)
	}

	// compare created release with original
	if !reflect.DeepEqual(rel, got) {
		t.Errorf("Expected {%q}, got {%q}", rel, got)
	}
}

func TestConfigMapUpdate(t *testing.T) {
	key := "key-1"
	rel := newTestRelease(key, 1, rspb.Status_DEPLOYED)

	cfgmaps := newTestFixture(t, []*rspb.Release{rel}...)

	// modify release status code & version
	rel = newTestRelease(key, 2, rspb.Status_SUPERSEDED)

	// perform the update
	if err := cfgmaps.Update(rel); err != nil {
		t.Fatalf("Failed to update release: %s", err)
	}

	// fetch the updated release
	got, err := cfgmaps.Get(key)
	if err != nil {
		t.Fatalf("Failed to get release with key %q: %s", key, err)
	}

	// check release has actually been updated by comparing modified fields
	switch {
	case rel.Info.Status.Code != got.Info.Status.Code:
		t.Errorf("Expected status %s, got status %s", rel.Info.Status.Code, got.Info.Status.Code)
	case rel.Version != got.Version:
		t.Errorf("Expected version %d, got version %d", rel.Version, got.Version)
	}
}

// newTestFixture initializes a MockConfigMapsInterface.
// ConfigMaps are created for each release provided.
func newTestFixture(t *testing.T, releases ...*rspb.Release) *ConfigMaps {
	var mock MockConfigMapsInterface
	mock.Init(t, releases...)

	return NewConfigMaps(&mock)
}

// newTestRelease creates a release object for testing.
func newTestRelease(key string, version int32, status rspb.Status_Code) *rspb.Release {
	return &rspb.Release{Name: key, Info: &rspb.Info{Status: &rspb.Status{Code: status}}, Version: version}
}

// MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface
type MockConfigMapsInterface struct {
	unversioned.ConfigMapsInterface

	objects map[string]*api.ConfigMap
}

func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Release) {
	mock.objects = map[string]*api.ConfigMap{}

	for _, rls := range releases {
		cfgmap, err := newConfigMapsObject(rls, nil)
		if err != nil {
			t.Fatalf("Failed to create configmap: %s", err)
		}
		mock.objects[rls.Name] = cfgmap
	}
}

func (mock *MockConfigMapsInterface) Get(name string) (*api.ConfigMap, error) {
	object, ok := mock.objects[name]
	if !ok {
		return nil, kberrs.NewNotFound(api.Resource("tests"), name)
	}
	return object, nil
}

func (mock *MockConfigMapsInterface) List(opts api.ListOptions) (*api.ConfigMapList, error) {
	var list api.ConfigMapList
	for _, cfgmap := range mock.objects {
		list.Items = append(list.Items, *cfgmap)
	}
	return &list, nil
}

func (mock *MockConfigMapsInterface) Create(cfgmap *api.ConfigMap) (*api.ConfigMap, error) {
	name := cfgmap.ObjectMeta.Name
	if object, ok := mock.objects[name]; ok {
		return object, kberrs.NewAlreadyExists(api.Resource("tests"), name)
	}
	mock.objects[name] = cfgmap
	return cfgmap, nil
}

func (mock *MockConfigMapsInterface) Update(cfgmap *api.ConfigMap) (*api.ConfigMap, error) {
	name := cfgmap.ObjectMeta.Name
	if _, ok := mock.objects[name]; !ok {
		return nil, kberrs.NewNotFound(api.Resource("tests"), name)
	}
	mock.objects[name] = cfgmap
	return cfgmap, nil
}

func (mock *MockConfigMapsInterface) Delete(name string) error {
	if _, ok := mock.objects[name]; !ok {
		return kberrs.NewNotFound(api.Resource("tests"), name)
	}
	delete(mock.objects, name)
	return nil
}
<|endoftext|>