<commit_before>package deploy

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/skatteetaten/ao/pkg/auroraconfig"
	"github.com/skatteetaten/ao/pkg/cmdoptions"
	"github.com/skatteetaten/ao/pkg/configuration"
	"github.com/skatteetaten/ao/pkg/executil"
	"github.com/skatteetaten/ao/pkg/jsonutil"
	"github.com/skatteetaten/ao/pkg/serverapi_v2"
)

const UsageString = "Usage: deploy <env> <app> <env/app> [--all] [--force] [-e env] [-a app] "

type DeployCommand struct {
	Affiliation string                      `json:"affiliation"`
	SetupParams jsonutil.SetupParamsPayload `json:"setupParams"`
}

type DeployClass struct {
	configuration configuration.ConfigurationClass
	setupCommand  DeployCommand
	appList       []string
	envList       []string
	legalAppList  []string
	legalEnvList  []string
	overrideJsons []string
}

func (deploy *DeployClass) init(persistentOptions *cmdoptions.CommonCommandOptions) (err error) {

	deploy.configuration.Init(persistentOptions)
	return
}

func (deploy *DeployClass) addLegalApp(app string) {
	for i := range deploy.legalAppList {
		if deploy.legalAppList[i] == app {
			return
		}
	}
	deploy.legalAppList = append(deploy.legalAppList, app)
	return
}

func (deploy *DeployClass) addLegalEnv(env string) {
	for i := range deploy.legalEnvList {
		if deploy.legalEnvList[i] == env {
			return
		}
	}
	deploy.legalEnvList = append(deploy.legalEnvList, env)
	return
}

func (deploy *DeployClass) generateJson(
	affiliation string, dryRun bool) (jsonStr string, err error) {

	if len(deploy.appList) != 0 {
		deploy.setupCommand.SetupParams.Apps = deploy.appList
	} else {
		deploy.setupCommand.SetupParams.Apps = make([]string, 0)
	}
	if len(deploy.envList) != 0 {
		deploy.setupCommand.SetupParams.Envs = deploy.envList
	} else {
		deploy.setupCommand.SetupParams.Envs = make([]string, 0)
	}

	//setupCommand.SetupParams.DryRun = dryRun
	deploy.setupCommand.SetupParams.Overrides, err = jsonutil.OverrideJsons2map(deploy.overrideJsons)
	if err != nil {
		return "", err
	}
	deploy.setupCommand.Affiliation = affiliation

	var jsonByte []byte

	jsonByte, err = json.Marshal(deploy.setupCommand)
	if !(err == nil) {
		return "", errors.New(fmt.Sprintf("Internal error in marshalling SetupCommand: %v\n", err.Error()))
	}

	jsonStr = string(jsonByte)
	return

}

func (deploy *DeployClass) ExecuteDeploy(args []string, overrideJsons []string, applist []string, envList []string,
	persistentOptions *cmdoptions.CommonCommandOptions, localDryRun bool, deployAll bool, force bool) (output string, err error) {

	deploy.init(persistentOptions)
	if !serverapi_v2.ValidateLogin(deploy.configuration.GetOpenshiftConfig()) {
		return "", errors.New("Not logged in, please use aoc login")
	}

	err = deploy.validateDeploy(args, applist, envList, deployAll, force)
	if err != nil {
		return "", err
	}

	var affiliation = deploy.configuration.GetAffiliation()
	json, err := deploy.generateJson(affiliation, persistentOptions.DryRun)
	if err != nil {
		return "", err
	}
	var apiEndpoint string = "/affiliation/" + affiliation + "/deploy"
	var responses map[string]string
	var applicationResults []serverapi_v2.ApplicationResult

	if localDryRun {
		return fmt.Sprintf("%v", string(jsonutil.PrettyPrintJson(json))), nil
	} else {
		responses, err = serverapi_v2.CallApi(http.MethodPut, apiEndpoint, json, persistentOptions.ShowConfig,
			persistentOptions.ShowObjects, false, persistentOptions.Localhost,
			persistentOptions.Verbose, deploy.configuration.GetOpenshiftConfig(), persistentOptions.DryRun, persistentOptions.Debug, persistentOptions.ServerApi, persistentOptions.Token)
		if err != nil {
			for server := range responses {
				response, err := serverapi_v2.ParseResponse(responses[server])
				if err != nil {
					return "", err
				}
				if !response.Success {
					output, err = serverapi_v2.ResponsItems2MessageString(response)
				}
			}
			return output, nil
		}
		for server := range responses {
			response, err := serverapi_v2.ParseResponse(responses[server])
			if err != nil {
				return "", err
			}
			if response.Success {
				applicationResults, err = serverapi_v2.ResponseItems2ApplicationResults(response)
			}
			for applicationResultIndex := range applicationResults {
				out, err := serverapi_v2.ApplicationResult2MessageString(applicationResults[applicationResultIndex])
				if err != nil {
					return out, err
				}
				output += out
			}
		}

	}

	return
}

func (deploy *DeployClass) getLegalEnvAppList() (err error) {

	auroraConfig, err := auroraconfig.GetAuroraConfig(&deploy.configuration)
	if err != nil {
		return err
	}
	for filename := range auroraConfig.Files {
		if strings.Contains(filename, "/") {
			// We have a full path name
			parts := strings.Split(filename, "/")
			deploy.addLegalEnv(parts[0])
			if !strings.Contains(parts[1], "about.json") {
				if strings.HasSuffix(parts[1], ".json") {
					deploy.addLegalApp(strings.TrimSuffix(parts[1], ".json"))
				}

			}
		}
	}

	return
}

// Try to match an argument with an app, returns "" if none found
func (deploy *DeployClass) getFuzzyApp(arg string) (app string, err error) {
	// First check for exact match
	for i := range deploy.legalAppList {
		if deploy.legalAppList[i] == arg {
			return arg, nil
		}
	}
	// No exact match found, look for an app name that contains the string
	for i := range deploy.legalAppList {
		if strings.Contains(deploy.legalAppList[i], arg) {
			if app != "" {
				err = errors.New(arg + ": Not a unique application identifier, matching " + app + " and " + deploy.legalAppList[i])
				return "", err
			}
			app = deploy.legalAppList[i]
		}
	}
	return app, nil
}

// Try to match an argument with an env, returns "" if none found
func (deploy *DeployClass) getFuzzyEnv(arg string) (env string, err error) {
	// First check for exact match
	for i := range deploy.legalEnvList {
		if deploy.legalEnvList[i] == arg {
			return arg, nil
		}
	}
	// No exact match found, look for an env name that contains the string
	for i := range deploy.legalEnvList {
		if strings.Contains(deploy.legalEnvList[i], arg) {
			if env != "" {
				err = errors.New(arg + ": Not a unique environment identifier, matching both " + env + " and " + deploy.legalEnvList[i])
				return "", err
			}
			env = deploy.legalEnvList[i]
		}
	}
	return env, nil
}

func (deploy *DeployClass) populateFuzzyEnvAppList(args []string) (err error) {

	for i := range args {
		var env string
		var app string

		if strings.Contains(args[i], "/") {
			parts := strings.Split(args[i], "/")
			env, err = deploy.getFuzzyEnv(parts[0])
			if err != nil {
				return err
			}
			app, err = deploy.getFuzzyApp(parts[1])
			if err != nil {
				return err
			}
		} else {
			env, err = deploy.getFuzzyEnv(args[i])
			if err != nil {
				return err
			}
			app, err = deploy.getFuzzyApp(args[i])
			if err != nil {
				return err
			}
			if env != "" && app != "" {
				err = errors.New(args[i] + ": Not a unique identifier, matching both environment " + env + " and application " + app)
				return err
			}
		}
		if env == "" && app == "" {
			// None found, return error
			err = errors.New(args[i] + ": not found")
			return err
		}
		if env != "" {
			deploy.envList = append(deploy.envList, env)
		}
		if app != "" {
			deploy.appList = append(deploy.appList, app)
		}

	}
	return
}

func (deploy *DeployClass) populateFlagsEnvAppList(appList []string, envList []string) (err error) {
	var env string
	var app string

	for i := range appList {
		app, err = deploy.getFuzzyApp(appList[i])
		if err != nil {
			return err
		}
		if app != "" {
			deploy.appList = append(deploy.appList, app)
		} else {
			err = errors.New(appList[i] + ": not found")
			return err
		}
	}

	for i := range envList {
		env, err = deploy.getFuzzyEnv(envList[i])
		if err != nil {
			return err
		}
		if env != "" {
			deploy.envList = append(deploy.envList, env)
		} else {
			err = errors.New(envList[i] + ": not found")
			return err
		}
	}

	return
}

func (deploy *DeployClass) populateAllAppForEnv(env string) (err error) {

	auroraConfig, err := auroraconfig.GetAuroraConfig(&deploy.configuration)
	if err != nil {
		return err
	}

	for filename := range auroraConfig.Files {
		if strings.Contains(filename, "/") {
			// We have a full path name
			parts := strings.Split(filename, "/")
			if parts[0] == env {
				if !strings.Contains(parts[1], "about.json") {
					if strings.HasSuffix(parts[1], ".json") {
						deploy.appList = append(deploy.appList, strings.TrimSuffix(parts[1], ".json"))
					}
				}
			}
		}
	}

	return
}

func (deploy *DeployClass) populateAll() {
	deploy.envList = deploy.legalEnvList
	deploy.appList = deploy.legalAppList
	return
}

func (deploy *DeployClass) validateDeploy(args []string, appList []string, envList []string, deployAll bool, force bool) (err error) {
	// We will accept a mixed list of apps, envs and env/app strings and parse them
	// Empty list is illegal

	if len(args) == 0 {
		if !deployAll {
			err = errors.New(UsageString)
			return err
		}
	}

	err = deploy.getLegalEnvAppList()
	if err != nil {
		return err
	}

	if deployAll {
		deploy.populateAll()
		if !force {

			response, err := executil.PromptYNC("This will deploy " + strconv.Itoa(len(deploy.appList)) + " applications in " + strconv.Itoa(len(deploy.envList)) + " environments.  Are you sure?")
			if err != nil {
				return err
			}
			if response != "Y" {
				err = errors.New("Operation cancelled by user")
				return err
			}

		}

	} else {
		err = deploy.populateFuzzyEnvAppList(args)
		if err != nil {
			return err
		}

		err = deploy.populateFlagsEnvAppList(appList, envList)
		if err != nil {
			return err
		}
	}

	if len(deploy.envList) > 0 && len(deploy.appList) == 0 {
		// User have specified one or more environments, but not an application list, so prefill it
		for i := range deploy.envList {
			err := deploy.populateAllAppForEnv(deploy.envList[i])
			if err != nil {
				return err
			}
		}
		response, err := executil.PromptYNC("This will deploy " + strconv.Itoa(len(deploy.appList)) + " applications in " + strconv.Itoa(len(deploy.envList)) + " environments.  Are you sure?")
		if err != nil {
			return err
		}
		if response != "Y" {
			err = errors.New("Operation cancelled by user")
			return err
		}
	}

	return
}
<commit_msg>Fixed missing processing of overrides in deploy command<commit_after>package deploy

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/skatteetaten/ao/pkg/auroraconfig"
	"github.com/skatteetaten/ao/pkg/cmdoptions"
	"github.com/skatteetaten/ao/pkg/configuration"
	"github.com/skatteetaten/ao/pkg/executil"
	"github.com/skatteetaten/ao/pkg/jsonutil"
	"github.com/skatteetaten/ao/pkg/serverapi_v2"
)

const UsageString = "Usage: deploy <env> <app> <env/app> [--all] [--force] [-e env] [-a app] "

type DeployCommand struct {
	Affiliation string                      `json:"affiliation"`
	SetupParams jsonutil.SetupParamsPayload `json:"setupParams"`
}

type DeployClass struct {
	configuration configuration.ConfigurationClass
	setupCommand  DeployCommand
	appList       []string
	envList       []string
	legalAppList  []string
	legalEnvList  []string
	overrideJsons []string
}

func (deploy *DeployClass) init(persistentOptions *cmdoptions.CommonCommandOptions) (err error) {

	deploy.configuration.Init(persistentOptions)
	return
}

func (deploy *DeployClass) addLegalApp(app string) {
	for i := range deploy.legalAppList {
		if deploy.legalAppList[i] == app {
			return
		}
	}
	deploy.legalAppList = append(deploy.legalAppList, app)
	return
}

func (deploy *DeployClass) addLegalEnv(env string) {
	for i := range deploy.legalEnvList {
		if deploy.legalEnvList[i] == env {
			return
		}
	}
	deploy.legalEnvList = append(deploy.legalEnvList, env)
	return
}

func (deploy *DeployClass) generateJson(
	affiliation string, dryRun bool) (jsonStr string, err error) {

	if len(deploy.appList) != 0 {
		deploy.setupCommand.SetupParams.Apps = deploy.appList
	} else {
		deploy.setupCommand.SetupParams.Apps = make([]string, 0)
	}
	if len(deploy.envList) != 0 {
		deploy.setupCommand.SetupParams.Envs = deploy.envList
	} else {
		deploy.setupCommand.SetupParams.Envs = make([]string, 0)
	}

	//setupCommand.SetupParams.DryRun = dryRun
	deploy.setupCommand.SetupParams.Overrides, err = jsonutil.OverrideJsons2map(deploy.overrideJsons)
	if err != nil {
		return "", err
	}
	deploy.setupCommand.Affiliation = affiliation

	var jsonByte []byte

	jsonByte, err = json.Marshal(deploy.setupCommand)
	if !(err == nil) {
		return "", errors.New(fmt.Sprintf("Internal error in marshalling SetupCommand: %v\n", err.Error()))
	}

	jsonStr = string(jsonByte)
	return

}

func (deploy *DeployClass) ExecuteDeploy(args []string, overrideJsons []string, applist []string, envList []string,
	persistentOptions *cmdoptions.CommonCommandOptions, localDryRun bool, deployAll bool, force bool) (output string, err error) {

	deploy.init(persistentOptions)
	if !serverapi_v2.ValidateLogin(deploy.configuration.GetOpenshiftConfig()) {
		return "", errors.New("Not logged in, please use aoc login")
	}

	err = deploy.validateDeploy(args, applist, envList, deployAll, force)
	if err != nil {
		return "", err
	}

	deploy.overrideJsons = overrideJsons

	var affiliation = deploy.configuration.GetAffiliation()
	json, err := deploy.generateJson(affiliation, persistentOptions.DryRun)
	if err != nil {
		return "", err
	}
	var apiEndpoint string = "/affiliation/" + affiliation + "/deploy"
	var responses map[string]string
	var applicationResults []serverapi_v2.ApplicationResult

	if localDryRun {
		return fmt.Sprintf("%v", string(jsonutil.PrettyPrintJson(json))), nil
	} else {
		responses, err = serverapi_v2.CallApi(http.MethodPut, apiEndpoint, json, persistentOptions.ShowConfig,
			persistentOptions.ShowObjects, false, persistentOptions.Localhost,
			persistentOptions.Verbose, deploy.configuration.GetOpenshiftConfig(), persistentOptions.DryRun, persistentOptions.Debug, persistentOptions.ServerApi, persistentOptions.Token)
		if err != nil {
			for server := range responses {
				response, err := serverapi_v2.ParseResponse(responses[server])
				if err != nil {
					return "", err
				}
				if !response.Success {
					output, err = serverapi_v2.ResponsItems2MessageString(response)
				}
			}
			return output, nil
		}
		for server := range responses {
			response, err := serverapi_v2.ParseResponse(responses[server])
			if err != nil {
				return "", err
			}
			if response.Success {
				applicationResults, err = serverapi_v2.ResponseItems2ApplicationResults(response)
			}
			for applicationResultIndex := range applicationResults {
				out, err := serverapi_v2.ApplicationResult2MessageString(applicationResults[applicationResultIndex])
				if err != nil {
					return out, err
				}
				output += out
			}
		}

	}

	return
}

func (deploy *DeployClass) getLegalEnvAppList() (err error) {

	auroraConfig, err := auroraconfig.GetAuroraConfig(&deploy.configuration)
	if err != nil {
		return err
	}
	for filename := range auroraConfig.Files {
		if strings.Contains(filename, "/") {
			// We have a full path name
			parts := strings.Split(filename, "/")
			deploy.addLegalEnv(parts[0])
			if !strings.Contains(parts[1], "about.json") {
				if strings.HasSuffix(parts[1], ".json") {
					deploy.addLegalApp(strings.TrimSuffix(parts[1], ".json"))
				}

			}
		}
	}

	return
}

// Try to match an argument with an app, returns "" if none found
func (deploy *DeployClass) getFuzzyApp(arg string) (app string, err error) {
	// First check for exact match
	for i := range deploy.legalAppList {
		if deploy.legalAppList[i] == arg {
			return arg, nil
		}
	}
	// No exact match found, look for an app name that contains the string
	for i := range deploy.legalAppList {
		if strings.Contains(deploy.legalAppList[i], arg) {
			if app != "" {
				err = errors.New(arg + ": Not a unique application identifier, matching " + app + " and " + deploy.legalAppList[i])
				return "", err
			}
			app = deploy.legalAppList[i]
		}
	}
	return app, nil
}

// Try to match an argument with an env, returns "" if none found
func (deploy *DeployClass) getFuzzyEnv(arg string) (env string, err error) {
	// First check for exact match
	for i := range deploy.legalEnvList {
		if deploy.legalEnvList[i] == arg {
			return arg, nil
		}
	}
	// No exact match found, look for an env name that contains the string
	for i := range deploy.legalEnvList {
		if strings.Contains(deploy.legalEnvList[i], arg) {
			if env != "" {
				err = errors.New(arg + ": Not a unique environment identifier, matching both " + env + " and " + deploy.legalEnvList[i])
				return "", err
			}
			env = deploy.legalEnvList[i]
		}
	}
	return env, nil
}

func (deploy *DeployClass) populateFuzzyEnvAppList(args []string) (err error) {

	for i := range args {
		var env string
		var app string

		if strings.Contains(args[i], "/") {
			parts := strings.Split(args[i], "/")
			env, err = deploy.getFuzzyEnv(parts[0])
			if err != nil {
				return err
			}
			app, err = deploy.getFuzzyApp(parts[1])
			if err != nil {
				return err
			}
		} else {
			env, err = deploy.getFuzzyEnv(args[i])
			if err != nil {
				return err
			}
			app, err = deploy.getFuzzyApp(args[i])
			if err != nil {
				return err
			}
			if env != "" && app != "" {
				err = errors.New(args[i] + ": Not a unique identifier, matching both environment " + env + " and application " + app)
				return err
			}
		}
		if env == "" && app == "" {
			// None found, return error
			err = errors.New(args[i] + ": not found")
			return err
		}
		if env != "" {
			deploy.envList = append(deploy.envList, env)
		}
		if app != "" {
			deploy.appList = append(deploy.appList, app)
		}

	}
	return
}

func (deploy *DeployClass) populateFlagsEnvAppList(appList []string, envList []string) (err error) {
	var env string
	var app string

	for i := range appList {
		app, err = deploy.getFuzzyApp(appList[i])
		if err != nil {
			return err
		}
		if app != "" {
			deploy.appList = append(deploy.appList, app)
		} else {
			err = errors.New(appList[i] + ": not found")
			return err
		}
	}

	for i := range envList {
		env, err = deploy.getFuzzyEnv(envList[i])
		if err != nil {
			return err
		}
		if env != "" {
			deploy.envList = append(deploy.envList, env)
		} else {
			err = errors.New(envList[i] + ": not found")
			return err
		}
	}

	return
}

func (deploy *DeployClass) populateAllAppForEnv(env string) (err error) {

	auroraConfig, err := auroraconfig.GetAuroraConfig(&deploy.configuration)
	if err != nil {
		return err
	}

	for filename := range auroraConfig.Files {
		if strings.Contains(filename, "/") {
			// We have a full path name
			parts := strings.Split(filename, "/")
			if parts[0] == env {
				if !strings.Contains(parts[1], "about.json") {
					if strings.HasSuffix(parts[1], ".json") {
						deploy.appList = append(deploy.appList, strings.TrimSuffix(parts[1], ".json"))
					}
				}
			}
		}
	}

	return
}

func (deploy *DeployClass) populateAll() {
	deploy.envList = deploy.legalEnvList
	deploy.appList = deploy.legalAppList
	return
}

func (deploy *DeployClass) validateDeploy(args []string, appList []string, envList []string, deployAll bool, force bool) (err error) {
	// We will accept a mixed list of apps, envs and env/app strings and parse them
	// Empty list is illegal

	if len(args) == 0 {
		if !deployAll {
			err = errors.New(UsageString)
			return err
		}
	}

	err = deploy.getLegalEnvAppList()
	if err != nil {
		return err
	}

	if deployAll {
		deploy.populateAll()
		if !force {

			response, err := executil.PromptYNC("This will deploy " + strconv.Itoa(len(deploy.appList)) + " applications in " + strconv.Itoa(len(deploy.envList)) + " environments.  Are you sure?")
			if err != nil {
				return err
			}
			if response != "Y" {
				err = errors.New("Operation cancelled by user")
				return err
			}

		}

	} else {
		err = deploy.populateFuzzyEnvAppList(args)
		if err != nil {
			return err
		}

		err = deploy.populateFlagsEnvAppList(appList, envList)
		if err != nil {
			return err
		}
	}

	if len(deploy.envList) > 0 && len(deploy.appList) == 0 {
		// User have specified one or more environments, but not an application list, so prefill it
		for i := range deploy.envList {
			err := deploy.populateAllAppForEnv(deploy.envList[i])
			if err != nil {
				return err
			}
		}
		response, err := executil.PromptYNC("This will deploy " + strconv.Itoa(len(deploy.appList)) + " applications in " + strconv.Itoa(len(deploy.envList)) + " environments.  Are you sure?")
		if err != nil {
			return err
		}
		if response != "Y" {
			err = errors.New("Operation cancelled by user")
			return err
		}
	}

	return
}
<|endoftext|>


<commit_before>package engine

import (
	"github.com/Aptomi/aptomi/pkg/lang"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"time"
)

// PolicyDataObject is an informational data structure with Kind and Constructor for PolicyData
var PolicyDataObject = &runtime.Info{
	Kind:        "policy",
	Storable:    true,
	Versioned:   true,
	Constructor: func() runtime.Object { return &PolicyData{} },
}

// PolicyDataKey is the default key for the policy object (there is only one policy exists but with multiple generations)
var PolicyDataKey = runtime.KeyFromParts(runtime.SystemNS, PolicyDataObject.Kind, runtime.EmptyName)

// PolicyData is a struct which contains references to a generation for each object included into the policy
type PolicyData struct {
	runtime.TypeKind `yaml:",inline"`
	Metadata         PolicyDataMetadata

	// Objects stores all policy objects in map: namespace -> kind -> name -> generation
	Objects map[string]map[string]map[string]runtime.Generation
}

// PolicyDataMetadata is the metadata for PolicyData object that includes generation
type PolicyDataMetadata struct {
	Generation runtime.Generation
	UpdatedAt  time.Time
	UpdatedBy  string
}

// GetName returns PolicyData name
func (policyData *PolicyData) GetName() string {
	return runtime.EmptyName
}

// GetNamespace returns PolicyData namespace
func (policyData *PolicyData) GetNamespace() string {
	return runtime.SystemNS
}

// GetGeneration returns PolicyData generation
func (policyData *PolicyData) GetGeneration() runtime.Generation {
	return policyData.Metadata.Generation
}

// SetGeneration sets PolicyData generation
func (policyData *PolicyData) SetGeneration(gen runtime.Generation) {
	policyData.Metadata.Generation = gen
}

// Add adds an object to PolicyData
func (policyData *PolicyData) Add(obj lang.Base) {
	byNs, exist := policyData.Objects[obj.GetNamespace()]
	if !exist {
		byNs = make(map[string]map[string]runtime.Generation)
		policyData.Objects[obj.GetNamespace()] = byNs
	}
	byKind, exist := byNs[obj.GetKind()]
	if !exist {
		byKind = make(map[string]runtime.Generation)
		byNs[obj.GetKind()] = byKind
	}
	byKind[obj.GetName()] = obj.GetGeneration()
}

func (policyData *PolicyData) Remove(obj lang.Base) bool {
	byNs, exist := policyData.Objects[obj.GetNamespace()]
	if !exist {
		return false
	}
	byKind, exist := byNs[obj.GetKind()]
	if !exist {
		return false
	}
	_, exist = byKind[obj.GetName()]
	delete(byKind, obj.GetName())

	return exist
}
<commit_msg>fixed linter<commit_after>package engine

import (
	"github.com/Aptomi/aptomi/pkg/lang"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"time"
)

// PolicyDataObject is an informational data structure with Kind and Constructor for PolicyData
var PolicyDataObject = &runtime.Info{
	Kind:        "policy",
	Storable:    true,
	Versioned:   true,
	Constructor: func() runtime.Object { return &PolicyData{} },
}

// PolicyDataKey is the default key for the policy object (there is only one policy exists but with multiple generations)
var PolicyDataKey = runtime.KeyFromParts(runtime.SystemNS, PolicyDataObject.Kind, runtime.EmptyName)

// PolicyData is a struct which contains references to a generation for each object included into the policy
type PolicyData struct {
	runtime.TypeKind `yaml:",inline"`
	Metadata         PolicyDataMetadata

	// Objects stores all policy objects in map: namespace -> kind -> name -> generation
	Objects map[string]map[string]map[string]runtime.Generation
}

// PolicyDataMetadata is the metadata for PolicyData object that includes generation
type PolicyDataMetadata struct {
	Generation runtime.Generation
	UpdatedAt  time.Time
	UpdatedBy  string
}

// GetName returns PolicyData name
func (policyData *PolicyData) GetName() string {
	return runtime.EmptyName
}

// GetNamespace returns PolicyData namespace
func (policyData *PolicyData) GetNamespace() string {
	return runtime.SystemNS
}

// GetGeneration returns PolicyData generation
func (policyData *PolicyData) GetGeneration() runtime.Generation {
	return policyData.Metadata.Generation
}

// SetGeneration sets PolicyData generation
func (policyData *PolicyData) SetGeneration(gen runtime.Generation) {
	policyData.Metadata.Generation = gen
}

// Add adds an object to PolicyData
func (policyData *PolicyData) Add(obj lang.Base) {
	byNs, exist := policyData.Objects[obj.GetNamespace()]
	if !exist {
		byNs = make(map[string]map[string]runtime.Generation)
		policyData.Objects[obj.GetNamespace()] = byNs
	}
	byKind, exist := byNs[obj.GetKind()]
	if !exist {
		byKind = make(map[string]runtime.Generation)
		byNs[obj.GetKind()] = byKind
	}
	byKind[obj.GetName()] = obj.GetGeneration()
}

// Remove deletes an object from PolicyData
func (policyData *PolicyData) Remove(obj lang.Base) bool { // nolint: interfacer
	byNs, exist := policyData.Objects[obj.GetNamespace()]
	if !exist {
		return false
	}
	byKind, exist := byNs[obj.GetKind()]
	if !exist {
		return false
	}
	_, exist = byKind[obj.GetName()]
	delete(byKind, obj.GetName())

	return exist
}
<|endoftext|>


<commit_before>package http

import (
	"encoding/asn1"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/nalind/gss/pkg/gss"
)

type NegotiateRoundTripper struct {
	Transport http.RoundTripper
	Flags     gss.Flags
	Mech      asn1.ObjectIdentifier
}

func NewNegotiateRoundTripper(rt http.RoundTripper) http.RoundTripper {
	return &NegotiateRoundTripper{Transport: rt}
}

func (rt *NegotiateRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req)

	resp, err := rt.Transport.RoundTrip(req)
	if err != nil {
		return resp, err
	}

	// TODO: handle multiple WWW-Authenticate headers, or a single header containing multiple challenges

	// enter the negotiate flow
	if isInitialChallenge(resp) {
		// fmt.Println("RoundTrip: Started negotiate loop")

		name, err := importName(req.Host)
		if err != nil {
			return nil, err
		}
		defer gss.ReleaseName(name)

		var ctx gss.ContextHandle
		defer gss.DeleteSecContext(ctx)

		var major, minor uint32

		// Local copy of flags
		flags := rt.Flags

		// TODO: guard against infinite loops, set some max

		// Loop as long as we get back negotiate challenges, or we don't think we've completed the auth
		for i := 0; isNegotiateResponse(resp) || major != gss.S_COMPLETE; i++ {
			// fmt.Printf("RoundTrip: Got Status=%v, WWW-Authenticate=%#v\n", resp.StatusCode, resp.Header.Get("WWW-Authenticate"))

			// require incoming token for continued responses
			var incomingToken []byte
			if i > 0 {
				incomingToken, err = gssapiData(resp)
				if err != nil {
					return nil, err
				}
				if len(incomingToken) == 0 {
					return nil, errors.New("Continued server response was missing gssapi-data")
				}
			}

			// call gss_init_sec_context to validate the incoming token (if given), and get our outgoing token (if needed)
			var outgoingToken []byte
			major, minor, _, outgoingToken, flags, _, _, _ = gss.InitSecContext(nil, &ctx, name, rt.Mech, flags, gss.C_INDEFINITE, nil, incomingToken)
			if major != gss.S_COMPLETE && major != gss.S_CONTINUE_NEEDED {
				return nil, gss.NewGSSError(fmt.Sprintf("initializing security context (step %d)", i+1), major, minor, &rt.Mech)
			}

			// fmt.Printf("Complete: %v, Continue: %v\n", major == gss.S_COMPLETE, major == gss.S_CONTINUE_NEEDED)

			// the remote server is unhappy, or we don't think we've finished the auth
			if resp.StatusCode == http.StatusUnauthorized || major == gss.S_CONTINUE_NEEDED {
				// retry the request with our new token, and restart the loop
				outgoingTokenBase64 := base64.StdEncoding.EncodeToString(outgoingToken)
				// fmt.Println("Re-sending request with Authorization token")
				req.Header.Set("Authorization", "Negotiate "+outgoingTokenBase64)
				resp, err = rt.Transport.RoundTrip(req)
				if err != nil {
					return nil, err
				}
			} else {
				return resp, nil
			}
		}
	}
	return resp, nil
}

func isInitialChallenge(resp *http.Response) bool {
	return resp.StatusCode == http.StatusUnauthorized && resp.Header.Get("WWW-Authenticate") == "Negotiate"
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

// isNegotiateResponse returns true if the response contains a WWW-Authenticate header with a Negotiate challenge
func isNegotiateResponse(resp *http.Response) bool {
	return resp.Header.Get("WWW-Authenticate") == "Negotiate" || strings.HasPrefix(resp.Header.Get("WWW-Authenticate"), "Negotiate ")
}

// gssapiData returns base64-decoded gssapi-data in any Negotiate challenge header
// An empty string is returned if no Negotiate challenge header is present, or if no
// gssapi-data is present. An error is returned if malformed gssapi-data is present.
func gssapiData(resp *http.Response) ([]byte, error) {
	authHeader := resp.Header.Get("WWW-Authenticate")

	parts := strings.SplitN(authHeader, " ", 2)
	if len(parts) < 2 || parts[0] != "Negotiate" {
		return nil, nil
	}

	// Remove whitespace
	gssapiData := strings.Replace(parts[1], " ", "", -1)

	// Decode
	decodedData, err := base64.StdEncoding.DecodeString(gssapiData)
	if err != nil {
		return nil, err
	}

	return decodedData, nil
}

// importName returns a gss.InternalName for a given hostname, or an error.
// The caller is responsible for releasing the returned name using gss.ReleaseName.
func importName(hostname string) (gss.InternalName, error) {
	major, minor, name := gss.ImportName("HTTP@"+hostname, gss.C_NT_HOSTBASED_SERVICE)
	if major != gss.S_COMPLETE {
		return nil, gss.NewGSSError("importing remote service name", major, minor, nil)
	}
	return name, nil
}
<commit_msg>Export ImportName<commit_after>package http

import (
	"encoding/asn1"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strings"

	"github.com/nalind/gss/pkg/gss"
)

type NegotiateRoundTripper struct {
	Transport http.RoundTripper
	Flags     gss.Flags
	Mech      asn1.ObjectIdentifier
}

func NewNegotiateRoundTripper(rt http.RoundTripper) http.RoundTripper {
	return &NegotiateRoundTripper{Transport: rt}
}

func (rt *NegotiateRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
	req = cloneRequest(req)

	resp, err := rt.Transport.RoundTrip(req)
	if err != nil {
		return resp, err
	}

	// TODO: handle multiple WWW-Authenticate headers, or a single header containing multiple challenges

	// enter the negotiate flow
	if isInitialChallenge(resp) {
		// fmt.Println("RoundTrip: Started negotiate loop")

		name, err := ImportName(req.Host)
		if err != nil {
			return nil, err
		}
		defer gss.ReleaseName(name)

		var ctx gss.ContextHandle
		defer gss.DeleteSecContext(ctx)

		var major, minor uint32

		// Local copy of flags
		flags := rt.Flags

		// TODO: guard against infinite loops, set some max

		// Loop as long as we get back negotiate challenges, or we don't think we've completed the auth
		for i := 0; isNegotiateResponse(resp) || major != gss.S_COMPLETE; i++ {
			// fmt.Printf("RoundTrip: Got Status=%v, WWW-Authenticate=%#v\n", resp.StatusCode, resp.Header.Get("WWW-Authenticate"))

			// require incoming token for continued responses
			var incomingToken []byte
			if i > 0 {
				incomingToken, err = gssapiData(resp)
				if err != nil {
					return nil, err
				}
				if len(incomingToken) == 0 {
					return nil, errors.New("Continued server response was missing gssapi-data")
				}
			}

			// call gss_init_sec_context to validate the incoming token (if given), and get our outgoing token (if needed)
			var outgoingToken []byte
			major, minor, _, outgoingToken, flags, _, _, _ = gss.InitSecContext(nil, &ctx, name, rt.Mech, flags, gss.C_INDEFINITE, nil, incomingToken)
			if major != gss.S_COMPLETE && major != gss.S_CONTINUE_NEEDED {
				return nil, gss.NewGSSError(fmt.Sprintf("initializing security context (step %d)", i+1), major, minor, &rt.Mech)
			}

			// fmt.Printf("Complete: %v, Continue: %v\n", major == gss.S_COMPLETE, major == gss.S_CONTINUE_NEEDED)

			// the remote server is unhappy, or we don't think we've finished the auth
			if resp.StatusCode == http.StatusUnauthorized || major == gss.S_CONTINUE_NEEDED {
				// retry the request with our new token, and restart the loop
				outgoingTokenBase64 := base64.StdEncoding.EncodeToString(outgoingToken)
				// fmt.Println("Re-sending request with Authorization token")
				req.Header.Set("Authorization", "Negotiate "+outgoingTokenBase64)
				resp, err = rt.Transport.RoundTrip(req)
				if err != nil {
					return nil, err
				}
			} else {
				return resp, nil
			}
		}
	}
	return resp, nil
}

func isInitialChallenge(resp *http.Response) bool {
	return resp.StatusCode == http.StatusUnauthorized && resp.Header.Get("WWW-Authenticate") == "Negotiate"
}

// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
	// shallow copy of the struct
	r2 := new(http.Request)
	*r2 = *r
	// deep copy of the Header
	r2.Header = make(http.Header)
	for k, s := range r.Header {
		r2.Header[k] = s
	}
	return r2
}

// isNegotiateResponse returns true if the response contains a WWW-Authenticate header with a Negotiate challenge
func isNegotiateResponse(resp *http.Response) bool {
	return resp.Header.Get("WWW-Authenticate") == "Negotiate" || strings.HasPrefix(resp.Header.Get("WWW-Authenticate"), "Negotiate ")
}

// gssapiData returns base64-decoded gssapi-data in any Negotiate challenge header
// An empty string is returned if no Negotiate challenge header is present, or if no
// gssapi-data is present. An error is returned if malformed gssapi-data is present.
func gssapiData(resp *http.Response) ([]byte, error) {
	authHeader := resp.Header.Get("WWW-Authenticate")

	parts := strings.SplitN(authHeader, " ", 2)
	if len(parts) < 2 || parts[0] != "Negotiate" {
		return nil, nil
	}

	// Remove whitespace
	gssapiData := strings.Replace(parts[1], " ", "", -1)

	// Decode
	decodedData, err := base64.StdEncoding.DecodeString(gssapiData)
	if err != nil {
		return nil, err
	}

	return decodedData, nil
}

// ImportName returns a gss.InternalName for a given hostname, or an error.
// The caller is responsible for releasing the returned name using gss.ReleaseName.
func ImportName(hostname string) (gss.InternalName, error) {
	major, minor, name := gss.ImportName("HTTP@"+hostname, gss.C_NT_HOSTBASED_SERVICE)
	if major != gss.S_COMPLETE {
		return nil, gss.NewGSSError("importing remote service name", major, minor, nil)
	}
	return name, nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package present

import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/url"
	"regexp"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)

var (
	parsers = make(map[string]func(string, int, string) (Elem, error))
	funcs   = template.FuncMap{}
)

// Template returns an empty template with the action functions in its FuncMap.
func Template() *template.Template {
	return template.New("").Funcs(funcs)
}

// Render renders the doc to the given writer using the provided template.
func (d *Doc) Render(w io.Writer, t *template.Template) error {
	data := struct {
		*Doc
		Template    *template.Template
		PlayEnabled bool
	}{d, t, PlayEnabled}
	return t.ExecuteTemplate(w, "root", data)
}

type ParseFunc func(fileName string, lineNumber int, inputLine string) (Elem, error)

// Register binds the named action, which does not begin with a period, to the
// specified parser to be invoked when the name, with a period, appears in the
// present input text.
func Register(name string, parser ParseFunc) {
	if len(name) == 0 || name[0] == ';' {
		panic("bad name in Register: " + name)
	}
	parsers["."+name] = parser
}

// Doc represents an entire document.
type Doc struct {
	Title    string
	Subtitle string
	Time     time.Time
	Authors  []Author
	Sections []Section
	Tags     []string
}

// Author represents the person who wrote and/or is presenting the document.
type Author struct {
	Elem []Elem
}

// TextElem returns the first text elements of the author details.
// This is used to display the author' name, job title, and company
// without the contact details.
func (p *Author) TextElem() (elems []Elem) {
	for _, el := range p.Elem {
		if _, ok := el.(Text); !ok {
			break
		}
		elems = append(elems, el)
	}
	return
}

// Section represents a section of a document (such as a presentation slide)
// comprising a title and a list of elements.
type Section struct {
	Number []int
	Title  string
	Elem   []Elem
}

func (s Section) Sections() (sections []Section) {
	for _, e := range s.Elem {
		if s, ok := e.(Section); ok {
			sections = append(sections, s)
		}
	}
	return
}

// Level returns the level of the given section.
// The document title is level 1, main section 2, etc.
func (s Section) Level() int {
	return len(s.Number) + 1
}

// FormattedNumber returns a string containing the concatenation of the
// numbers identifying a Section.
func (s Section) FormattedNumber() string {
	b := &bytes.Buffer{}
	for _, n := range s.Number {
		fmt.Fprintf(b, "%v.", n)
	}
	return b.String()
}

func (s Section) TemplateName() string { return "section" }

// Elem defines the interface for a present element. That is, something that
// can provide the name of the template used to render the element.
type Elem interface {
	TemplateName() string
}

// renderElem implements the elem template function, used to render
// sub-templates.
func renderElem(t *template.Template, e Elem) (template.HTML, error) {
	var data interface{} = e
	if s, ok := e.(Section); ok {
		data = struct {
			Section
			Template *template.Template
		}{s, t}
	}
	return execTemplate(t, e.TemplateName(), data)
}

func init() {
	funcs["elem"] = renderElem
}

// execTemplate is a helper to execute a template and return the output as a
// template.HTML value.
func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) {
	b := new(bytes.Buffer)
	err := t.ExecuteTemplate(b, name, data)
	if err != nil {
		return "", err
	}
	return template.HTML(b.String()), nil
}

// Text represents an optionally preformatted paragraph.
type Text struct {
	Lines []string
	Pre   bool
}

func (t Text) TemplateName() string { return "text" }

// List represents a bulleted list.
type List struct {
	Bullet []string
}

func (l List) TemplateName() string { return "list" }

// Lines is a helper for parsing line-based input.
type Lines struct {
	line int // 0 indexed, so has 1-indexed number of last line returned
	text []string
}

func readLines(r io.Reader) (*Lines, error) {
	contentBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	return &Lines{0, strings.Split(string(contentBytes), "\n")}, nil
}

func (l *Lines) next() (text string, ok bool) {
	for {
		current := l.line
		l.line++
		if current >= len(l.text) {
			return "", false
		}
		text = l.text[current]
		// Lines starting with # are comments.
		if len(text) == 0 || text[0] != '#' {
			ok = true
			break
		}
	}
	return
}

func (l *Lines) back() {
	l.line--
}

func (l *Lines) nextNonEmpty() (text string, ok bool) {
	for {
		text, ok = l.next()
		if !ok {
			return
		}
		if len(text) > 0 {
			break
		}
	}
	return
}

// ParseMode represents flags for the Parse function.
type ParseMode int

const (
	// If set, parse only the title and subtitle.
	TitlesOnly ParseMode = 1
)

// Parse parses the document in the file specified by name.
func Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) {
	doc := new(Doc)
	lines, err := readLines(r)
	if err != nil {
		return nil, err
	}
	err = parseHeader(doc, lines)
	if err != nil {
		return nil, err
	}
	if mode&TitlesOnly != 0 {
		return doc, nil
	}
	// Authors
	if doc.Authors, err = parseAuthors(lines); err != nil {
		return nil, err
	}
	// Sections
	if doc.Sections, err = parseSections(name, lines, []int{}, doc); err != nil {
		return nil, err
	}
	return doc, nil
}

// isHeading matches any section heading.
var isHeading = regexp.MustCompile(`^\*+ `)

// lesserHeading returns true if text is a heading of a lesser or equal level
// than that denoted by prefix.
func lesserHeading(text, prefix string) bool {
	return isHeading.MatchString(text) && !strings.HasPrefix(text, prefix+"*")
}

// parseSections parses Sections from lines for the section level indicated by
// number (a nil number indicates the top level).
func parseSections(name string, lines *Lines, number []int, doc *Doc) ([]Section, error) {
	var sections []Section
	for i := 1; ; i++ {
		// Next non-empty line is title.
		text, ok := lines.nextNonEmpty()
		for ok && text == "" {
			text, ok = lines.next()
		}
		if !ok {
			break
		}
		prefix := strings.Repeat("*", len(number)+1)
		if !strings.HasPrefix(text, prefix+" ") {
			lines.back()
			break
		}
		section := Section{
			Number: append(append([]int{}, number...), i),
			Title:  text[len(prefix)+1:],
		}
		text, ok = lines.nextNonEmpty()
		for ok && !lesserHeading(text, prefix) {
			var e Elem
			r, _ := utf8.DecodeRuneInString(text)
			switch {
			case unicode.IsSpace(r):
				i := strings.IndexFunc(text, func(r rune) bool {
					return !unicode.IsSpace(r)
				})
				if i < 0 {
					break
				}
				indent := text[:i]
				var s []string
				for ok && (strings.HasPrefix(text, indent) || text == "") {
					if text != "" {
						text = text[i:]
					}
					s = append(s, text)
					text, ok = lines.next()
				}
				lines.back()
				pre := strings.Join(s, "\n")
				pre = strings.TrimRightFunc(pre, unicode.IsSpace)
				e = Text{Lines: []string{pre}, Pre: true}
			case strings.HasPrefix(text, "- "):
				var b []string
				for ok && strings.HasPrefix(text, "- ") {
					b = append(b, text[2:])
					text, ok = lines.next()
				}
				lines.back()
				e = List{Bullet: b}
			case strings.HasPrefix(text, prefix+"* "):
				lines.back()
				subsecs, err := parseSections(name, lines, section.Number, doc)
				if err != nil {
					return nil, err
				}
				for _, ss := range subsecs {
					section.Elem = append(section.Elem, ss)
				}
			case strings.HasPrefix(text, "."):
				args := strings.Fields(text)
				parser := parsers[args[0]]
				if parser == nil {
					return nil, fmt.Errorf("%s:%d: unknown command %q\n", name, lines.line, text)
				}
				t, err := parser(name, lines.line, text)
				if err != nil {
					return nil, err
				}
				e = t
			default:
				var l []string
				for ok && strings.TrimSpace(text) != "" {
					if text[0] == '.' { // Command breaks text block.
						break
					}
					if strings.HasPrefix(text, `\.`) { // Backslash escapes initial period.
						text = text[1:]
					}
					l = append(l, text)
					text, ok = lines.next()
				}
				if len(l) > 0 {
					e = Text{Lines: l}
				}
			}
			if e != nil {
				section.Elem = append(section.Elem, e)
			}
			text, ok = lines.nextNonEmpty()
		}
		if isHeading.MatchString(text) {
			lines.back()
		}
		sections = append(sections, section)
	}
	return sections, nil
}

func parseHeader(doc *Doc, lines *Lines) error {
	var ok bool
	// First non-empty line starts header.
	doc.Title, ok = lines.nextNonEmpty()
	if !ok {
		return errors.New("unexpected EOF; expected title")
	}
	for {
		text, ok := lines.next()
		if !ok {
			return errors.New("unexpected EOF")
		}
		if text == "" {
			break
		}
		const tagPrefix = "Tags:"
		if strings.HasPrefix(text, tagPrefix) {
			tags := strings.Split(text[len(tagPrefix):], ",")
			for i := range tags {
				tags[i] = strings.TrimSpace(tags[i])
			}
			doc.Tags = append(doc.Tags, tags...)
		} else if t, ok := parseTime(text); ok {
			doc.Time = t
		} else if doc.Subtitle == "" {
			doc.Subtitle = text
		} else {
			return fmt.Errorf("unexpected header line: %q", text)
		}
	}
	return nil
}

func parseAuthors(lines *Lines) (authors []Author, err error) {
	// This grammar demarcates authors with blanks.

	// Skip blank lines.
	if _, ok := lines.nextNonEmpty(); !ok {
		return nil, errors.New("unexpected EOF")
	}
	lines.back()

	var a *Author
	for {
		text, ok := lines.next()
		if !ok {
			return nil, errors.New("unexpected EOF")
		}

		// If we find a section heading, we're done.
		if strings.HasPrefix(text, "* ") {
			lines.back()
			break
		}

		// If we encounter a blank we're done with this author.
		if a != nil && len(text) == 0 {
			authors = append(authors, *a)
			a = nil
			continue
		}
		if a == nil {
			a = new(Author)
		}

		// Parse the line. Those that
		// - begin with @ are twitter names,
		// - contain slashes are links, or
		// - contain an @ symbol are an email address.
		// The rest is just text.
		var el Elem
		switch {
		case strings.HasPrefix(text, "@"):
			el = parseURL("http://twitter.com/" + text[1:])
			if l, ok := el.(Link); ok {
				l.Label = text
				el = l
			}
		case strings.Contains(text, ":"):
			el = parseURL(text)
		case strings.Contains(text, "@"):
			el = parseURL("mailto:" + text)
		}
		if el == nil {
			el = Text{Lines: []string{text}}
		}
		a.Elem = append(a.Elem, el)
	}
	if a != nil {
		authors = append(authors, *a)
	}
	return authors, nil
}

func parseURL(text string) Elem {
	u, err := url.Parse(text)
	if err != nil {
		log.Printf("Parse(%q): %v", text, err)
		return nil
	}
	return Link{URL: u}
}

func parseTime(text string) (t time.Time, ok bool) {
	t, err := time.Parse("15:04 2 Jan 2006", text)
	if err == nil {
		return t, true
	}
	t, err = time.Parse("2 Jan 2006", text)
	if err == nil {
		// at 11am UTC it is the same date everywhere
		t = t.Add(time.Hour * 11)
		return t, true
	}
	return time.Time{}, false
}
<commit_msg>go.talks/pkg/present: substitute tabs with 4 spaces in preformatted blocks<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package present

import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/url"
	"regexp"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)

var (
	parsers = make(map[string]func(string, int, string) (Elem, error))
	funcs   = template.FuncMap{}
)

// Template returns an empty template with the action functions in its FuncMap.
func Template() *template.Template {
	return template.New("").Funcs(funcs)
}

// Render renders the doc to the given writer using the provided template.
func (d *Doc) Render(w io.Writer, t *template.Template) error {
	data := struct {
		*Doc
		Template    *template.Template
		PlayEnabled bool
	}{d, t, PlayEnabled}
	return t.ExecuteTemplate(w, "root", data)
}

type ParseFunc func(fileName string, lineNumber int, inputLine string) (Elem, error)

// Register binds the named action, which does not begin with a period, to the
// specified parser to be invoked when the name, with a period, appears in the
// present input text.
func Register(name string, parser ParseFunc) {
	if len(name) == 0 || name[0] == ';' {
		panic("bad name in Register: " + name)
	}
	parsers["."+name] = parser
}

// Doc represents an entire document.
type Doc struct {
	Title    string
	Subtitle string
	Time     time.Time
	Authors  []Author
	Sections []Section
	Tags     []string
}

// Author represents the person who wrote and/or is presenting the document.
type Author struct {
	Elem []Elem
}

// TextElem returns the first text elements of the author details.
// This is used to display the author' name, job title, and company
// without the contact details.
func (p *Author) TextElem() (elems []Elem) {
	for _, el := range p.Elem {
		if _, ok := el.(Text); !ok {
			break
		}
		elems = append(elems, el)
	}
	return
}

// Section represents a section of a document (such as a presentation slide)
// comprising a title and a list of elements.
type Section struct {
	Number []int
	Title  string
	Elem   []Elem
}

func (s Section) Sections() (sections []Section) {
	for _, e := range s.Elem {
		if s, ok := e.(Section); ok {
			sections = append(sections, s)
		}
	}
	return
}

// Level returns the level of the given section.
// The document title is level 1, main section 2, etc.
func (s Section) Level() int {
	return len(s.Number) + 1
}

// FormattedNumber returns a string containing the concatenation of the
// numbers identifying a Section.
func (s Section) FormattedNumber() string {
	b := &bytes.Buffer{}
	for _, n := range s.Number {
		fmt.Fprintf(b, "%v.", n)
	}
	return b.String()
}

func (s Section) TemplateName() string { return "section" }

// Elem defines the interface for a present element. That is, something that
// can provide the name of the template used to render the element.
type Elem interface {
	TemplateName() string
}

// renderElem implements the elem template function, used to render
// sub-templates.
func renderElem(t *template.Template, e Elem) (template.HTML, error) {
	var data interface{} = e
	if s, ok := e.(Section); ok {
		data = struct {
			Section
			Template *template.Template
		}{s, t}
	}
	return execTemplate(t, e.TemplateName(), data)
}

func init() {
	funcs["elem"] = renderElem
}

// execTemplate is a helper to execute a template and return the output as a
// template.HTML value.
func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) {
	b := new(bytes.Buffer)
	err := t.ExecuteTemplate(b, name, data)
	if err != nil {
		return "", err
	}
	return template.HTML(b.String()), nil
}

// Text represents an optionally preformatted paragraph.
type Text struct {
	Lines []string
	Pre   bool
}

func (t Text) TemplateName() string { return "text" }

// List represents a bulleted list.
type List struct {
	Bullet []string
}

func (l List) TemplateName() string { return "list" }

// Lines is a helper for parsing line-based input.
type Lines struct {
	line int // 0 indexed, so has 1-indexed number of last line returned
	text []string
}

func readLines(r io.Reader) (*Lines, error) {
	contentBytes, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	return &Lines{0, strings.Split(string(contentBytes), "\n")}, nil
}

func (l *Lines) next() (text string, ok bool) {
	for {
		current := l.line
		l.line++
		if current >= len(l.text) {
			return "", false
		}
		text = l.text[current]
		// Lines starting with # are comments.
		if len(text) == 0 || text[0] != '#' {
			ok = true
			break
		}
	}
	return
}

func (l *Lines) back() {
	l.line--
}

func (l *Lines) nextNonEmpty() (text string, ok bool) {
	for {
		text, ok = l.next()
		if !ok {
			return
		}
		if len(text) > 0 {
			break
		}
	}
	return
}

// ParseMode represents flags for the Parse function.
type ParseMode int

const (
	// If set, parse only the title and subtitle.
	TitlesOnly ParseMode = 1
)

// Parse parses the document in the file specified by name.
func Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) {
	doc := new(Doc)
	lines, err := readLines(r)
	if err != nil {
		return nil, err
	}
	err = parseHeader(doc, lines)
	if err != nil {
		return nil, err
	}
	if mode&TitlesOnly != 0 {
		return doc, nil
	}
	// Authors
	if doc.Authors, err = parseAuthors(lines); err != nil {
		return nil, err
	}
	// Sections
	if doc.Sections, err = parseSections(name, lines, []int{}, doc); err != nil {
		return nil, err
	}
	return doc, nil
}

// isHeading matches any section heading.
var isHeading = regexp.MustCompile(`^\*+ `)

// lesserHeading returns true if text is a heading of a lesser or equal level
// than that denoted by prefix.
func lesserHeading(text, prefix string) bool {
	return isHeading.MatchString(text) && !strings.HasPrefix(text, prefix+"*")
}

// parseSections parses Sections from lines for the section level indicated by
// number (a nil number indicates the top level).
func parseSections(name string, lines *Lines, number []int, doc *Doc) ([]Section, error) {
	var sections []Section
	for i := 1; ; i++ {
		// Next non-empty line is title.
		text, ok := lines.nextNonEmpty()
		for ok && text == "" {
			text, ok = lines.next()
		}
		if !ok {
			break
		}
		prefix := strings.Repeat("*", len(number)+1)
		if !strings.HasPrefix(text, prefix+" ") {
			lines.back()
			break
		}
		section := Section{
			Number: append(append([]int{}, number...), i),
			Title:  text[len(prefix)+1:],
		}
		text, ok = lines.nextNonEmpty()
		for ok && !lesserHeading(text, prefix) {
			var e Elem
			r, _ := utf8.DecodeRuneInString(text)
			switch {
			case unicode.IsSpace(r):
				i := strings.IndexFunc(text, func(r rune) bool {
					return !unicode.IsSpace(r)
				})
				if i < 0 {
					break
				}
				indent := text[:i]
				var s []string
				for ok && (strings.HasPrefix(text, indent) || text == "") {
					if text != "" {
						text = text[i:]
					}
					s = append(s, text)
					text, ok = lines.next()
				}
				lines.back()
				pre := strings.Join(s, "\n")
				pre = strings.Replace(pre, "\t", "    ", -1) // browsers treat tabs badly
				pre = strings.TrimRightFunc(pre, unicode.IsSpace)
				e = Text{Lines: []string{pre}, Pre: true}
			case strings.HasPrefix(text, "- "):
				var b []string
				for ok && strings.HasPrefix(text, "- ") {
					b = append(b, text[2:])
					text, ok = lines.next()
				}
				lines.back()
				e = List{Bullet: b}
			case strings.HasPrefix(text, prefix+"* "):
				lines.back()
				subsecs, err := parseSections(name, lines, section.Number, doc)
				if err != nil {
					return nil, err
				}
				for _, ss := range subsecs {
					section.Elem = append(section.Elem, ss)
				}
			case strings.HasPrefix(text, "."):
				args := strings.Fields(text)
				parser := parsers[args[0]]
				if parser == nil {
					return nil, fmt.Errorf("%s:%d: unknown command %q\n", name, lines.line, text)
				}
				t, err := parser(name, lines.line, text)
				if err != nil {
					return nil, err
				}
				e = t
			default:
				var l []string
				for ok && strings.TrimSpace(text) != "" {
					if text[0] == '.' { // Command breaks text block.
						break
					}
					if strings.HasPrefix(text, `\.`) { // Backslash escapes initial period.
						text = text[1:]
					}
					l = append(l, text)
					text, ok = lines.next()
				}
				if len(l) > 0 {
					e = Text{Lines: l}
				}
			}
			if e != nil {
				section.Elem = append(section.Elem, e)
			}
			text, ok = lines.nextNonEmpty()
		}
		if isHeading.MatchString(text) {
			lines.back()
		}
		sections = append(sections, section)
	}
	return sections, nil
}

func parseHeader(doc *Doc, lines *Lines) error {
	var ok bool
	// First non-empty line starts header.
	doc.Title, ok = lines.nextNonEmpty()
	if !ok {
		return errors.New("unexpected EOF; expected title")
	}
	for {
		text, ok := lines.next()
		if !ok {
			return errors.New("unexpected EOF")
		}
		if text == "" {
			break
		}
		const tagPrefix = "Tags:"
		if strings.HasPrefix(text, tagPrefix) {
			tags := strings.Split(text[len(tagPrefix):], ",")
			for i := range tags {
				tags[i] = strings.TrimSpace(tags[i])
			}
			doc.Tags = append(doc.Tags, tags...)
		} else if t, ok := parseTime(text); ok {
			doc.Time = t
		} else if doc.Subtitle == "" {
			doc.Subtitle = text
		} else {
			return fmt.Errorf("unexpected header line: %q", text)
		}
	}
	return nil
}

func parseAuthors(lines *Lines) (authors []Author, err error) {
	// This grammar demarcates authors with blanks.

	// Skip blank lines.
	if _, ok := lines.nextNonEmpty(); !ok {
		return nil, errors.New("unexpected EOF")
	}
	lines.back()

	var a *Author
	for {
		text, ok := lines.next()
		if !ok {
			return nil, errors.New("unexpected EOF")
		}

		// If we find a section heading, we're done.
		if strings.HasPrefix(text, "* ") {
			lines.back()
			break
		}

		// If we encounter a blank we're done with this author.
		if a != nil && len(text) == 0 {
			authors = append(authors, *a)
			a = nil
			continue
		}
		if a == nil {
			a = new(Author)
		}

		// Parse the line. Those that
		// - begin with @ are twitter names,
		// - contain slashes are links, or
		// - contain an @ symbol are an email address.
		// The rest is just text.
		var el Elem
		switch {
		case strings.HasPrefix(text, "@"):
			el = parseURL("http://twitter.com/" + text[1:])
			if l, ok := el.(Link); ok {
				l.Label = text
				el = l
			}
		case strings.Contains(text, ":"):
			el = parseURL(text)
		case strings.Contains(text, "@"):
			el = parseURL("mailto:" + text)
		}
		if el == nil {
			el = Text{Lines: []string{text}}
		}
		a.Elem = append(a.Elem, el)
	}
	if a != nil {
		authors = append(authors, *a)
	}
	return authors, nil
}

func parseURL(text string) Elem {
	u, err := url.Parse(text)
	if err != nil {
		log.Printf("Parse(%q): %v", text, err)
		return nil
	}
	return Link{URL: u}
}

func parseTime(text string) (t time.Time, ok bool) {
	t, err := time.Parse("15:04 2 Jan 2006", text)
	if err == nil {
		return t, true
	}
	t, err = time.Parse("2 Jan 2006", text)
	if err == nil {
		// at 11am UTC it is the same date everywhere
		t = t.Add(time.Hour * 11)
		return t, true
	}
	return time.Time{}, false
}
<|endoftext|>


<commit_before>/*******************************************************************************
*
* Copyright 2022 SAP SE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of the License along with this
* program. If not, 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 reports

import (
	"database/sql"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/sapcc/limes/pkg/core"
	"github.com/sapcc/limes/pkg/db"
)

var scrapeErrorsQuery = db.SimplifyWhitespaceInSQL(`
	SELECT d.uuid, d.name, p.uuid, p.name, ps.type, ps.checked_at, ps.scrape_error_message
	  FROM projects p
	  LEFT OUTER JOIN domains d ON d.id = p.domain_id
	  LEFT OUTER JOIN project_services ps ON ps.project_id = p.id
	WHERE %s GROUP BY d.uuid, d.name, p.uuid, p.name, ps.type, ps.checked_at, ps.scrape_error_message
	HAVING ps.scrape_error_message != ''
	ORDER BY d.name, p.name, ps.type, ps.scrape_error_message
`)

type ScrapeError struct {
	Project struct {
		ID     string `json:"id"`
		Name   string `json:"name"`
		Domain struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"domain"`
	} `json:"project"`
	ServiceType string `json:"service_type"`
	CheckedAt   *int64 `json:"checked_at"`
	Message     string `json:"message"`
}

func GetScrapeErrors(cluster *core.Cluster, dbi db.Interface, filter Filter) ([]ScrapeError, error) {
	return getScrapeErrors(cluster, dbi, filter, scrapeErrorsQuery)
}

func GetRateScrapeErrors(cluster *core.Cluster, dbi db.Interface, filter Filter) ([]ScrapeError, error) {
	dbQuery := strings.ReplaceAll(scrapeErrorsQuery, "scrape_error_message", "rates_scrape_error_message")
	dbQuery = strings.ReplaceAll(dbQuery, "checked_at", "rates_checked_at")
	return getScrapeErrors(cluster, dbi, filter, dbQuery)
}

func getScrapeErrors(cluster *core.Cluster, dbi db.Interface, filter Filter, dbQuery string) ([]ScrapeError, error) {
	fields := map[string]interface{}{"d.cluster_id": cluster.ID}

	var result []ScrapeError
	queryStr, joinArgs := filter.PrepareQuery(dbQuery)
	whereStr, whereArgs := db.BuildSimpleWhereClause(fields, len(joinArgs))
	err := db.ForeachRow(dbi, fmt.Sprintf(queryStr, whereStr), append(joinArgs, whereArgs...), func(rows *sql.Rows) error {
		sErr := ScrapeError{}
		var checkedAtAsTime *time.Time
		err := rows.Scan(
			&sErr.Project.Domain.ID, &sErr.Project.Domain.Name, &sErr.Project.ID,
			&sErr.Project.Name, &sErr.ServiceType, &checkedAtAsTime, &sErr.Message,
		)
		if err != nil {
			return err
		}

		if checkedAtAsTime != nil {
			v := checkedAtAsTime.Unix()
			sErr.CheckedAt = &v
		}
		result = append(result, sErr)

		return nil
	})
	if err != nil {
		return nil, err
	}

	if len(result) == 0 {
		//Ensure that empty list gets serialized as `[]` rather than as `null`.
		return []ScrapeError{}, nil
	}

	//To avoid excessively large responses, we group identical scrape errors for multiple
	//project services of the same type into one item with a dummy project ID.
	uniqueErrors := make(map[string]map[string]ScrapeError) //map[serviceType]map[Message]ScrapeError
	for _, v := range result {
		if _, found := uniqueErrors[v.ServiceType][v.Message]; found {
			if v.Project.Name == "dummy-project" {
				continue
			}
			v.Project.ID = "uuid-for-dummy-project"
			v.Project.Name = "dummy-project"
			v.Project.Domain.ID = "uuid-for-dummy-domain"
			v.Project.Domain.Name = "dummy-domain"
		}

		if _, ok := uniqueErrors[v.ServiceType]; !ok {
			uniqueErrors[v.ServiceType] = make(map[string]ScrapeError)
		}
		uniqueErrors[v.ServiceType][v.Message] = v
	}
	var new []ScrapeError
	for _, errMsgs := range uniqueErrors {
		for _, sErr := range errMsgs {
			new = append(new, sErr)
		}
	}
	//Deterministic ordering for unit tests
	sort.Slice(new, func(i, j int) bool {
		srvType1 := new[i].ServiceType
		srvType2 := new[j].ServiceType
		if srvType1 != srvType2 {
			return srvType1 < srvType2
		}
		pID1 := new[i].Project.ID
		pID2 := new[i].Project.ID
		return pID1 < pID2
	})
	result = new

	return result, nil
}
<commit_msg>reports/error: improve DB query<commit_after>/*******************************************************************************
*
* Copyright 2022 SAP SE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of the License along with this
* program. If not, 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 reports

import (
	"database/sql"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/sapcc/limes/pkg/core"
	"github.com/sapcc/limes/pkg/db"
)

var scrapeErrorsQuery = db.SimplifyWhitespaceInSQL(`
	SELECT d.uuid, d.name, p.uuid, p.name, ps.type, ps.checked_at, ps.scrape_error_message
	  FROM projects p
	  JOIN domains d ON d.id = p.domain_id
	  JOIN project_services ps ON ps.project_id = p.id
	WHERE %s AND ps.scrape_error_message != ''
	ORDER BY d.name, p.name, ps.type, ps.scrape_error_message
`)

type ScrapeError struct {
	Project struct {
		ID     string `json:"id"`
		Name   string `json:"name"`
		Domain struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"domain"`
	} `json:"project"`
	ServiceType string `json:"service_type"`
	CheckedAt   *int64 `json:"checked_at"`
	Message     string `json:"message"`
}

func GetScrapeErrors(cluster *core.Cluster, dbi db.Interface, filter Filter) ([]ScrapeError, error) {
	return getScrapeErrors(cluster, dbi, filter, scrapeErrorsQuery)
}

func GetRateScrapeErrors(cluster *core.Cluster, dbi db.Interface, filter Filter) ([]ScrapeError, error) {
	dbQuery := strings.ReplaceAll(scrapeErrorsQuery, "scrape_error_message", "rates_scrape_error_message")
	dbQuery = strings.ReplaceAll(dbQuery, "checked_at", "rates_checked_at")
	return getScrapeErrors(cluster, dbi, filter, dbQuery)
}

func getScrapeErrors(cluster *core.Cluster, dbi db.Interface, filter Filter, dbQuery string) ([]ScrapeError, error) {
	fields := map[string]interface{}{"d.cluster_id": cluster.ID}

	var result []ScrapeError
	queryStr, joinArgs := filter.PrepareQuery(dbQuery)
	whereStr, whereArgs := db.BuildSimpleWhereClause(fields, len(joinArgs))
	err := db.ForeachRow(dbi, fmt.Sprintf(queryStr, whereStr), append(joinArgs, whereArgs...), func(rows *sql.Rows) error {
		sErr := ScrapeError{}
		var checkedAtAsTime *time.Time
		err := rows.Scan(
			&sErr.Project.Domain.ID, &sErr.Project.Domain.Name, &sErr.Project.ID,
			&sErr.Project.Name, &sErr.ServiceType, &checkedAtAsTime, &sErr.Message,
		)
		if err != nil {
			return err
		}

		if checkedAtAsTime != nil {
			v := checkedAtAsTime.Unix()
			sErr.CheckedAt = &v
		}
		result = append(result, sErr)

		return nil
	})
	if err != nil {
		return nil, err
	}

	if len(result) == 0 {
		//Ensure that empty list gets serialized as `[]` rather than as `null`.
		return []ScrapeError{}, nil
	}

	//To avoid excessively large responses, we group identical scrape errors for multiple
	//project services of the same type into one item with a dummy project ID.
	uniqueErrors := make(map[string]map[string]ScrapeError) //map[serviceType]map[Message]ScrapeError
	for _, v := range result {
		if _, found := uniqueErrors[v.ServiceType][v.Message]; found {
			if v.Project.Name == "dummy-project" {
				continue
			}
			v.Project.ID = "uuid-for-dummy-project"
			v.Project.Name = "dummy-project"
			v.Project.Domain.ID = "uuid-for-dummy-domain"
			v.Project.Domain.Name = "dummy-domain"
		}

		if _, ok := uniqueErrors[v.ServiceType]; !ok {
			uniqueErrors[v.ServiceType] = make(map[string]ScrapeError)
		}
		uniqueErrors[v.ServiceType][v.Message] = v
	}
	var new []ScrapeError
	for _, errMsgs := range uniqueErrors {
		for _, sErr := range errMsgs {
			new = append(new, sErr)
		}
	}
	//Deterministic ordering for unit tests
	sort.Slice(new, func(i, j int) bool {
		srvType1 := new[i].ServiceType
		srvType2 := new[j].ServiceType
		if srvType1 != srvType2 {
			return srvType1 < srvType2
		}
		pID1 := new[i].Project.ID
		pID2 := new[i].Project.ID
		return pID1 < pID2
	})
	result = new

	return result, nil
}
<|endoftext|>


<commit_before>package slinga

import (
	"bytes"
	"errors"
	"strings"
	"text/template"
	log "github.com/Sirupsen/logrus"
)

/*
	Core engine for Slinga processing and evaluation
*/

// TemplateData defines struct that holds all params and gets passed to template engine for evaluating template string
type TemplateData struct {
	Labels    map[string]string
	User      User
	Discovery map[string]interface{}
}

// ResolveUsage evaluates all recorded Dependencies ("<user> needs <service> with <labels>") and calculates allocations
func (usage *ServiceUsageState) ResolveUsage(users *GlobalUsers) error {
	for serviceName, dependencies := range usage.Dependencies.Dependencies {
		for _, d := range dependencies {
			user := users.Users[d.UserID]

			// take user labels
			labels := user.getLabelSet()

			// combine them with dependency labels
			labels = labels.addLabels(d.getLabelSet())

			// see if it needs to be traced (addl debug output on console)
			tracing.setEnable(d.Trace)

			// resolve usage via applying policy
			resKey, err := usage.resolveWithLabels(user, serviceName, labels, usage.ComponentInstanceMap, 0)

			// disable tracing
			tracing.setEnable(false)

			// see if there is an error
			if err != nil {
				return err
			}

			// record high-level service resolution
			d.ResolvesTo = resKey
		}
	}
	return nil
}

// Evaluate "<user> needs <service>" statement
func (usage *ServiceUsageState) resolveWithLabels(user User, serviceName string, labels LabelSet, cim map[string]interface{}, depth int) (string, error) {

	// Resolving allocations for service
	debug.WithFields(log.Fields{
		"service": serviceName,
		"user": user.Name,
		"labels": labels,
	}).Info("Resolving allocations for service")

	tracing.Printf(depth, "[Dependency]")
	tracing.Printf(depth, "User: %s (ID = %s)", user.Name, user.ID)
	tracing.Printf(depth+1, "Labels: %s", labels)
	tracing.Printf(depth, "Service: %s", serviceName)

	// Policy
	policy := usage.Policy

	// Locate the service
	service, err := policy.getService(serviceName)
	if err != nil {
		tracing.Printf(depth, "Error while trying to look up service %s (%v)", serviceName, err)
		return "", err
	}

	// Process service and transform labels
	labels = labels.applyTransform(service.Labels)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the context
	context, err := policy.getMatchedContext(*service, user, labels, depth)
	if err != nil {
		tracing.Printf(depth, "Error while matching context for service %s (%v)", serviceName, err)
		return "", err
	}
	// If no matching context is found, let's just exit
	if context == nil {
		tracing.Printf(depth, "No context matched for service %s", serviceName)
		return "", nil
	}

	// Process context and transform labels
	labels = labels.applyTransform(context.Labels)
	tracing.Printf(depth, "Context: %s", context.Name)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the allocation
	allocation, err := policy.getMatchedAllocation(*service, user, *context, labels, depth)
	if err != nil {
		tracing.Printf(depth, "Error while matching allocation for service %s, context %s (%v)", serviceName, context.Name, err)
		return "", err
	}
	// If no matching allocation is found, let's just exit
	if allocation == nil {
		tracing.Printf(depth, "No allocation matched for service %s, context %s", serviceName, context.Name)
		return "", nil
	}

	// Process allocation and transform labels
	labels = labels.applyTransform(allocation.Labels)
	tracing.Printf(depth, "Allocation: %s", allocation.NameResolved)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Now, sort all components in topological order
	componentsOrdered, err := service.getComponentsSortedTopologically()
	if err != nil {
		return "", err
	}

	// Resolve every component
	for _, component := range componentsOrdered {
		// Process component and transform labels
		componentLabels := labels.applyTransform(component.Labels)

		// Is it a code?
		var codeParams interface{}
		var discoveryParams interface{}

		componentKey := usage.createServiceUsageKey(service, context, allocation, component)
		discoveryParams, err = component.processTemplateParams(component.Discovery, componentKey, componentLabels, user, cim, "discovery", depth)
		if err != nil {
			return "", err
		}

		// Create new map with resolution keys for component
		cimComponent := make(map[string]interface{})
		cim[component.Name] = cimComponent

		if component.Code != nil {
			// Evaluate code params
			debug.WithFields(log.Fields{
				"service": service.Name,
				"component": component.Name,
			}).Info("Processing dependency on code execution")

			// TODO: WAT?? no HelmName please
			cimComponent["instance"] = HelmName(componentKey)
			if discoveryParamsMap, ok := discoveryParams.(map[interface{}]interface{}); ok {
				for k, v := range discoveryParamsMap {
					cimComponent[k.(string)] = v
				}
			}

			codeParams, err = component.processTemplateParams(component.Code.Params, componentKey, componentLabels, user, cim, "code", depth)
			if err != nil {
				return "", err
			}
		} else if component.Service != "" {
			debug.WithFields(log.Fields{
				"service": service.Name,
				"component": component.Name,
				"dependsOnService": component.Service,
			}).Info("Processing dependency on another service")

			tracing.Println()

			_, err := usage.resolveWithLabels(user, component.Service, componentLabels, cimComponent, depth+1)
			if err != nil {
				return "", err
			}
		} else {
			debug.WithFields(log.Fields{
				"service": service.Name,
				"component": component.Name,
			}).Fatal("Invalid component (not code and not service")
		}

		// Record usage of a given component
		usage.recordUsage(componentKey, user, componentLabels, codeParams, discoveryParams)
	}

	// Record usage of a given service
	serviceKey := usage.createServiceUsageKey(service, context, allocation, nil)
	usage.recordUsage(serviceKey, user, labels, nil, nil)

	return context.Name + "#" + allocation.NameResolved, nil
}

// Topologically sort components and return true if there is a cycle detected
func (service *Service) dfsComponentSort(u *ServiceComponent, colors map[string]int) bool {
	colors[u.Name] = 1

	for _, vName := range u.Dependencies {
		v, exists := service.getComponentsMap()[vName]
		if !exists {
			debug.WithFields(log.Fields{
				"service": service.Name,
				"component": vName,
			}).Fatal("Service dependency points to non-existing component")
		}
		if vColor, ok := colors[v.Name]; !ok {
			// not visited yet -> visit and exit if a cycle was found
			if service.dfsComponentSort(v, colors) {
				return true
			}
		} else if vColor == 1 {
			return true
		}
	}

	service.componentsOrdered = append(service.componentsOrdered, u)
	colors[u.Name] = 2
	return false
}

// Sorts all components in a topological way
func (service *Service) getComponentsSortedTopologically() ([]*ServiceComponent, error) {
	if service.componentsOrdered == nil {
		// Initiate colors
		colors := make(map[string]int)

		// Dfs
		var cycle = false
		for _, c := range service.Components {
			if _, ok := colors[c.Name]; !ok {
				if service.dfsComponentSort(c, colors) {
					cycle = true
					break
				}
			}
		}

		if cycle {
			return nil, errors.New("Component cycle detected in service " + service.Name)
		}
	}

	return service.componentsOrdered, nil
}

// Helper to get a service
func (policy *Policy) getService(serviceName string) (*Service, error) {
	// Locate the service
	service := policy.Services[serviceName]
	if service == nil {
		return nil, errors.New("Service " + serviceName + " not found")
	}
	return service, nil
}

// Helper to get a matched context
func (policy *Policy) getMatchedContext(service Service, user User, labels LabelSet, depth int) (*Context, error) {
	// Locate the list of contexts for service
	contexts, ok := policy.Contexts[service.Name]
	if !ok || len(contexts) <= 0 {
		tracing.Printf(depth+1, "No contexts found")
		return nil, errors.New("No contexts found for " + service.Name)
	}

	// See which context matches
	var contextMatched *Context
	for _, c := range contexts {
		m := c.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing context '%s': (criteria = %+v)", m, c.Name, c.Criteria)
		if m {
			contextMatched = c
			break
		}
	}

	if contextMatched != nil {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": contextMatched.Name,
			"user": user.Name,
		}).Info("Matched context")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"user": user.Name,
		}).Info("No context matched")
	}
	return contextMatched, nil
}

// Helper to get a matched allocation
func (policy *Policy) getMatchedAllocation(service Service, user User, context Context, labels LabelSet, depth int) (*Allocation, error) {
	if len(context.Allocations) <= 0 {
		tracing.Printf(depth+1, "No allocations found")
		return nil, errors.New("No allocations found for " + service.Name)
	}

	// See which allocation matches
	var allocationMatched *Allocation
	for _, a := range context.Allocations {
		m := a.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing allocation '%s': (criteria = %+v)", m, a.Name, a.Criteria)
		if m {
			allocationMatched = a
			break
		}
	}

	// Check errors and resolve allocation name (it can be dynamic, depending on user labels)
	if allocationMatched != nil {
		err := allocationMatched.resolveName(user)
		if err != nil {
			debug.WithFields(log.Fields{
				"service": service.Name,
				"context": context.Name,
				"allocation": allocationMatched.Name,
				"user": user.Name,
			}).Fatal("Cannot resolve name for an allocation")
		}
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": context.Name,
			"allocation": allocationMatched.Name,
			"allocationResolved": allocationMatched.NameResolved,
			"user": user.Name,
		}).Info("Matched allocation")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": context.Name,
			"user": user.Name,
		}).Info("No allocation matched")
	}

	return allocationMatched, nil
}

type ProcessingError struct {
	Reason string
}

func (err ProcessingError) Error() string {
	return err.Reason
}

func (component *ServiceComponent) processTemplateParams(template interface{}, componentKey string, labels LabelSet, user User, cim map[string]interface{}, templateType string, depth int) (interface{}, error) {
	if template == nil {
		return nil, nil
	}
	tracing.Printf(depth+1, "Component: %s (%s)", component.Name, templateType)

	cimCopy := make(map[string]interface{})
	// TODO SPARTAAA!
	cimCopy["instance"] = HelmName(componentKey)
	for k, v := range cim {
		cimCopy[k] = v
	}

	templateData := TemplateData{
		Labels:    labels.Labels,
		Discovery: cimCopy,
		User:      user}

	var evalParamsInterface func(params interface{}) (interface{}, error)
	evalParamsInterface = func(params interface{}) (interface{}, error) {
		if params == nil {
			return "", nil
		} else if paramsMap, ok := params.(map[interface{}]interface{}); ok {
			resultMap := make(map[interface{}]interface{})

			for key, value := range paramsMap {
				evaluatedValue, err := evalParamsInterface(value)
				if err != nil {
					return nil, err
				}
				resultMap[key] = evaluatedValue
			}

			return resultMap, nil
		} else if paramsStr, ok := params.(string); ok {
			evaluatedValue, err := evaluateCodeParamTemplate(paramsStr, templateData)
			tracing.Printf(depth+2, "Parameter '%s': %s", paramsStr, evaluatedValue)
			if err != nil {
				return nil, err
			}
			return evaluatedValue, nil
		} else if paramsInt, ok := params.(int); ok {
			return paramsInt, nil
		} else if paramsBool, ok := params.(bool); ok {
			return paramsBool, nil
		}

		return nil, ProcessingError{"There should be map[string]interface{} or string"}
	}

	return evalParamsInterface(template)
}

func evaluateCodeParamTemplate(templateStr string, templateData TemplateData) (string, error) {
	tmpl, err := template.New("").Parse(templateStr)
	if err != nil {
		return "", errors.New("Invalid template " + templateStr)
	}

	var doc bytes.Buffer
	err = tmpl.Execute(&doc, templateData)

	if err != nil {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	result := doc.String()
	if strings.Contains(result, "<no value>") {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	// TODO: remove this ugly shit later (should not reference Helm in generic engine)
	return HelmName(doc.String()), nil
}
<commit_msg>minor tracing + formatting fixes<commit_after>package slinga

import (
	"bytes"
	"errors"
	"strings"
	"text/template"
	log "github.com/Sirupsen/logrus"
)

/*
	Core engine for Slinga processing and evaluation
*/

// TemplateData defines struct that holds all params and gets passed to template engine for evaluating template string
type TemplateData struct {
	Labels    map[string]string
	User      User
	Discovery map[string]interface{}
}

// ResolveUsage evaluates all recorded Dependencies ("<user> needs <service> with <labels>") and calculates allocations
func (usage *ServiceUsageState) ResolveUsage(users *GlobalUsers) error {
	for serviceName, dependencies := range usage.Dependencies.Dependencies {
		for _, d := range dependencies {
			user := users.Users[d.UserID]

			// take user labels
			labels := user.getLabelSet()

			// combine them with dependency labels
			labels = labels.addLabels(d.getLabelSet())

			// see if it needs to be traced (addl debug output on console)
			tracing.setEnable(d.Trace)

			// resolve usage via applying policy
			resKey, err := usage.resolveWithLabels(user, serviceName, labels, usage.ComponentInstanceMap, 0)

			// disable tracing
			tracing.setEnable(false)

			// see if there is an error
			if err != nil {
				return err
			}

			// record high-level service resolution
			d.ResolvesTo = resKey
		}
	}
	return nil
}

// Evaluate "<user> needs <service>" statement
func (usage *ServiceUsageState) resolveWithLabels(user User, serviceName string, labels LabelSet, cim map[string]interface{}, depth int) (string, error) {

	// Resolving allocations for service
	debug.WithFields(log.Fields{
		"service": serviceName,
		"user":    user.Name,
		"labels":  labels,
	}).Info("Resolving allocations for service")

	tracing.Printf(depth, "[Dependency]")
	tracing.Printf(depth, "User: %s (ID = %s)", user.Name, user.ID)
	tracing.Printf(depth+1, "Labels: %s", labels)
	tracing.Printf(depth, "Service: %s", serviceName)

	// Policy
	policy := usage.Policy

	// Locate the service
	service, err := policy.getService(serviceName)
	if err != nil {
		tracing.Printf(depth+1, "Error while trying to look up service %s (%v)", serviceName, err)
		return "", err
	}

	// Process service and transform labels
	labels = labels.applyTransform(service.Labels)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the context
	context, err := policy.getMatchedContext(*service, user, labels, depth)
	if err != nil {
		tracing.Printf(depth+1, "Error while matching context for service %s (%v)", serviceName, err)
		return "", err
	}
	// If no matching context is found, let's just exit
	if context == nil {
		tracing.Printf(depth+1, "No context matched for service %s", serviceName)
		return "", nil
	}

	// Process context and transform labels
	labels = labels.applyTransform(context.Labels)
	tracing.Printf(depth, "Context: %s", context.Name)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Match the allocation
	allocation, err := policy.getMatchedAllocation(*service, user, *context, labels, depth)
	if err != nil {
		tracing.Printf(depth, "Error while matching allocation for service %s, context %s (%v)", serviceName, context.Name, err)
		return "", err
	}
	// If no matching allocation is found, let's just exit
	if allocation == nil {
		tracing.Printf(depth, "No allocation matched for service %s, context %s", serviceName, context.Name)
		return "", nil
	}

	// Process allocation and transform labels
	labels = labels.applyTransform(allocation.Labels)
	tracing.Printf(depth, "Allocation: %s", allocation.NameResolved)
	tracing.Printf(depth+1, "New labels = %s", labels)

	// Now, sort all components in topological order
	componentsOrdered, err := service.getComponentsSortedTopologically()
	if err != nil {
		return "", err
	}

	// Resolve every component
	for _, component := range componentsOrdered {
		// Process component and transform labels
		componentLabels := labels.applyTransform(component.Labels)

		// Is it a code?
		var codeParams interface{}
		var discoveryParams interface{}

		componentKey := usage.createServiceUsageKey(service, context, allocation, component)
		discoveryParams, err = component.processTemplateParams(component.Discovery, componentKey, componentLabels, user, cim, "discovery", depth)
		if err != nil {
			return "", err
		}

		// Create new map with resolution keys for component
		cimComponent := make(map[string]interface{})
		cim[component.Name] = cimComponent

		if component.Code != nil {
			// Evaluate code params
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": component.Name,
			}).Info("Processing dependency on code execution")

			// TODO: WAT?? no HelmName please
			cimComponent["instance"] = HelmName(componentKey)
			if discoveryParamsMap, ok := discoveryParams.(map[interface{}]interface{}); ok {
				for k, v := range discoveryParamsMap {
					cimComponent[k.(string)] = v
				}
			}

			codeParams, err = component.processTemplateParams(component.Code.Params, componentKey, componentLabels, user, cim, "code", depth)
			if err != nil {
				return "", err
			}
		} else if component.Service != "" {
			debug.WithFields(log.Fields{
				"service":          service.Name,
				"component":        component.Name,
				"dependsOnService": component.Service,
			}).Info("Processing dependency on another service")

			tracing.Println()

			_, err := usage.resolveWithLabels(user, component.Service, componentLabels, cimComponent, depth+1)
			if err != nil {
				return "", err
			}
		} else {
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": component.Name,
			}).Fatal("Invalid component (not code and not service")
		}

		// Record usage of a given component
		usage.recordUsage(componentKey, user, componentLabels, codeParams, discoveryParams)
	}

	// Record usage of a given service
	serviceKey := usage.createServiceUsageKey(service, context, allocation, nil)
	usage.recordUsage(serviceKey, user, labels, nil, nil)

	return context.Name + "#" + allocation.NameResolved, nil
}

// Topologically sort components and return true if there is a cycle detected
func (service *Service) dfsComponentSort(u *ServiceComponent, colors map[string]int) bool {
	colors[u.Name] = 1

	for _, vName := range u.Dependencies {
		v, exists := service.getComponentsMap()[vName]
		if !exists {
			debug.WithFields(log.Fields{
				"service":   service.Name,
				"component": vName,
			}).Fatal("Service dependency points to non-existing component")
		}
		if vColor, ok := colors[v.Name]; !ok {
			// not visited yet -> visit and exit if a cycle was found
			if service.dfsComponentSort(v, colors) {
				return true
			}
		} else if vColor == 1 {
			return true
		}
	}

	service.componentsOrdered = append(service.componentsOrdered, u)
	colors[u.Name] = 2
	return false
}

// Sorts all components in a topological way
func (service *Service) getComponentsSortedTopologically() ([]*ServiceComponent, error) {
	if service.componentsOrdered == nil {
		// Initiate colors
		colors := make(map[string]int)

		// Dfs
		var cycle = false
		for _, c := range service.Components {
			if _, ok := colors[c.Name]; !ok {
				if service.dfsComponentSort(c, colors) {
					cycle = true
					break
				}
			}
		}

		if cycle {
			return nil, errors.New("Component cycle detected in service " + service.Name)
		}
	}

	return service.componentsOrdered, nil
}

// Helper to get a service
func (policy *Policy) getService(serviceName string) (*Service, error) {
	// Locate the service
	service := policy.Services[serviceName]
	if service == nil {
		return nil, errors.New("Service " + serviceName + " not found")
	}
	return service, nil
}

// Helper to get a matched context
func (policy *Policy) getMatchedContext(service Service, user User, labels LabelSet, depth int) (*Context, error) {
	// Locate the list of contexts for service
	contexts, ok := policy.Contexts[service.Name]
	if !ok || len(contexts) <= 0 {
		tracing.Printf(depth+1, "No contexts found")
		return nil, errors.New("No contexts found for " + service.Name)
	}

	// See which context matches
	var contextMatched *Context
	for _, c := range contexts {
		m := c.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing context '%s': (criteria = %+v)", m, c.Name, c.Criteria)
		if m {
			contextMatched = c
			break
		}
	}

	if contextMatched != nil {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": contextMatched.Name,
			"user":    user.Name,
		}).Info("Matched context")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"user":    user.Name,
		}).Info("No context matched")
	}
	return contextMatched, nil
}

// Helper to get a matched allocation
func (policy *Policy) getMatchedAllocation(service Service, user User, context Context, labels LabelSet, depth int) (*Allocation, error) {
	if len(context.Allocations) <= 0 {
		tracing.Printf(depth+1, "No allocations found")
		return nil, errors.New("No allocations found for " + service.Name)
	}

	// See which allocation matches
	var allocationMatched *Allocation
	for _, a := range context.Allocations {
		m := a.matches(labels)
		tracing.Printf(depth+1, "[%t] Testing allocation '%s': (criteria = %+v)", m, a.Name, a.Criteria)
		if m {
			allocationMatched = a
			break
		}
	}

	// Check errors and resolve allocation name (it can be dynamic, depending on user labels)
	if allocationMatched != nil {
		err := allocationMatched.resolveName(user)
		if err != nil {
			debug.WithFields(log.Fields{
				"service":    service.Name,
				"context":    context.Name,
				"allocation": allocationMatched.Name,
				"user":       user.Name,
			}).Fatal("Cannot resolve name for an allocation")
		}
		debug.WithFields(log.Fields{
			"service":            service.Name,
			"context":            context.Name,
			"allocation":         allocationMatched.Name,
			"allocationResolved": allocationMatched.NameResolved,
			"user":               user.Name,
		}).Info("Matched allocation")
	} else {
		debug.WithFields(log.Fields{
			"service": service.Name,
			"context": context.Name,
			"user":    user.Name,
		}).Info("No allocation matched")
	}

	return allocationMatched, nil
}

type ProcessingError struct {
	Reason string
}

func (err ProcessingError) Error() string {
	return err.Reason
}

func (component *ServiceComponent) processTemplateParams(template interface{}, componentKey string, labels LabelSet, user User, cim map[string]interface{}, templateType string, depth int) (interface{}, error) {
	if template == nil {
		return nil, nil
	}
	tracing.Printf(depth+1, "Component: %s (%s)", component.Name, templateType)

	cimCopy := make(map[string]interface{})
	// TODO SPARTAAA!
	cimCopy["instance"] = HelmName(componentKey)
	for k, v := range cim {
		cimCopy[k] = v
	}

	templateData := TemplateData{
		Labels:    labels.Labels,
		Discovery: cimCopy,
		User:      user}

	var evalParamsInterface func(params interface{}) (interface{}, error)
	evalParamsInterface = func(params interface{}) (interface{}, error) {
		if params == nil {
			return "", nil
		} else if paramsMap, ok := params.(map[interface{}]interface{}); ok {
			resultMap := make(map[interface{}]interface{})

			for key, value := range paramsMap {
				evaluatedValue, err := evalParamsInterface(value)
				if err != nil {
					return nil, err
				}
				resultMap[key] = evaluatedValue
			}

			return resultMap, nil
		} else if paramsStr, ok := params.(string); ok {
			evaluatedValue, err := evaluateCodeParamTemplate(paramsStr, templateData)
			tracing.Printf(depth+2, "Parameter '%s': %s", paramsStr, evaluatedValue)
			if err != nil {
				return nil, err
			}
			return evaluatedValue, nil
		} else if paramsInt, ok := params.(int); ok {
			return paramsInt, nil
		} else if paramsBool, ok := params.(bool); ok {
			return paramsBool, nil
		}

		return nil, ProcessingError{"There should be map[string]interface{} or string"}
	}

	return evalParamsInterface(template)
}

func evaluateCodeParamTemplate(templateStr string, templateData TemplateData) (string, error) {
	tmpl, err := template.New("").Parse(templateStr)
	if err != nil {
		return "", errors.New("Invalid template " + templateStr)
	}

	var doc bytes.Buffer
	err = tmpl.Execute(&doc, templateData)

	if err != nil {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	result := doc.String()
	if strings.Contains(result, "<no value>") {
		return "", errors.New("Cannot evaluate template " + templateStr)
	}

	// TODO: remove this ugly shit later (should not reference Helm in generic engine)
	return HelmName(doc.String()), nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Ceph-CSI 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 util

import (
	"io/ioutil"
	"os"
)

var cephConfig = []byte(`[global]
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx

# Workaround for http://tracker.ceph.com/issues/23446
fuse_set_user_groups = false
`)

const (
	cephConfigRoot = "/etc/ceph"
	CephConfigPath = "/etc/ceph/ceph.conf"
)

func createCephConfigRoot() error {
	return os.MkdirAll(cephConfigRoot, 0755) // #nosec
}

// WriteCephConfig writes out a basic ceph.conf file, making it easy to use
// ceph related CLIs
func WriteCephConfig() error {
	if err := createCephConfigRoot(); err != nil {
		return err
	}

	return ioutil.WriteFile(CephConfigPath, cephConfig, 0640)
}
<commit_msg>Avoid keyring message while logging<commit_after>/*
Copyright 2018 The Ceph-CSI 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 util

import (
	"io/ioutil"
	"os"
)

var cephConfig = []byte(`[global]
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx

# Workaround for http://tracker.ceph.com/issues/23446
fuse_set_user_groups = false
`)

const (
	cephConfigRoot = "/etc/ceph"
	// CephConfigPath ceph configuration file
	CephConfigPath = "/etc/ceph/ceph.conf"

	keyRing = "/etc/ceph/keyring"
)

func createCephConfigRoot() error {
	return os.MkdirAll(cephConfigRoot, 0755) // #nosec
}

// WriteCephConfig writes out a basic ceph.conf file, making it easy to use
// ceph related CLIs
func WriteCephConfig() error {
	if err := createCephConfigRoot(); err != nil {
		return err
	}

	err := ioutil.WriteFile(CephConfigPath, cephConfig, 0640)
	if err != nil {
		return err
	}

	return createKeyRingFile()
}

/*
if any ceph commands fails it will log below error message

7f39ff02a700 -1 auth: unable to find a keyring on
/etc/ceph/ceph.client.admin.keyring,/etc/ceph/ceph.keyring,/etc/ceph/keyring,
/etc/ceph/keyring.bin,: (2) No such file or directory
*/
// createKeyRingFile creates the keyring files to fix above error message logging
func createKeyRingFile() error {
	_, err := os.Create(keyRing)
	return err
}
<|endoftext|>