<commit_before>/*
Copyright 2017 The Kubernetes 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 auth

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/spf13/cobra"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
	internalauthorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

// CanIOptions is the start of the data required to perform the operation.  As new fields are added, add them here instead of
// referencing the cmd.Flags()
type CanIOptions struct {
	AllNamespaces bool
	Quiet         bool
	Namespace     string
	SelfSARClient internalauthorizationclient.SelfSubjectAccessReviewsGetter

	Verb         string
	Resource     schema.GroupVersionResource
	ResourceName string

	Out io.Writer
	Err io.Writer
}

var (
	canILong = templates.LongDesc(`
		Check whether an action is allowed.

		VERB is a logical Kubernetes API verb like 'get', 'list', 'watch', 'delete', etc.
		TYPE is a Kubernetes resource.  Shortcuts and groups will be resolved.
		NAME is the name of a particular Kubernetes resource.`)

	canIExample = templates.Examples(`
		# Check to see if I can create pods in any namespace
		kubectl auth can-i create pods --all-namespaces

		# Check to see if I can list deployments in my current namespace
		kubectl auth can-i list deployments.extensions

		# Check to see if I can do everything in my current namespace ("*" means all)
		kubectl auth can-i '*' '*'

		# Check to see if I can get the job named "bar" in namespace "foo"
		kubectl auth can-i list jobs.batch/bar -n foo`)
)

func NewCmdCanI(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
	o := &CanIOptions{
		Out: out,
		Err: err,
	}

	cmd := &cobra.Command{
		Use:     "can-i VERB [TYPE | TYPE/NAME]",
		Short:   "Check whether an action is allowed",
		Long:    canILong,
		Example: canIExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, args))
			cmdutil.CheckErr(o.Validate())

			allowed, err := o.RunAccessCheck()
			if err == nil {
				return
			}

			if o.Quiet && !allowed {
				os.Exit(1)
			}

			cmdutil.CheckErr(err)
		},
	}

	cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If true, check the specified action in all namespaces.")
	cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "If true, suppress output and just return the exit code.")
	return cmd
}

func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error {
	switch len(args) {
	case 2:
		resourceTokens := strings.SplitN(args[1], "/", 2)
		restMapper, _ := f.Object()
		o.Verb = args[0]
		o.Resource = resourceFor(restMapper, resourceTokens[0])
		if len(resourceTokens) > 1 {
			o.ResourceName = resourceTokens[1]
		}
	default:
		return errors.New("you must specify two or three arguments: verb, resource, and optional resourceName")
	}

	var err error
	client, err := f.ClientSet()
	if err != nil {
		return err
	}
	o.SelfSARClient = client.Authorization()

	o.Namespace = ""
	if !o.AllNamespaces {
		o.Namespace, _, err = f.DefaultNamespace()
		if err != nil {
			return err
		}
	}

	if o.Quiet {
		o.Out = ioutil.Discard
	}

	return nil
}

func (o *CanIOptions) Validate() error {
	errors := []error{}
	return utilerrors.NewAggregate(errors)
}

func (o *CanIOptions) RunAccessCheck() (bool, error) {
	sar := &authorizationapi.SelfSubjectAccessReview{
		Spec: authorizationapi.SelfSubjectAccessReviewSpec{
			ResourceAttributes: &authorizationapi.ResourceAttributes{
				Namespace: o.Namespace,
				Verb:      o.Verb,
				Group:     o.Resource.Group,
				Resource:  o.Resource.Resource,
				Name:      o.ResourceName,
			},
		},
	}

	response, err := o.SelfSARClient.SelfSubjectAccessReviews().Create(sar)
	if err != nil {
		return false, err
	}

	if response.Status.Allowed {
		fmt.Fprintln(o.Out, "yes")
	} else {
		fmt.Fprint(o.Out, "no")
		if len(response.Status.Reason) > 0 {
			fmt.Fprintf(o.Out, " - %v", response.Status.Reason)
		}
		if len(response.Status.EvaluationError) > 0 {
			fmt.Fprintf(o.Out, " - %v", response.Status.EvaluationError)
		}
		fmt.Fprintln(o.Out)
	}

	return response.Status.Allowed, nil
}

func resourceFor(mapper meta.RESTMapper, resourceArg string) schema.GroupVersionResource {
	fullySpecifiedGVR, groupResource := schema.ParseResourceArg(strings.ToLower(resourceArg))
	gvr := schema.GroupVersionResource{}
	if fullySpecifiedGVR != nil {
		gvr, _ = mapper.ResourceFor(*fullySpecifiedGVR)
	}
	if gvr.Empty() {
		var err error
		gvr, err = mapper.ResourceFor(groupResource.WithVersion(""))
		if err != nil {
			return schema.GroupVersionResource{Resource: resourceArg}
		}
	}

	return gvr
}
<commit_msg>fix return value of "kubectl auth cani --quiet"<commit_after>/*
Copyright 2017 The Kubernetes 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 auth

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/spf13/cobra"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
	internalauthorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

// CanIOptions is the start of the data required to perform the operation.  As new fields are added, add them here instead of
// referencing the cmd.Flags()
type CanIOptions struct {
	AllNamespaces bool
	Quiet         bool
	Namespace     string
	SelfSARClient internalauthorizationclient.SelfSubjectAccessReviewsGetter

	Verb         string
	Resource     schema.GroupVersionResource
	ResourceName string

	Out io.Writer
	Err io.Writer
}

var (
	canILong = templates.LongDesc(`
		Check whether an action is allowed.

		VERB is a logical Kubernetes API verb like 'get', 'list', 'watch', 'delete', etc.
		TYPE is a Kubernetes resource.  Shortcuts and groups will be resolved.
		NAME is the name of a particular Kubernetes resource.`)

	canIExample = templates.Examples(`
		# Check to see if I can create pods in any namespace
		kubectl auth can-i create pods --all-namespaces

		# Check to see if I can list deployments in my current namespace
		kubectl auth can-i list deployments.extensions

		# Check to see if I can do everything in my current namespace ("*" means all)
		kubectl auth can-i '*' '*'

		# Check to see if I can get the job named "bar" in namespace "foo"
		kubectl auth can-i list jobs.batch/bar -n foo`)
)

func NewCmdCanI(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
	o := &CanIOptions{
		Out: out,
		Err: err,
	}

	cmd := &cobra.Command{
		Use:     "can-i VERB [TYPE | TYPE/NAME]",
		Short:   "Check whether an action is allowed",
		Long:    canILong,
		Example: canIExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, args))
			cmdutil.CheckErr(o.Validate())

			allowed, err := o.RunAccessCheck()
			if err == nil {
				if o.Quiet && !allowed {
					os.Exit(1)
				}
			}

			cmdutil.CheckErr(err)
		},
	}

	cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If true, check the specified action in all namespaces.")
	cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "If true, suppress output and just return the exit code.")
	return cmd
}

func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error {
	switch len(args) {
	case 2:
		resourceTokens := strings.SplitN(args[1], "/", 2)
		restMapper, _ := f.Object()
		o.Verb = args[0]
		o.Resource = resourceFor(restMapper, resourceTokens[0])
		if len(resourceTokens) > 1 {
			o.ResourceName = resourceTokens[1]
		}
	default:
		return errors.New("you must specify two or three arguments: verb, resource, and optional resourceName")
	}

	var err error
	client, err := f.ClientSet()
	if err != nil {
		return err
	}
	o.SelfSARClient = client.Authorization()

	o.Namespace = ""
	if !o.AllNamespaces {
		o.Namespace, _, err = f.DefaultNamespace()
		if err != nil {
			return err
		}
	}

	if o.Quiet {
		o.Out = ioutil.Discard
	}

	return nil
}

func (o *CanIOptions) Validate() error {
	errors := []error{}
	return utilerrors.NewAggregate(errors)
}

func (o *CanIOptions) RunAccessCheck() (bool, error) {
	sar := &authorizationapi.SelfSubjectAccessReview{
		Spec: authorizationapi.SelfSubjectAccessReviewSpec{
			ResourceAttributes: &authorizationapi.ResourceAttributes{
				Namespace: o.Namespace,
				Verb:      o.Verb,
				Group:     o.Resource.Group,
				Resource:  o.Resource.Resource,
				Name:      o.ResourceName,
			},
		},
	}

	response, err := o.SelfSARClient.SelfSubjectAccessReviews().Create(sar)
	if err != nil {
		return false, err
	}

	if response.Status.Allowed {
		fmt.Fprintln(o.Out, "yes")
	} else {
		fmt.Fprint(o.Out, "no")
		if len(response.Status.Reason) > 0 {
			fmt.Fprintf(o.Out, " - %v", response.Status.Reason)
		}
		if len(response.Status.EvaluationError) > 0 {
			fmt.Fprintf(o.Out, " - %v", response.Status.EvaluationError)
		}
		fmt.Fprintln(o.Out)
	}

	return response.Status.Allowed, nil
}

func resourceFor(mapper meta.RESTMapper, resourceArg string) schema.GroupVersionResource {
	fullySpecifiedGVR, groupResource := schema.ParseResourceArg(strings.ToLower(resourceArg))
	gvr := schema.GroupVersionResource{}
	if fullySpecifiedGVR != nil {
		gvr, _ = mapper.ResourceFor(*fullySpecifiedGVR)
	}
	if gvr.Empty() {
		var err error
		gvr, err = mapper.ResourceFor(groupResource.WithVersion(""))
		if err != nil {
			return schema.GroupVersionResource{Resource: resourceArg}
		}
	}

	return gvr
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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.
*/

// Common logic used by both http and file channels.
package config

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/apis/core/helper"
	// TODO: remove this import if
	// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
	// to "v1"?
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	_ "k8s.io/kubernetes/pkg/apis/core/install"
	k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
	"k8s.io/kubernetes/pkg/apis/core/validation"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/util/hash"

	"github.com/golang/glog"
)

// Generate a pod name that is unique among nodes by appending the nodeName.
func generatePodName(name string, nodeName types.NodeName) string {
	return fmt.Sprintf("%s-%s", name, nodeName)
}

func applyDefaults(pod *api.Pod, source string, isFile bool, nodeName types.NodeName) error {
	if len(pod.UID) == 0 {
		hasher := md5.New()
		if isFile {
			fmt.Fprintf(hasher, "host:%s", nodeName)
			fmt.Fprintf(hasher, "file:%s", source)
		} else {
			fmt.Fprintf(hasher, "url:%s", source)
		}
		hash.DeepHashObject(hasher, pod)
		pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
		glog.V(5).Infof("Generated UID %q pod %q from %s", pod.UID, pod.Name, source)
	}

	pod.Name = generatePodName(pod.Name, nodeName)
	glog.V(5).Infof("Generated Name %q for UID %q from URL %s", pod.Name, pod.UID, source)

	if pod.Namespace == "" {
		pod.Namespace = metav1.NamespaceDefault
	}
	glog.V(5).Infof("Using namespace %q for pod %q from %s", pod.Namespace, pod.Name, source)

	// Set the Host field to indicate this pod is scheduled on the current node.
	pod.Spec.NodeName = string(nodeName)

	pod.ObjectMeta.SelfLink = getSelfLink(pod.Name, pod.Namespace)

	if pod.Annotations == nil {
		pod.Annotations = make(map[string]string)
	}
	// The generated UID is the hash of the file.
	pod.Annotations[kubetypes.ConfigHashAnnotationKey] = string(pod.UID)

	if isFile {
		// Applying the default Taint tolerations to static pods,
		// so they are not evicted when there are node problems.
		helper.AddOrUpdateTolerationInPod(pod, &api.Toleration{
			Operator: "Exists",
			Effect:   api.TaintEffectNoExecute,
		})
	}

	// Set the default status to pending.
	pod.Status.Phase = api.PodPending
	return nil
}

func getSelfLink(name, namespace string) string {
	var selfLink string
	if len(namespace) == 0 {
		namespace = metav1.NamespaceDefault
	}
	selfLink = fmt.Sprintf("/api/"+legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.Version+"/namespaces/%s/pods/%s", namespace, name)
	return selfLink
}

type defaultFunc func(pod *api.Pod) error

func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *v1.Pod, err error) {
	// JSON is valid YAML, so this should work for everything.
	json, err := utilyaml.ToJSON(data)
	if err != nil {
		return false, nil, err
	}
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), json)
	if err != nil {
		return false, pod, err
	}

	newPod, ok := obj.(*api.Pod)
	// Check whether the object could be converted to single pod.
	if !ok {
		return false, pod, fmt.Errorf("invalid pod: %#v", obj)
	}

	// Apply default values and validate the pod.
	if err = defaultFn(newPod); err != nil {
		return true, pod, err
	}
	if errs := validation.ValidatePod(newPod); len(errs) > 0 {
		return true, pod, fmt.Errorf("invalid pod: %v", errs)
	}
	v1Pod := &v1.Pod{}
	if err := k8s_api_v1.Convert_core_Pod_To_v1_Pod(newPod, v1Pod, nil); err != nil {
		glog.Errorf("Pod %q failed to convert to v1", newPod.Name)
		return true, nil, err
	}
	return true, v1Pod, nil
}

func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.PodList, err error) {
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
	if err != nil {
		return false, pods, err
	}

	newPods, ok := obj.(*api.PodList)
	// Check whether the object could be converted to list of pods.
	if !ok {
		err = fmt.Errorf("invalid pods list: %#v", obj)
		return false, pods, err
	}

	// Apply default values and validate pods.
	for i := range newPods.Items {
		newPod := &newPods.Items[i]
		if err = defaultFn(newPod); err != nil {
			return true, pods, err
		}
		if errs := validation.ValidatePod(newPod); len(errs) > 0 {
			err = fmt.Errorf("invalid pod: %v", errs)
			return true, pods, err
		}
	}
	v1Pods := &v1.PodList{}
	if err := k8s_api_v1.Convert_core_PodList_To_v1_PodList(newPods, v1Pods, nil); err != nil {
		return true, pods, err
	}
	return true, *v1Pods, err
}
<commit_msg>force node name in generated static pod name lowercase<commit_after>/*
Copyright 2014 The Kubernetes 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.
*/

// Common logic used by both http and file channels.
package config

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strings"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/apis/core/helper"
	// TODO: remove this import if
	// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
	// to "v1"?
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	_ "k8s.io/kubernetes/pkg/apis/core/install"
	k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
	"k8s.io/kubernetes/pkg/apis/core/validation"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/util/hash"

	"github.com/golang/glog"
)

// Generate a pod name that is unique among nodes by appending the nodeName.
func generatePodName(name string, nodeName types.NodeName) string {
	return fmt.Sprintf("%s-%s", name, strings.ToLower(string(nodeName)))
}

func applyDefaults(pod *api.Pod, source string, isFile bool, nodeName types.NodeName) error {
	if len(pod.UID) == 0 {
		hasher := md5.New()
		if isFile {
			fmt.Fprintf(hasher, "host:%s", nodeName)
			fmt.Fprintf(hasher, "file:%s", source)
		} else {
			fmt.Fprintf(hasher, "url:%s", source)
		}
		hash.DeepHashObject(hasher, pod)
		pod.UID = types.UID(hex.EncodeToString(hasher.Sum(nil)[0:]))
		glog.V(5).Infof("Generated UID %q pod %q from %s", pod.UID, pod.Name, source)
	}

	pod.Name = generatePodName(pod.Name, nodeName)
	glog.V(5).Infof("Generated Name %q for UID %q from URL %s", pod.Name, pod.UID, source)

	if pod.Namespace == "" {
		pod.Namespace = metav1.NamespaceDefault
	}
	glog.V(5).Infof("Using namespace %q for pod %q from %s", pod.Namespace, pod.Name, source)

	// Set the Host field to indicate this pod is scheduled on the current node.
	pod.Spec.NodeName = string(nodeName)

	pod.ObjectMeta.SelfLink = getSelfLink(pod.Name, pod.Namespace)

	if pod.Annotations == nil {
		pod.Annotations = make(map[string]string)
	}
	// The generated UID is the hash of the file.
	pod.Annotations[kubetypes.ConfigHashAnnotationKey] = string(pod.UID)

	if isFile {
		// Applying the default Taint tolerations to static pods,
		// so they are not evicted when there are node problems.
		helper.AddOrUpdateTolerationInPod(pod, &api.Toleration{
			Operator: "Exists",
			Effect:   api.TaintEffectNoExecute,
		})
	}

	// Set the default status to pending.
	pod.Status.Phase = api.PodPending
	return nil
}

func getSelfLink(name, namespace string) string {
	var selfLink string
	if len(namespace) == 0 {
		namespace = metav1.NamespaceDefault
	}
	selfLink = fmt.Sprintf("/api/"+legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.Version+"/namespaces/%s/pods/%s", namespace, name)
	return selfLink
}

type defaultFunc func(pod *api.Pod) error

func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *v1.Pod, err error) {
	// JSON is valid YAML, so this should work for everything.
	json, err := utilyaml.ToJSON(data)
	if err != nil {
		return false, nil, err
	}
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), json)
	if err != nil {
		return false, pod, err
	}

	newPod, ok := obj.(*api.Pod)
	// Check whether the object could be converted to single pod.
	if !ok {
		return false, pod, fmt.Errorf("invalid pod: %#v", obj)
	}

	// Apply default values and validate the pod.
	if err = defaultFn(newPod); err != nil {
		return true, pod, err
	}
	if errs := validation.ValidatePod(newPod); len(errs) > 0 {
		return true, pod, fmt.Errorf("invalid pod: %v", errs)
	}
	v1Pod := &v1.Pod{}
	if err := k8s_api_v1.Convert_core_Pod_To_v1_Pod(newPod, v1Pod, nil); err != nil {
		glog.Errorf("Pod %q failed to convert to v1", newPod.Name)
		return true, nil, err
	}
	return true, v1Pod, nil
}

func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.PodList, err error) {
	obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
	if err != nil {
		return false, pods, err
	}

	newPods, ok := obj.(*api.PodList)
	// Check whether the object could be converted to list of pods.
	if !ok {
		err = fmt.Errorf("invalid pods list: %#v", obj)
		return false, pods, err
	}

	// Apply default values and validate pods.
	for i := range newPods.Items {
		newPod := &newPods.Items[i]
		if err = defaultFn(newPod); err != nil {
			return true, pods, err
		}
		if errs := validation.ValidatePod(newPod); len(errs) > 0 {
			err = fmt.Errorf("invalid pod: %v", errs)
			return true, pods, err
		}
	}
	v1Pods := &v1.PodList{}
	if err := k8s_api_v1.Convert_core_PodList_To_v1_PodList(newPods, v1Pods, nil); err != nil {
		return true, pods, err
	}
	return true, *v1Pods, err
}
<|endoftext|>


<commit_before>package resources

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/atlassian/smith"
	"github.com/atlassian/smith/pkg/util"

	apiext_v1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	crdClientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	api_errors "k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

func BundleCrd() *apiext_v1b1.CustomResourceDefinition {
	return &apiext_v1b1.CustomResourceDefinition{
		ObjectMeta: meta_v1.ObjectMeta{
			Name: smith.BundleResourceName,
		},
		Spec: apiext_v1b1.CustomResourceDefinitionSpec{
			Group:   smith.SmithResourceGroup,
			Version: smith.BundleResourceVersion,
			Names: apiext_v1b1.CustomResourceDefinitionNames{
				Plural:   smith.BundleResourcePlural,
				Singular: smith.BundleResourceSingular,
				Kind:     smith.BundleResourceKind,
			},
		},
	}
}

func EnsureCrdExists(ctx context.Context, scheme *runtime.Scheme, clientset crdClientset.Interface, store smith.ByNameStore, crd *apiext_v1b1.CustomResourceDefinition) error {
	crdGVK := apiext_v1b1.SchemeGroupVersion.WithKind("CustomResourceDefinition")
	for {
		obj, exists, err := store.Get(crdGVK, meta_v1.NamespaceNone, crd.Name)
		if err != nil {
			return err
		}
		if exists {
			o := obj.(*apiext_v1b1.CustomResourceDefinition)
			if !IsEqualCrd(crd, o, scheme) {
				log.Printf("Updating CustomResourceDefinition %s", crd.Name)
				o.Spec = crd.Spec
				o.Annotations = crd.Annotations
				o.Labels = crd.Labels
				_, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Update(o) // This is a CAS
				if err != nil {
					if !api_errors.IsConflict(err) {
						return fmt.Errorf("failed to update CustomResourceDefinition %s: %v", crd.Name, err)
					}
					log.Printf("Conflict updating CustomResourceDefinition %s", crd.Name)
					// wait for store to pick up the object and re-iterate
					if err := util.Sleep(ctx, 1*time.Second); err != nil {
						return err
					}
					continue
				}
			}
		} else {
			log.Printf("Creating CustomResourceDefinition %s", crd.Name)
			_, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
			if err != nil {
				if !api_errors.IsAlreadyExists(err) {
					return fmt.Errorf("failed to create %s CustomResourceDefinition: %v", crd.Name, err)
				}
				log.Printf("CustomResourceDefinition %s was created concurrently", crd.Name)
				// wait for store to pick up the object and re-iterate
				if err := util.Sleep(ctx, 1*time.Second); err != nil {
					return err
				}
				continue
			}
			log.Printf("CustomResourceDefinition %s created", crd.Name)
			// TODO It takes a while for k8s to add a new rest endpoint. Polling?
			if err := util.Sleep(ctx, 15*time.Second); err != nil {
				return err
			}
		}
		break
	}
	return nil
}

func IsEqualCrd(a, b *apiext_v1b1.CustomResourceDefinition, scheme *runtime.Scheme) bool {
	aCopy := *a
	bCopy := *b

	scheme.Default(&aCopy)
	scheme.Default(&bCopy)

	// Ignoring labels and annotations for now
	as := a.Spec
	bs := b.Spec
	return as.Group == bs.Group &&
		as.Version == bs.Version &&
		isEqualCrdNames(a, b) &&
		a.Spec.Scope == b.Spec.Scope
}

func isEqualCrdNames(a, b *apiext_v1b1.CustomResourceDefinition) bool {
	n1 := a.Spec.Names
	n2 := b.Spec.Names
	return n1.Plural == n2.Plural &&
		n1.Singular == n2.Singular &&
		isEqualShortNames(n1.ShortNames, n2.ShortNames) &&
		n1.Kind == n2.Kind &&
		n1.ListKind == n2.ListKind
}

func isEqualShortNames(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}

	for i, v := range a {
		if v != b[i] {
			return false
		}
	}

	return true
}
<commit_msg>Refactor EnsureCrd method (#114)<commit_after>package resources

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/atlassian/smith"
	"github.com/atlassian/smith/pkg/util"

	apiext_v1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	crdClientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	api_errors "k8s.io/apimachinery/pkg/api/errors"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
)

var (
	crdGVK = apiext_v1b1.SchemeGroupVersion.WithKind("CustomResourceDefinition")
)

func BundleCrd() *apiext_v1b1.CustomResourceDefinition {
	return &apiext_v1b1.CustomResourceDefinition{
		ObjectMeta: meta_v1.ObjectMeta{
			Name: smith.BundleResourceName,
		},
		Spec: apiext_v1b1.CustomResourceDefinitionSpec{
			Group:   smith.SmithResourceGroup,
			Version: smith.BundleResourceVersion,
			Names: apiext_v1b1.CustomResourceDefinitionNames{
				Plural:   smith.BundleResourcePlural,
				Singular: smith.BundleResourceSingular,
				Kind:     smith.BundleResourceKind,
			},
		},
	}
}

func EnsureCrdExists(ctx context.Context, scheme *runtime.Scheme, clientset crdClientset.Interface, store smith.ByNameStore, crd *apiext_v1b1.CustomResourceDefinition) error {
	for {
		obj, exists, err := store.Get(crdGVK, meta_v1.NamespaceNone, crd.Name)
		if err != nil {
			return err
		}
		if exists {
			o := obj.(*apiext_v1b1.CustomResourceDefinition)
			if IsEqualCrd(crd, o, scheme) {
				return nil
			}
			log.Printf("Updating CustomResourceDefinition %s", crd.Name)
			o.Spec = crd.Spec
			o.Annotations = crd.Annotations
			o.Labels = crd.Labels
			// TODO this is only necessary because there is no support for generation/observedGeneration at the moment
			o.Status = apiext_v1b1.CustomResourceDefinitionStatus{}
			_, err = clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Update(o) // This is a CAS
			if err == nil {
				log.Printf("CustomResourceDefinition %s updated, waiting for it to become established", crd.Name)
				return WaitForCrdToBecomeEstablished(ctx, store, crd)
			}
			if !api_errors.IsConflict(err) {
				return fmt.Errorf("failed to update CustomResourceDefinition %s: %v", crd.Name, err)
			}
			log.Printf("Conflict updating CustomResourceDefinition %s, retrying", crd.Name)
		} else {
			log.Printf("Creating CustomResourceDefinition %s", crd.Name)
			_, err := clientset.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
			if err == nil {
				log.Printf("CustomResourceDefinition %s created, waiting for it to become established", crd.Name)
				return WaitForCrdToBecomeEstablished(ctx, store, crd)
			}
			if !api_errors.IsAlreadyExists(err) {
				return fmt.Errorf("failed to create %s CustomResourceDefinition: %v", crd.Name, err)
			}
			log.Printf("CustomResourceDefinition %s was created concurrently", crd.Name)
		}
		// wait for store to pick up the object and re-iterate
		if err = util.Sleep(ctx, time.Second); err != nil {
			return err
		}
	}
}

func WaitForCrdToBecomeEstablished(ctx context.Context, store smith.ByNameStore, crd *apiext_v1b1.CustomResourceDefinition) error {
	return wait.PollUntil(100*time.Millisecond, func() (done bool, err error) {
		obj, exists, err := store.Get(crdGVK, meta_v1.NamespaceNone, crd.Name)
		if err != nil || !exists {
			return false, err
		}
		// TODO check generation/observedGeneration when supported
		established := false
		for _, cond := range obj.(*apiext_v1b1.CustomResourceDefinition).Status.Conditions {
			switch cond.Type {
			case apiext_v1b1.Established:
				if cond.Status == apiext_v1b1.ConditionTrue {
					established = true
				}
			case apiext_v1b1.NamesAccepted:
				if cond.Status == apiext_v1b1.ConditionFalse {
					return false, fmt.Errorf("failed to create CRD %s: name conflict: %s", crd.Name, cond.Reason)
				}
			}
		}
		return established, nil
	}, ctx.Done())
}

func IsEqualCrd(a, b *apiext_v1b1.CustomResourceDefinition, scheme *runtime.Scheme) bool {
	aCopy := *a
	bCopy := *b
	a = &aCopy
	b = &bCopy

	scheme.Default(a)
	scheme.Default(b)

	// Ignoring labels and annotations for now
	as := a.Spec
	bs := b.Spec
	return as.Group == bs.Group &&
		as.Version == bs.Version &&
		isEqualCrdNames(a, b) &&
		a.Spec.Scope == b.Spec.Scope
}

func isEqualCrdNames(a, b *apiext_v1b1.CustomResourceDefinition) bool {
	n1 := a.Spec.Names
	n2 := b.Spec.Names
	return n1.Plural == n2.Plural &&
		n1.Singular == n2.Singular &&
		isEqualShortNames(n1.ShortNames, n2.ShortNames) &&
		n1.Kind == n2.Kind &&
		n1.ListKind == n2.ListKind
}

func isEqualShortNames(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}

	for i, v := range a {
		if v != b[i] {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright 2017 uSwitch
//
// 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 server

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net"
	"time"

	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	log "github.com/sirupsen/logrus"
	"github.com/uswitch/k8sc/official"
	"github.com/uswitch/kiam/pkg/aws/sts"
	"github.com/uswitch/kiam/pkg/k8s"
	"github.com/uswitch/kiam/pkg/prefetch"
	pb "github.com/uswitch/kiam/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/security/advancedtls"
	v1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/tools/record"
)

// KiamServerBuilder helps construct the KiamServer
type KiamServerBuilder struct {
	config               *Config
	stsGateway           sts.STSGateway
	podCache             *k8s.PodCache
	namespaceCache       *k8s.NamespaceCache
	eventRecorder        record.EventRecorder
	transportCredentials credentials.TransportCredentials
	tlsConfig            *dynamicTLSConfig
	grpcServer           *grpc.Server
}

func NewKiamServerBuilder(c *Config) *KiamServerBuilder {
	return &KiamServerBuilder{config: c}
}

// WithAWSSTSGateway creates the server with an STS Gateway that interacts
// with AWS APIs. Use WithSTSGateway to provide a different implementation.
func (b *KiamServerBuilder) WithAWSSTSGateway() (*KiamServerBuilder, error) {
	cfg, err := sts.NewServerConfigBuilder().WithRegion(b.config.Region)
	if err != nil {
		return nil, err
	}
	cfg.WithCredentialsFromAssumedRole(sts.NewSTSCredentialsProvider(), b.config.AssumeRoleArn)
	stsGateway, err := sts.DefaultGateway(cfg.Config())
	if err != nil {
		return nil, err
	}

	b.WithSTSGateway(stsGateway)

	return b, nil
}

// WithSTSGateway specifies the STS Gateway to use when issuing credentials
func (b *KiamServerBuilder) WithSTSGateway(gateway sts.STSGateway) {
	b.stsGateway = gateway
}

func newRoleARNResolver(config *Config) (sts.ARNResolver, error) {
	if config.AutoDetectBaseARN {
		log.Infof("detecting arn prefix")
		prefix, err := sts.DetectARNPrefix()
		if err != nil {
			return nil, fmt.Errorf("error detecting arn prefix: %s", err)
		}
		log.Infof("using detected prefix: %s", prefix)
		return sts.DefaultResolver(prefix), nil
	}

	return sts.DefaultResolver(config.RoleBaseARN), nil
}

func eventRecorder(kubeClient *kubernetes.Clientset) record.EventRecorder {
	source := v1.EventSource{Component: "kiam.server"}
	sink := &typedcorev1.EventSinkImpl{
		Interface: kubeClient.CoreV1().Events(""),
	}
	broadcaster := record.NewBroadcaster()
	broadcaster.StartRecordingToSink(sink)

	return broadcaster.NewRecorder(scheme.Scheme, source)
}

// WithKubernetesClient configures the server to use the Kubernetes client to watch
// Pods and Namespaces.
func (b *KiamServerBuilder) WithKubernetesClient() (*KiamServerBuilder, error) {
	client, err := official.NewClient(b.config.KubeConfig)
	if err != nil {
		return nil, err
	}

	arnResolver, err := newRoleARNResolver(b.config)
	if err != nil {
		return nil, err
	}

	podCache := k8s.NewPodCache(arnResolver, k8s.NewListWatch(client, k8s.ResourcePods), b.config.PodSyncInterval, b.config.PrefetchBufferSize)
	nsCache := k8s.NewNamespaceCache(k8s.NewListWatch(client, k8s.ResourceNamespaces), time.Minute)

	b.WithCaches(podCache, nsCache)

	b.eventRecorder = eventRecorder(client)

	return b, nil
}

// WithCaches configures the Pod and Namespace caches used for watching for Kubernetes objects.
func (b *KiamServerBuilder) WithCaches(podCache *k8s.PodCache, nsCache *k8s.NamespaceCache) *KiamServerBuilder {
	b.podCache = podCache
	b.namespaceCache = nsCache

	return b
}

// WithTLS configures the Kiam server to use mutual TLS. Should always be used in production.
func (b *KiamServerBuilder) WithTLS() (*KiamServerBuilder, error) {
	notifyFn := serverTLSMetrics.notifyFunc(x509.ExtKeyUsageServerAuth)
	tlsConfig, err := newDynamicTLSConfig(b.config.TLS.ServerCert, b.config.TLS.ServerKey, b.config.TLS.CA, notifyFn)
	if err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			tlsConfig.Close()
		}
	}()

	creds, err := advancedtls.NewServerCreds(&advancedtls.ServerOptions{
		GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
			return tlsConfig.LoadCert(), nil
		},
		RootCertificateOptions: advancedtls.RootCertificateOptions{
			GetRootCAs: func(_ *advancedtls.GetRootCAsParams) (*advancedtls.GetRootCAsResults, error) {
				return &advancedtls.GetRootCAsResults{TrustCerts: tlsConfig.LoadCACerts()}, nil
			},
		},
		RequireClientCert: true,
	})
	if err != nil {
		return nil, err
	}

	b.transportCredentials = creds
	b.tlsConfig = tlsConfig

	b.grpcServer = grpc.NewServer(
		grpc.Creds(b.transportCredentials),
		grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
		grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
		grpc.KeepaliveParams(b.config.KeepaliveParams),
	)

	return b, nil
}

// WithGRPCServer controls the gRPC Server that the KiamServer will use to listen.
func (b *KiamServerBuilder) WithGRPCServer(server *grpc.Server) *KiamServerBuilder {
	b.grpcServer = server
	return b
}

func (b *KiamServerBuilder) Build() (*KiamServer, error) {
	arnResolver, err := newRoleARNResolver(b.config)
	if err != nil {
		return nil, err
	}

	credentialsCache := sts.DefaultCache(
		b.stsGateway,
		b.config.SessionName,
		b.config.SessionDuration,
		b.config.SessionRefresh,
	)

	listener, err := net.Listen("tcp", b.config.BindAddress)
	if err != nil {
		return nil, err
	}

	srv := &KiamServer{
		tlsConfig:           b.tlsConfig,
		listener:            listener,
		server:              b.grpcServer,
		pods:                b.podCache,
		namespaces:          b.namespaceCache,
		eventRecorder:       b.eventRecorder,
		manager:             prefetch.NewManager(credentialsCache, b.podCache, arnResolver),
		credentialsProvider: credentialsCache,
		assumePolicy: Policies(
			NewRequestingAnnotatedRolePolicy(b.podCache, arnResolver),
			NewNamespacePermittedRoleNamePolicy(!b.config.DisableStrictNamespaceRegexp, b.namespaceCache, arnResolver),
		),
		parallelFetchers: b.config.ParallelFetcherProcesses,
	}
	pb.RegisterKiamServiceServer(b.grpcServer, srv)
	return srv, nil
}
<commit_msg>Set arnResolver on server in builder (#446)<commit_after>// Copyright 2017 uSwitch
//
// 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 server

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"net"
	"time"

	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	log "github.com/sirupsen/logrus"
	"github.com/uswitch/k8sc/official"
	"github.com/uswitch/kiam/pkg/aws/sts"
	"github.com/uswitch/kiam/pkg/k8s"
	"github.com/uswitch/kiam/pkg/prefetch"
	pb "github.com/uswitch/kiam/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/security/advancedtls"
	v1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/tools/record"
)

// KiamServerBuilder helps construct the KiamServer
type KiamServerBuilder struct {
	config               *Config
	stsGateway           sts.STSGateway
	podCache             *k8s.PodCache
	namespaceCache       *k8s.NamespaceCache
	eventRecorder        record.EventRecorder
	transportCredentials credentials.TransportCredentials
	tlsConfig            *dynamicTLSConfig
	grpcServer           *grpc.Server
}

func NewKiamServerBuilder(c *Config) *KiamServerBuilder {
	return &KiamServerBuilder{config: c}
}

// WithAWSSTSGateway creates the server with an STS Gateway that interacts
// with AWS APIs. Use WithSTSGateway to provide a different implementation.
func (b *KiamServerBuilder) WithAWSSTSGateway() (*KiamServerBuilder, error) {
	cfg, err := sts.NewServerConfigBuilder().WithRegion(b.config.Region)
	if err != nil {
		return nil, err
	}
	cfg.WithCredentialsFromAssumedRole(sts.NewSTSCredentialsProvider(), b.config.AssumeRoleArn)
	stsGateway, err := sts.DefaultGateway(cfg.Config())
	if err != nil {
		return nil, err
	}

	b.WithSTSGateway(stsGateway)

	return b, nil
}

// WithSTSGateway specifies the STS Gateway to use when issuing credentials
func (b *KiamServerBuilder) WithSTSGateway(gateway sts.STSGateway) {
	b.stsGateway = gateway
}

func newRoleARNResolver(config *Config) (sts.ARNResolver, error) {
	if config.AutoDetectBaseARN {
		log.Infof("detecting arn prefix")
		prefix, err := sts.DetectARNPrefix()
		if err != nil {
			return nil, fmt.Errorf("error detecting arn prefix: %s", err)
		}
		log.Infof("using detected prefix: %s", prefix)
		return sts.DefaultResolver(prefix), nil
	}

	return sts.DefaultResolver(config.RoleBaseARN), nil
}

func eventRecorder(kubeClient *kubernetes.Clientset) record.EventRecorder {
	source := v1.EventSource{Component: "kiam.server"}
	sink := &typedcorev1.EventSinkImpl{
		Interface: kubeClient.CoreV1().Events(""),
	}
	broadcaster := record.NewBroadcaster()
	broadcaster.StartRecordingToSink(sink)

	return broadcaster.NewRecorder(scheme.Scheme, source)
}

// WithKubernetesClient configures the server to use the Kubernetes client to watch
// Pods and Namespaces.
func (b *KiamServerBuilder) WithKubernetesClient() (*KiamServerBuilder, error) {
	client, err := official.NewClient(b.config.KubeConfig)
	if err != nil {
		return nil, err
	}

	arnResolver, err := newRoleARNResolver(b.config)
	if err != nil {
		return nil, err
	}

	podCache := k8s.NewPodCache(arnResolver, k8s.NewListWatch(client, k8s.ResourcePods), b.config.PodSyncInterval, b.config.PrefetchBufferSize)
	nsCache := k8s.NewNamespaceCache(k8s.NewListWatch(client, k8s.ResourceNamespaces), time.Minute)

	b.WithCaches(podCache, nsCache)

	b.eventRecorder = eventRecorder(client)

	return b, nil
}

// WithCaches configures the Pod and Namespace caches used for watching for Kubernetes objects.
func (b *KiamServerBuilder) WithCaches(podCache *k8s.PodCache, nsCache *k8s.NamespaceCache) *KiamServerBuilder {
	b.podCache = podCache
	b.namespaceCache = nsCache

	return b
}

// WithTLS configures the Kiam server to use mutual TLS. Should always be used in production.
func (b *KiamServerBuilder) WithTLS() (*KiamServerBuilder, error) {
	notifyFn := serverTLSMetrics.notifyFunc(x509.ExtKeyUsageServerAuth)
	tlsConfig, err := newDynamicTLSConfig(b.config.TLS.ServerCert, b.config.TLS.ServerKey, b.config.TLS.CA, notifyFn)
	if err != nil {
		return nil, err
	}

	defer func() {
		if err != nil {
			tlsConfig.Close()
		}
	}()

	creds, err := advancedtls.NewServerCreds(&advancedtls.ServerOptions{
		GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
			return tlsConfig.LoadCert(), nil
		},
		RootCertificateOptions: advancedtls.RootCertificateOptions{
			GetRootCAs: func(_ *advancedtls.GetRootCAsParams) (*advancedtls.GetRootCAsResults, error) {
				return &advancedtls.GetRootCAsResults{TrustCerts: tlsConfig.LoadCACerts()}, nil
			},
		},
		RequireClientCert: true,
	})
	if err != nil {
		return nil, err
	}

	b.transportCredentials = creds
	b.tlsConfig = tlsConfig

	b.grpcServer = grpc.NewServer(
		grpc.Creds(b.transportCredentials),
		grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
		grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
		grpc.KeepaliveParams(b.config.KeepaliveParams),
	)

	return b, nil
}

// WithGRPCServer controls the gRPC Server that the KiamServer will use to listen.
func (b *KiamServerBuilder) WithGRPCServer(server *grpc.Server) *KiamServerBuilder {
	b.grpcServer = server
	return b
}

func (b *KiamServerBuilder) Build() (*KiamServer, error) {
	arnResolver, err := newRoleARNResolver(b.config)
	if err != nil {
		return nil, err
	}

	credentialsCache := sts.DefaultCache(
		b.stsGateway,
		b.config.SessionName,
		b.config.SessionDuration,
		b.config.SessionRefresh,
	)

	listener, err := net.Listen("tcp", b.config.BindAddress)
	if err != nil {
		return nil, err
	}

	srv := &KiamServer{
		tlsConfig:           b.tlsConfig,
		listener:            listener,
		server:              b.grpcServer,
		pods:                b.podCache,
		namespaces:          b.namespaceCache,
		eventRecorder:       b.eventRecorder,
		manager:             prefetch.NewManager(credentialsCache, b.podCache, arnResolver),
		credentialsProvider: credentialsCache,
		assumePolicy: Policies(
			NewRequestingAnnotatedRolePolicy(b.podCache, arnResolver),
			NewNamespacePermittedRoleNamePolicy(!b.config.DisableStrictNamespaceRegexp, b.namespaceCache, arnResolver),
		),
		parallelFetchers: b.config.ParallelFetcherProcesses,
		arnResolver:      arnResolver,
	}
	pb.RegisterKiamServiceServer(b.grpcServer, srv)
	return srv, nil
}
<|endoftext|>


<commit_before>package system

import (
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
)

// LUtimesNano is used to change access and modification time of the specified path.
// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm.
func LUtimesNano(path string, ts []syscall.Timespec) error {
	var _path *byte
	_path, err := unix.BytePtrFromString(path)
	if err != nil {
		return err
	}

	if _, _, err := unix.Syscall(unix.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != unix.ENOSYS {
		return err
	}

	return nil
}
<commit_msg>Fix LUtimesNano on FreeBSD<commit_after>package system

import (
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
)

// LUtimesNano is used to change access and modification time of the specified path.
// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm.
func LUtimesNano(path string, ts []syscall.Timespec) error {
	atFdCwd := unix.AT_FDCWD

	var _path *byte
	_path, err := unix.BytePtrFromString(path)
	if err != nil {
		return err
	}
	if _, _, err := unix.Syscall6(unix.SYS_UTIMENSAT, uintptr(atFdCwd), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), unix.AT_SYMLINK_NOFOLLOW, 0, 0); err != 0 && err != unix.ENOSYS {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 The Kubeflow 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 trainer

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/golang/protobuf/proto"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/tools/record"

	tfv1alpha1 "github.com/kubeflow/tf-operator/pkg/apis/tensorflow/v1alpha1"
	tfJobFake "github.com/kubeflow/tf-operator/pkg/client/clientset/versioned/fake"
	"github.com/kubeflow/tf-operator/pkg/util"
)

var (
	groupVersionKind = schema.GroupVersionKind{
		Group:   tfv1alpha1.GroupName,
		Version: tfv1alpha1.GroupVersion,
		Kind:    tfv1alpha1.TFJobResourceKind,
	}
)

func TestTFReplicaSet(t *testing.T) {
	clientSet := fake.NewSimpleClientset()

	testSchedulerName := "test-scheduler"

	jobSpec := &tfv1alpha1.TFJob{
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "some-job",
			UID:  "some-uid",
		},
		Spec: tfv1alpha1.TFJobSpec{
			RuntimeId: "some-runtime",
			ReplicaSpecs: []*tfv1alpha1.TFReplicaSpec{
				{
					Replicas: proto.Int32(2),
					TFPort:   proto.Int32(10),
					Template: &v1.PodTemplateSpec{
						Spec: v1.PodSpec{
							Containers: []v1.Container{
								{
									Name: "tensorflow",
								},
							},
						},
					},
					TFReplicaType: tfv1alpha1.PS,
				},
			},
			SchedulerName: testSchedulerName,
		},
	}

	recorder := record.NewFakeRecorder(100)
	job, err := initJob(clientSet, &tfJobFake.Clientset{}, recorder, jobSpec)

	if err != nil {
		t.Fatalf("initJob failed: %v", err)
	}

	replica, err := NewTFReplicaSet(clientSet, recorder, *jobSpec.Spec.ReplicaSpecs[0], job)

	if err != nil {
		t.Fatalf("NewTFReplicaSet failed: %v", err)
	}

	if err := replica.SyncPods(); err != nil {
		t.Fatalf("replica.SyncPods() error; %v", err)
	}

	if err := replica.SyncServices(); err != nil {
		t.Fatalf("replica.SyncServices() error; %v", err)
	}

	trueVal := true
	expectedOwnerReference := meta_v1.OwnerReference{
		APIVersion:         groupVersionKind.GroupVersion().String(),
		Kind:               groupVersionKind.Kind,
		Name:               "some-job",
		UID:                "some-uid",
		Controller:         &trueVal,
		BlockOwnerDeletion: &trueVal,
	}

	for index := 0; index < 2; index++ {
		// Expected labels
		expectedLabels := map[string]string{
			"kubeflow.org": "",
			"task_index":   fmt.Sprintf("%v", index),
			"job_type":     "PS",
			"runtime_id":   "some-runtime",
			"tf_job_name":  "some-job",
		}

		// Check that a service was created.
		sList, err := clientSet.CoreV1().Services(replica.Job.job.ObjectMeta.Namespace).List(meta_v1.ListOptions{})
		if err != nil {
			t.Fatalf("List services error; %v", err)
		}

		if len(sList.Items) != 2 {
			t.Fatalf("Expected 2 services got %v", len(sList.Items))
		}

		s := sList.Items[index]

		if !reflect.DeepEqual(expectedLabels, s.ObjectMeta.Labels) {
			t.Fatalf("Service Labels; Got %v Want: %v", s.ObjectMeta.Labels, expectedLabels)
		}

		name := fmt.Sprintf("some-job-ps-some-runtime-%v", index)
		if s.ObjectMeta.Name != name {
			t.Fatalf("Job.ObjectMeta.Name = %v; want %v", s.ObjectMeta.Name, name)
		}

		if len(s.ObjectMeta.OwnerReferences) != 1 {
			t.Fatalf("Expected 1 owner reference got %v", len(s.ObjectMeta.OwnerReferences))
		}

		if !reflect.DeepEqual(s.ObjectMeta.OwnerReferences[0], expectedOwnerReference) {
			t.Fatalf("Service.Metadata.OwnerReferences; Got %v; want %v", util.Pformat(s.ObjectMeta.OwnerReferences[0]), util.Pformat(expectedOwnerReference))
		}

		// Check that a pod was created.
		l, err := clientSet.CoreV1().Pods(replica.Job.job.ObjectMeta.Namespace).List(meta_v1.ListOptions{})
		if err != nil {
			t.Fatalf("List pods error; %v", err)
		}

		if len(l.Items) != 2 {
			t.Fatalf("Expected 1 pod got %v", len(l.Items))
		}

		p := l.Items[index]

		if !reflect.DeepEqual(expectedLabels, p.ObjectMeta.Labels) {
			t.Fatalf("Pod Labels; Got %v Want: %v", expectedLabels, p.ObjectMeta.Labels)
		}

		if len(p.Spec.Containers) != 1 {
			t.Fatalf("Expected 1 container got %v", len(p.Spec.Containers))
		}

		if len(p.ObjectMeta.OwnerReferences) != 1 {
			t.Fatalf("Expected 1 owner reference got %v", len(p.ObjectMeta.OwnerReferences))
		}

		if !reflect.DeepEqual(p.ObjectMeta.OwnerReferences[0], expectedOwnerReference) {
			t.Fatalf("Pod.Metadata.OwnerReferences; Got %v; want %v", util.Pformat(p.ObjectMeta.OwnerReferences[0]), util.Pformat(expectedOwnerReference))
		}

		c := p.Spec.Containers[0]
		if len(c.Env) != 1 {
			t.Fatalf("Expected 1 environment variable got %v", len(c.Env))
		}

		if strings.Compare(p.Spec.SchedulerName, testSchedulerName) != 0 {
			t.Fatalf("p.Spec.Template.Spec.SchedulerName; Got %v; want %v", p.Spec.SchedulerName, testSchedulerName)
		}

		actualTFConfig := &TFConfig{}
		if err := json.Unmarshal([]byte(c.Env[0].Value), actualTFConfig); err != nil {
			t.Fatalf("Could not unmarshal TFConfig %v", err)
		}

		expectedTFConfig := &TFConfig{
			Cluster: ClusterSpec{},
			Task: TaskSpec{
				Type:  "ps",
				Index: index,
			},
			Environment: "cloud",
		}

		if !reflect.DeepEqual(expectedTFConfig, actualTFConfig) {
			t.Fatalf("Got %v, Want %v", actualTFConfig, expectedTFConfig)
		}
	}
	// Delete the job.
	// N.B it doesn't look like the Fake clientset is sophisticated enough to delete jobs in response to a
	// DeleteCollection request (deleting individual jobs does appear to work with the Fake). So if we were to list
	// the jobs after calling Delete we'd still see the job. So we will rely on E2E tests to verify Delete works
	// correctly.
	if err := replica.Delete(); err != nil {
		t.Fatalf("replica.Delete() error; %v", err)
	}
}

func TestTFReplicaSetStatusFromPodList(t *testing.T) {
	type TestCase struct {
		PodList  v1.PodList
		Name     string
		Expected tfv1alpha1.ReplicaState
	}

	cases := []TestCase{
		{
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateRunning,
		},
		{
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 0,
										},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateSucceeded,
		},
		{
			// Multiple containers; make sure we match by name.
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "other",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
								},
								{
									Name: "master",
									State: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 0,
										},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateSucceeded,
		},
		{
			// Container failed with permanent error and then got restarted.
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
									LastTerminationState: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 100,
											Message:  "some reason",
										},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateFailed,
		},
		{
			// Multiple Pods; check we get the most recent.
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
								},
							},
							StartTime: &meta_v1.Time{
								Time: time.Date(2017, 0, 0, 0, 0, 0, 0, time.UTC),
							},
						},
					},
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 100,
											Message:  "some reason",
										},
									},
								},
							},
							StartTime: &meta_v1.Time{
								Time: time.Date(2018, 0, 0, 0, 0, 0, 0, time.UTC),
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateFailed,
		},
	}

	for _, c := range cases {
		status := replicaStatusFromPodList(c.PodList, c.Name)
		if status != c.Expected {
			t.Errorf("replicaStatusFromPodList(%+v, %v)=%v ; want %v", c.PodList, c.Name, status, c.Expected)
		}
	}
}
<commit_msg>Fix output on test failure (#511)<commit_after>// Copyright 2018 The Kubeflow 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 trainer

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/golang/protobuf/proto"
	"k8s.io/api/core/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/tools/record"

	tfv1alpha1 "github.com/kubeflow/tf-operator/pkg/apis/tensorflow/v1alpha1"
	tfJobFake "github.com/kubeflow/tf-operator/pkg/client/clientset/versioned/fake"
	"github.com/kubeflow/tf-operator/pkg/util"
)

var (
	groupVersionKind = schema.GroupVersionKind{
		Group:   tfv1alpha1.GroupName,
		Version: tfv1alpha1.GroupVersion,
		Kind:    tfv1alpha1.TFJobResourceKind,
	}
)

func TestTFReplicaSet(t *testing.T) {
	clientSet := fake.NewSimpleClientset()

	testSchedulerName := "test-scheduler"

	jobSpec := &tfv1alpha1.TFJob{
		ObjectMeta: meta_v1.ObjectMeta{
			Name: "some-job",
			UID:  "some-uid",
		},
		Spec: tfv1alpha1.TFJobSpec{
			RuntimeId: "some-runtime",
			ReplicaSpecs: []*tfv1alpha1.TFReplicaSpec{
				{
					Replicas: proto.Int32(2),
					TFPort:   proto.Int32(10),
					Template: &v1.PodTemplateSpec{
						Spec: v1.PodSpec{
							Containers: []v1.Container{
								{
									Name: "tensorflow",
								},
							},
						},
					},
					TFReplicaType: tfv1alpha1.PS,
				},
			},
			SchedulerName: testSchedulerName,
		},
	}

	recorder := record.NewFakeRecorder(100)
	job, err := initJob(clientSet, &tfJobFake.Clientset{}, recorder, jobSpec)

	if err != nil {
		t.Fatalf("initJob failed: %v", err)
	}

	replica, err := NewTFReplicaSet(clientSet, recorder, *jobSpec.Spec.ReplicaSpecs[0], job)

	if err != nil {
		t.Fatalf("NewTFReplicaSet failed: %v", err)
	}

	if err := replica.SyncPods(); err != nil {
		t.Fatalf("replica.SyncPods() error; %v", err)
	}

	if err := replica.SyncServices(); err != nil {
		t.Fatalf("replica.SyncServices() error; %v", err)
	}

	trueVal := true
	expectedOwnerReference := meta_v1.OwnerReference{
		APIVersion:         groupVersionKind.GroupVersion().String(),
		Kind:               groupVersionKind.Kind,
		Name:               "some-job",
		UID:                "some-uid",
		Controller:         &trueVal,
		BlockOwnerDeletion: &trueVal,
	}

	for index := 0; index < 2; index++ {
		// Expected labels
		expectedLabels := map[string]string{
			"kubeflow.org": "",
			"task_index":   fmt.Sprintf("%v", index),
			"job_type":     "PS",
			"runtime_id":   "some-runtime",
			"tf_job_name":  "some-job",
		}

		// Check that a service was created.
		sList, err := clientSet.CoreV1().Services(replica.Job.job.ObjectMeta.Namespace).List(meta_v1.ListOptions{})
		if err != nil {
			t.Fatalf("List services error; %v", err)
		}

		if len(sList.Items) != 2 {
			t.Fatalf("Expected 2 services got %v", len(sList.Items))
		}

		s := sList.Items[index]

		if !reflect.DeepEqual(expectedLabels, s.ObjectMeta.Labels) {
			t.Fatalf("Service Labels; Got %v Want: %v", s.ObjectMeta.Labels, expectedLabels)
		}

		name := fmt.Sprintf("some-job-ps-some-runtime-%v", index)
		if s.ObjectMeta.Name != name {
			t.Fatalf("Job.ObjectMeta.Name = %v; want %v", s.ObjectMeta.Name, name)
		}

		if len(s.ObjectMeta.OwnerReferences) != 1 {
			t.Fatalf("Expected 1 owner reference got %v", len(s.ObjectMeta.OwnerReferences))
		}

		if !reflect.DeepEqual(s.ObjectMeta.OwnerReferences[0], expectedOwnerReference) {
			t.Fatalf("Service.Metadata.OwnerReferences; Got %v; want %v", util.Pformat(s.ObjectMeta.OwnerReferences[0]), util.Pformat(expectedOwnerReference))
		}

		// Check that a pod was created.
		l, err := clientSet.CoreV1().Pods(replica.Job.job.ObjectMeta.Namespace).List(meta_v1.ListOptions{})
		if err != nil {
			t.Fatalf("List pods error; %v", err)
		}

		if len(l.Items) != 2 {
			t.Fatalf("Expected 1 pod got %v", len(l.Items))
		}

		p := l.Items[index]

		if !reflect.DeepEqual(expectedLabels, p.ObjectMeta.Labels) {
			t.Fatalf("Pod Labels; Got %v Want: %v", p.ObjectMeta.Labels, expectedLabels)
		}

		if len(p.Spec.Containers) != 1 {
			t.Fatalf("Expected 1 container got %v", len(p.Spec.Containers))
		}

		if len(p.ObjectMeta.OwnerReferences) != 1 {
			t.Fatalf("Expected 1 owner reference got %v", len(p.ObjectMeta.OwnerReferences))
		}

		if !reflect.DeepEqual(p.ObjectMeta.OwnerReferences[0], expectedOwnerReference) {
			t.Fatalf("Pod.Metadata.OwnerReferences; Got %v; want %v", util.Pformat(p.ObjectMeta.OwnerReferences[0]), util.Pformat(expectedOwnerReference))
		}

		c := p.Spec.Containers[0]
		if len(c.Env) != 1 {
			t.Fatalf("Expected 1 environment variable got %v", len(c.Env))
		}

		if strings.Compare(p.Spec.SchedulerName, testSchedulerName) != 0 {
			t.Fatalf("p.Spec.Template.Spec.SchedulerName; Got %v; want %v", p.Spec.SchedulerName, testSchedulerName)
		}

		actualTFConfig := &TFConfig{}
		if err := json.Unmarshal([]byte(c.Env[0].Value), actualTFConfig); err != nil {
			t.Fatalf("Could not unmarshal TFConfig %v", err)
		}

		expectedTFConfig := &TFConfig{
			Cluster: ClusterSpec{},
			Task: TaskSpec{
				Type:  "ps",
				Index: index,
			},
			Environment: "cloud",
		}

		if !reflect.DeepEqual(expectedTFConfig, actualTFConfig) {
			t.Fatalf("Got %v, Want %v", actualTFConfig, expectedTFConfig)
		}
	}
	// Delete the job.
	// N.B it doesn't look like the Fake clientset is sophisticated enough to delete jobs in response to a
	// DeleteCollection request (deleting individual jobs does appear to work with the Fake). So if we were to list
	// the jobs after calling Delete we'd still see the job. So we will rely on E2E tests to verify Delete works
	// correctly.
	if err := replica.Delete(); err != nil {
		t.Fatalf("replica.Delete() error; %v", err)
	}
}

func TestTFReplicaSetStatusFromPodList(t *testing.T) {
	type TestCase struct {
		PodList  v1.PodList
		Name     string
		Expected tfv1alpha1.ReplicaState
	}

	cases := []TestCase{
		{
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateRunning,
		},
		{
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 0,
										},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateSucceeded,
		},
		{
			// Multiple containers; make sure we match by name.
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "other",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
								},
								{
									Name: "master",
									State: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 0,
										},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateSucceeded,
		},
		{
			// Container failed with permanent error and then got restarted.
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
									LastTerminationState: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 100,
											Message:  "some reason",
										},
									},
								},
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateFailed,
		},
		{
			// Multiple Pods; check we get the most recent.
			PodList: v1.PodList{
				Items: []v1.Pod{
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Running: &v1.ContainerStateRunning{},
									},
								},
							},
							StartTime: &meta_v1.Time{
								Time: time.Date(2017, 0, 0, 0, 0, 0, 0, time.UTC),
							},
						},
					},
					{
						Status: v1.PodStatus{
							ContainerStatuses: []v1.ContainerStatus{
								{
									Name: "master",
									State: v1.ContainerState{
										Terminated: &v1.ContainerStateTerminated{
											ExitCode: 100,
											Message:  "some reason",
										},
									},
								},
							},
							StartTime: &meta_v1.Time{
								Time: time.Date(2018, 0, 0, 0, 0, 0, 0, time.UTC),
							},
						},
					},
				},
			},
			Name:     "master",
			Expected: tfv1alpha1.ReplicaStateFailed,
		},
	}

	for _, c := range cases {
		status := replicaStatusFromPodList(c.PodList, c.Name)
		if status != c.Expected {
			t.Errorf("replicaStatusFromPodList(%+v, %v)=%v ; want %v", c.PodList, c.Name, status, c.Expected)
		}
	}
}
<|endoftext|>


<commit_before>/*
picel (picture element) is an image processing micro service.

	https://github.com/henvic/picel

*/
package main

import (
	"flag"
	"fmt"
	"github.com/henvic/picel/image"
	"github.com/henvic/picel/logger"
	"github.com/henvic/picel/server"
	"net/http"
	"os/exec"
	"strings"
)

const (
	Version        = "0.0.1"
	defaultAddr    = ":8123"
	defaultBackend = ""
)

var (
	addr        string
	verbose     bool
	flagVersion bool
)

func init() {
	flag.StringVar(&addr, "addr", defaultAddr, "Serving address")
	flag.StringVar(&server.Backend, "backend", defaultBackend, "Image storage back-end server")
	flag.BoolVar(&verbose, "verbose", false, "Pipe image processing output to stderr/stdout")
	flag.BoolVar(&flagVersion, "version", false, "Print version information and quit")
}

func showVersion() {
	fmt.Println("picel version", Version)
}

func existsDependency(cmd string) bool {
	_, err := exec.LookPath(cmd)
	return err == nil
}

func checkMissingDependencies(arg ...string) {
	var missing []string
	left := len(arg)

	for left > 0 {
		if !existsDependency(arg[left-1]) {
			missing = append(missing, arg[left-1])
		}
		left--
	}

	if missing != nil {
		logger.Stderr.Println("Dependencies missing:", strings.Join(missing, ", "))
	}
}

func main() {
	flag.Parse()

	image.Verbose = verbose
	server.Verbose = verbose

	if flagVersion {
		showVersion()
		return
	}

	checkMissingDependencies("convert", "cwebp", "gif2webp")

	logger.Stdout.Println(fmt.Sprintf("picel started listening on %v", addr))

	if server.Backend != "" {
		logger.Stdout.Println(fmt.Sprintf("Single backend mode: %v", server.Backend))
	}

	http.HandleFunc("/", server.Handler)
	panic(http.ListenAndServe(addr, nil))
}
<commit_msg>Source formatting.<commit_after>/*
picel (picture element) is an image processing micro service.

	https://github.com/henvic/picel

*/
package main

import (
	"flag"
	"fmt"
	"github.com/henvic/picel/image"
	"github.com/henvic/picel/logger"
	"github.com/henvic/picel/server"
	"net/http"
	"os/exec"
	"strings"
)

const (
	// Current picel version
	Version        = "0.0.1"
	defaultAddr    = ":8123"
	defaultBackend = ""
)

var (
	addr        string
	verbose     bool
	flagVersion bool
)

func init() {
	flag.StringVar(&addr, "addr", defaultAddr, "Serving address")
	flag.StringVar(&server.Backend, "backend", defaultBackend, "Image storage back-end server")
	flag.BoolVar(&verbose, "verbose", false, "Pipe image processing output to stderr/stdout")
	flag.BoolVar(&flagVersion, "version", false, "Print version information and quit")
}

func showVersion() {
	fmt.Println("picel version", Version)
}

func existsDependency(cmd string) bool {
	_, err := exec.LookPath(cmd)
	return err == nil
}

func checkMissingDependencies(arg ...string) {
	var missing []string
	left := len(arg)

	for left > 0 {
		if !existsDependency(arg[left-1]) {
			missing = append(missing, arg[left-1])
		}
		left--
	}

	if missing != nil {
		logger.Stderr.Println("Dependencies missing:", strings.Join(missing, ", "))
	}
}

func main() {
	flag.Parse()

	image.Verbose = verbose
	server.Verbose = verbose

	if flagVersion {
		showVersion()
		return
	}

	checkMissingDependencies("convert", "cwebp", "gif2webp")

	logger.Stdout.Println(fmt.Sprintf("picel started listening on %v", addr))

	if server.Backend != "" {
		logger.Stdout.Println(fmt.Sprintf("Single backend mode: %v", server.Backend))
	}

	http.HandleFunc("/", server.Handler)
	panic(http.ListenAndServe(addr, nil))
}
<|endoftext|>


<commit_before>package gamedayapi

type Pitch struct {
	Des string `xml:"des,attr"`
}
<commit_msg>Filling in the pitch attributes.<commit_after>package gamedayapi

import "encoding/xml"

type Pitch struct {
	XMLName        xml.Name `xml:"pitch"`
	ID             string   `xml:"id,attr"`
	Type           string   `xml:"type,attr"`
	Des            string   `xml:"des,attr"`
	DesEs          string   `xml:"des_es,attr"`
	TFS            string   `xml:"tfs,attr"`
	TFSZulu        string   `xml:"tfs_zulu,attr"`
	X              string   `xml:"x,attr"`
	Y              string   `xml:"y,attr"`
	SvID           string   `xml:"sv_id,attr"`
	StartSpeed     string   `xml:"start_speed,attr"`
	EndSpeed       string   `xml:"end_speed,attr"`
	SzTop          string   `xml:"sz_top,attr"`
	SzBottom       string   `xml:"sz_bot,attr"`
	PFXX           string   `xml:"pfx_x,attr"`
	PFXZ           string   `xml:"pfx_z,attr"`
	PX             string   `xml:"px,attr"`
	PZ             string   `xml:"pz,attr"`
	X0             string   `xml:"x0,attr"`
	Y0             string   `xml:"y0,attr"`
	Z0             string   `xml:"z0,attr"`
	VX0            string   `xml:"vx0,attr"`
	VY0            string   `xml:"vy0,attr"`
	VZ0            string   `xml:"vz0,attr"`
	AX             string   `xml:"ax,attr"`
	AY             string   `xml:"ay,attr"`
	AZ             string   `xml:"az,attr"`
	BreakY         string   `xml:"break_y,attr"`
	BreakAngle     string   `xml:"break_angle,attr"`
	BreakLength    string   `xml:"break_length,attr"`
	PitchType      string   `xml:"pitch_type,attr"`
	TypeConfidence string   `xml:"type_confidence,attr"`
	Zone           string   `xml:"zone,attr"`
	Nasty          string   `xml:"nasty,attr"`
	SpinDir        string   `xml:"spin_dir,attr"`
	SpinRate       string   `xml:"spin_rate,attr"`
	CC             string   `xml:"cc,attr"`
	MT             string   `xml:"mt,attr"`
}
<|endoftext|>


<commit_before>package logging

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/hectane/go-acl/api"
	"golang.org/x/sys/windows"
)

func dupToStd(_ *os.File) interface{} {
	return errors.New("dupToStd() is not implemented on windows")
}

type WindowsSysInfo interface {
	SysInfo
	Owner() *windows.SID
	Group() *windows.SID
	DACL() windows.Handle
	SACL() windows.Handle
	SecurityDescriptor() windows.Handle
}

type windowsSysInfo struct {
	path  string
	data  *syscall.Win32FileAttributeData
	owner *windows.SID
	group *windows.SID
	dacl  windows.Handle
	sacl  windows.Handle
}

func GetSysInfo(dir string, info os.FileInfo) (SysInfo, error) {
	wi := windowsSysInfo{
		path: filepath.Join(dir, info.Name()),
		data: info.Sys().(*syscall.Win32FileAttributeData),
	}
	var secDesc windows.Handle
	err := api.GetNamedSecurityInfo(
		wi.path,
		api.SE_FILE_OBJECT,
		api.OWNER_SECURITY_INFORMATION,
		&wi.owner,
		&wi.group,
		&wi.dacl,
		&wi.sacl,
		&secDesc,
	)
	if err != nil && !errors.Is(err, windows.ERROR_SUCCESS) {
		return nil, err
	}
	if _, err = windows.LocalFree(secDesc); err != nil && !errors.Is(err, windows.ERROR_SUCCESS) {
		return nil, err
	}
	return &wi, nil
}

func (wi *windowsSysInfo) SetOwnerAndGroup(name string) error {
	return api.SetNamedSecurityInfo(name, api.SE_FILE_OBJECT, api.OWNER_SECURITY_INFORMATION, wi.owner, wi.group, wi.dacl, wi.sacl)
}

func (wi *windowsSysInfo) HaveSameOwnerAndGroup(s SysInfo) bool {
	eq := func(a, b *windows.SID) bool {
		if a == b {
			return true
		}
		if a == nil || b == nil {
			return false
		}
		return a.Equals(b)
	}
	owi, ok := s.(*windowsSysInfo)
	return ok && eq(wi.owner, owi.owner) && eq(wi.group, owi.group)
}

func (wi *windowsSysInfo) BirthTime() time.Time {
	return time.Unix(0, wi.data.CreationTime.Nanoseconds())
}

func (wi *windowsSysInfo) Owner() *windows.SID {
	return wi.owner
}

func (wi *windowsSysInfo) Group() *windows.SID {
	return wi.group
}

func (wi *windowsSysInfo) DACL() windows.Handle {
	return wi.dacl
}

func (wi *windowsSysInfo) SACL() windows.Handle {
	return wi.sacl
}

func (wi *windowsSysInfo) String() string {
	return fmt.Sprintf("CTIME %v, UID %v, GID %v", wi.BirthTime(), wi.owner, wi.group)
}
<commit_msg>Prevent windows.ERROR_SUCCESS from being returned from SetOwnerAndGroup<commit_after>package logging

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/hectane/go-acl/api"
	"golang.org/x/sys/windows"
)

func dupToStd(_ *os.File) interface{} {
	return errors.New("dupToStd() is not implemented on windows")
}

type WindowsSysInfo interface {
	SysInfo
	Owner() *windows.SID
	Group() *windows.SID
	DACL() windows.Handle
	SACL() windows.Handle
	SecurityDescriptor() windows.Handle
}

type windowsSysInfo struct {
	path  string
	data  *syscall.Win32FileAttributeData
	owner *windows.SID
	group *windows.SID
	dacl  windows.Handle
	sacl  windows.Handle
}

func GetSysInfo(dir string, info os.FileInfo) (SysInfo, error) {
	wi := windowsSysInfo{
		path: filepath.Join(dir, info.Name()),
		data: info.Sys().(*syscall.Win32FileAttributeData),
	}
	var secDesc windows.Handle
	err := api.GetNamedSecurityInfo(
		wi.path,
		api.SE_FILE_OBJECT,
		api.OWNER_SECURITY_INFORMATION,
		&wi.owner,
		&wi.group,
		&wi.dacl,
		&wi.sacl,
		&secDesc,
	)
	if err != nil && !errors.Is(err, windows.ERROR_SUCCESS) {
		return nil, err
	}
	if _, err = windows.LocalFree(secDesc); err != nil && !errors.Is(err, windows.ERROR_SUCCESS) {
		return nil, err
	}
	return &wi, nil
}

func (wi *windowsSysInfo) SetOwnerAndGroup(name string) error {
	err := api.SetNamedSecurityInfo(name, api.SE_FILE_OBJECT, api.OWNER_SECURITY_INFORMATION, wi.owner, wi.group, wi.dacl, wi.sacl)
	if err != nil {
		// On some systems it seems SetNamedSecurityInfo will return ERROR_SUCCESS on success... this is an odd violation of the principle
		// that windows APIs return err = nil on success but okay
		if errors.Is(err, windows.ERROR_SUCCESS) {
			return nil
		}
		return err
	}
	return nil
}

func (wi *windowsSysInfo) HaveSameOwnerAndGroup(s SysInfo) bool {
	eq := func(a, b *windows.SID) bool {
		if a == b {
			return true
		}
		if a == nil || b == nil {
			return false
		}
		return a.Equals(b)
	}
	owi, ok := s.(*windowsSysInfo)
	return ok && eq(wi.owner, owi.owner) && eq(wi.group, owi.group)
}

func (wi *windowsSysInfo) BirthTime() time.Time {
	return time.Unix(0, wi.data.CreationTime.Nanoseconds())
}

func (wi *windowsSysInfo) Owner() *windows.SID {
	return wi.owner
}

func (wi *windowsSysInfo) Group() *windows.SID {
	return wi.group
}

func (wi *windowsSysInfo) DACL() windows.Handle {
	return wi.dacl
}

func (wi *windowsSysInfo) SACL() windows.Handle {
	return wi.sacl
}

func (wi *windowsSysInfo) String() string {
	return fmt.Sprintf("CTIME %v, UID %v, GID %v", wi.BirthTime(), wi.owner, wi.group)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 openapi

import (
	"fmt"
	"net/http"
	"time"

	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/klog"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	"k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator"
)

const (
	successfulUpdateDelay      = time.Minute
	successfulUpdateDelayLocal = time.Second
	failedUpdateMaxExpDelay    = time.Hour
)

type syncAction int

const (
	syncRequeue syncAction = iota
	syncRequeueRateLimited
	syncNothing
)

// AggregationController periodically check for changes in OpenAPI specs of APIServices and update/remove
// them if necessary.
type AggregationController struct {
	openAPIAggregationManager aggregator.SpecAggregator
	queue                     workqueue.RateLimitingInterface
	downloader                *aggregator.Downloader

	// To allow injection for testing.
	syncHandler func(key string) (syncAction, error)
}

// NewAggregationController creates new OpenAPI aggregation controller.
func NewAggregationController(downloader *aggregator.Downloader, openAPIAggregationManager aggregator.SpecAggregator) *AggregationController {
	c := &AggregationController{
		openAPIAggregationManager: openAPIAggregationManager,
		queue: workqueue.NewNamedRateLimitingQueue(
			workqueue.NewItemExponentialFailureRateLimiter(successfulUpdateDelay, failedUpdateMaxExpDelay), "APIServiceOpenAPIAggregationControllerQueue1"),
		downloader: downloader,
	}

	c.syncHandler = c.sync

	// update each service at least once, also those which are not coming from APIServices, namely local services
	for _, name := range openAPIAggregationManager.GetAPIServiceNames() {
		c.queue.AddAfter(name, time.Second)
	}

	return c
}

// Run starts OpenAPI AggregationController
func (c *AggregationController) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	klog.Infof("Starting OpenAPI AggregationController")
	defer klog.Infof("Shutting down OpenAPI AggregationController")

	go wait.Until(c.runWorker, time.Second, stopCh)

	<-stopCh
}

func (c *AggregationController) runWorker() {
	for c.processNextWorkItem() {
	}
}

// processNextWorkItem deals with one key off the queue.  It returns false when it's time to quit.
func (c *AggregationController) processNextWorkItem() bool {
	key, quit := c.queue.Get()
	defer c.queue.Done(key)
	if quit {
		return false
	}

	if aggregator.IsLocalAPIService(key.(string)) {
		// for local delegation targets that are aggregated once per second, log at
		// higher level to avoid flooding the log
		klog.V(5).Infof("OpenAPI AggregationController: Processing item %s", key)
	} else {
		klog.Infof("OpenAPI AggregationController: Processing item %s", key)
	}

	action, err := c.syncHandler(key.(string))
	if err == nil {
		c.queue.Forget(key)
	} else {
		utilruntime.HandleError(fmt.Errorf("loading OpenAPI spec for %q failed with: %v", key, err))
	}

	switch action {
	case syncRequeue:
		if aggregator.IsLocalAPIService(key.(string)) {
			klog.V(7).Infof("OpenAPI AggregationController: action for local item %s: Requeue after %s.", key, successfulUpdateDelayLocal)
			c.queue.AddAfter(key, successfulUpdateDelayLocal)
		} else {
			klog.V(7).Infof("OpenAPI AggregationController: action for item %s: Requeue.", key)
			c.queue.AddAfter(key, successfulUpdateDelay)
		}
	case syncRequeueRateLimited:
		klog.Infof("OpenAPI AggregationController: action for item %s: Rate Limited Requeue.", key)
		c.queue.AddRateLimited(key)
	case syncNothing:
		klog.Infof("OpenAPI AggregationController: action for item %s: Nothing (removed from the queue).", key)
	}

	return true
}

func (c *AggregationController) sync(key string) (syncAction, error) {
	handler, etag, exists := c.openAPIAggregationManager.GetAPIServiceInfo(key)
	if !exists || handler == nil {
		return syncNothing, nil
	}
	returnSpec, newEtag, httpStatus, err := c.downloader.Download(handler, etag)
	switch {
	case err != nil:
		return syncRequeueRateLimited, err
	case httpStatus == http.StatusNotModified:
	case httpStatus == http.StatusNotFound || returnSpec == nil:
		return syncRequeueRateLimited, fmt.Errorf("OpenAPI spec does not exist")
	case httpStatus == http.StatusOK:
		if err := c.openAPIAggregationManager.UpdateAPIServiceSpec(key, returnSpec, newEtag); err != nil {
			return syncRequeueRateLimited, err
		}
	}
	return syncRequeue, nil
}

// AddAPIService adds a new API Service to OpenAPI Aggregation.
func (c *AggregationController) AddAPIService(handler http.Handler, apiService *v1.APIService) {
	if apiService.Spec.Service == nil {
		return
	}
	if err := c.openAPIAggregationManager.AddUpdateAPIService(handler, apiService); err != nil {
		utilruntime.HandleError(fmt.Errorf("adding %q to AggregationController failed with: %v", apiService.Name, err))
	}
	c.queue.AddAfter(apiService.Name, time.Second)
}

// UpdateAPIService updates API Service's info and handler.
func (c *AggregationController) UpdateAPIService(handler http.Handler, apiService *v1.APIService) {
	if apiService.Spec.Service == nil {
		return
	}
	if err := c.openAPIAggregationManager.AddUpdateAPIService(handler, apiService); err != nil {
		utilruntime.HandleError(fmt.Errorf("updating %q to AggregationController failed with: %v", apiService.Name, err))
	}
	key := apiService.Name
	if c.queue.NumRequeues(key) > 0 {
		// The item has failed before. Remove it from failure queue and
		// update it in a second
		c.queue.Forget(key)
		c.queue.AddAfter(key, time.Second)
	}
	// Else: The item has been succeeded before and it will be updated soon (after successfulUpdateDelay)
	// we don't add it again as it will cause a duplication of items.
}

// RemoveAPIService removes API Service from OpenAPI Aggregation Controller.
func (c *AggregationController) RemoveAPIService(apiServiceName string) {
	if err := c.openAPIAggregationManager.RemoveAPIServiceSpec(apiServiceName); err != nil {
		utilruntime.HandleError(fmt.Errorf("removing %q from AggregationController failed with: %v", apiServiceName, err))
	}
	// This will only remove it if it was failing before. If it was successful, processNextWorkItem will figure it out
	// and will not add it again to the queue.
	c.queue.Forget(apiServiceName)
}
<commit_msg>kube-aggregator: increase log level of AggregationController API group logging<commit_after>/*
Copyright 2016 The Kubernetes 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 openapi

import (
	"fmt"
	"net/http"
	"time"

	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/klog"
	"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	"k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator"
)

const (
	successfulUpdateDelay      = time.Minute
	successfulUpdateDelayLocal = time.Second
	failedUpdateMaxExpDelay    = time.Hour
)

type syncAction int

const (
	syncRequeue syncAction = iota
	syncRequeueRateLimited
	syncNothing
)

// AggregationController periodically check for changes in OpenAPI specs of APIServices and update/remove
// them if necessary.
type AggregationController struct {
	openAPIAggregationManager aggregator.SpecAggregator
	queue                     workqueue.RateLimitingInterface
	downloader                *aggregator.Downloader

	// To allow injection for testing.
	syncHandler func(key string) (syncAction, error)
}

// NewAggregationController creates new OpenAPI aggregation controller.
func NewAggregationController(downloader *aggregator.Downloader, openAPIAggregationManager aggregator.SpecAggregator) *AggregationController {
	c := &AggregationController{
		openAPIAggregationManager: openAPIAggregationManager,
		queue: workqueue.NewNamedRateLimitingQueue(
			workqueue.NewItemExponentialFailureRateLimiter(successfulUpdateDelay, failedUpdateMaxExpDelay), "APIServiceOpenAPIAggregationControllerQueue1"),
		downloader: downloader,
	}

	c.syncHandler = c.sync

	// update each service at least once, also those which are not coming from APIServices, namely local services
	for _, name := range openAPIAggregationManager.GetAPIServiceNames() {
		c.queue.AddAfter(name, time.Second)
	}

	return c
}

// Run starts OpenAPI AggregationController
func (c *AggregationController) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	klog.Infof("Starting OpenAPI AggregationController")
	defer klog.Infof("Shutting down OpenAPI AggregationController")

	go wait.Until(c.runWorker, time.Second, stopCh)

	<-stopCh
}

func (c *AggregationController) runWorker() {
	for c.processNextWorkItem() {
	}
}

// processNextWorkItem deals with one key off the queue.  It returns false when it's time to quit.
func (c *AggregationController) processNextWorkItem() bool {
	key, quit := c.queue.Get()
	defer c.queue.Done(key)
	if quit {
		return false
	}

	if aggregator.IsLocalAPIService(key.(string)) {
		// for local delegation targets that are aggregated once per second, log at
		// higher level to avoid flooding the log
		klog.V(6).Infof("OpenAPI AggregationController: Processing item %s", key)
	} else {
		klog.V(4).Infof("OpenAPI AggregationController: Processing item %s", key)
	}

	action, err := c.syncHandler(key.(string))
	if err == nil {
		c.queue.Forget(key)
	} else {
		utilruntime.HandleError(fmt.Errorf("loading OpenAPI spec for %q failed with: %v", key, err))
	}

	switch action {
	case syncRequeue:
		if aggregator.IsLocalAPIService(key.(string)) {
			klog.V(7).Infof("OpenAPI AggregationController: action for local item %s: Requeue after %s.", key, successfulUpdateDelayLocal)
			c.queue.AddAfter(key, successfulUpdateDelayLocal)
		} else {
			klog.V(7).Infof("OpenAPI AggregationController: action for item %s: Requeue.", key)
			c.queue.AddAfter(key, successfulUpdateDelay)
		}
	case syncRequeueRateLimited:
		klog.Infof("OpenAPI AggregationController: action for item %s: Rate Limited Requeue.", key)
		c.queue.AddRateLimited(key)
	case syncNothing:
		klog.Infof("OpenAPI AggregationController: action for item %s: Nothing (removed from the queue).", key)
	}

	return true
}

func (c *AggregationController) sync(key string) (syncAction, error) {
	handler, etag, exists := c.openAPIAggregationManager.GetAPIServiceInfo(key)
	if !exists || handler == nil {
		return syncNothing, nil
	}
	returnSpec, newEtag, httpStatus, err := c.downloader.Download(handler, etag)
	switch {
	case err != nil:
		return syncRequeueRateLimited, err
	case httpStatus == http.StatusNotModified:
	case httpStatus == http.StatusNotFound || returnSpec == nil:
		return syncRequeueRateLimited, fmt.Errorf("OpenAPI spec does not exist")
	case httpStatus == http.StatusOK:
		if err := c.openAPIAggregationManager.UpdateAPIServiceSpec(key, returnSpec, newEtag); err != nil {
			return syncRequeueRateLimited, err
		}
	}
	return syncRequeue, nil
}

// AddAPIService adds a new API Service to OpenAPI Aggregation.
func (c *AggregationController) AddAPIService(handler http.Handler, apiService *v1.APIService) {
	if apiService.Spec.Service == nil {
		return
	}
	if err := c.openAPIAggregationManager.AddUpdateAPIService(handler, apiService); err != nil {
		utilruntime.HandleError(fmt.Errorf("adding %q to AggregationController failed with: %v", apiService.Name, err))
	}
	c.queue.AddAfter(apiService.Name, time.Second)
}

// UpdateAPIService updates API Service's info and handler.
func (c *AggregationController) UpdateAPIService(handler http.Handler, apiService *v1.APIService) {
	if apiService.Spec.Service == nil {
		return
	}
	if err := c.openAPIAggregationManager.AddUpdateAPIService(handler, apiService); err != nil {
		utilruntime.HandleError(fmt.Errorf("updating %q to AggregationController failed with: %v", apiService.Name, err))
	}
	key := apiService.Name
	if c.queue.NumRequeues(key) > 0 {
		// The item has failed before. Remove it from failure queue and
		// update it in a second
		c.queue.Forget(key)
		c.queue.AddAfter(key, time.Second)
	}
	// Else: The item has been succeeded before and it will be updated soon (after successfulUpdateDelay)
	// we don't add it again as it will cause a duplication of items.
}

// RemoveAPIService removes API Service from OpenAPI Aggregation Controller.
func (c *AggregationController) RemoveAPIService(apiServiceName string) {
	if err := c.openAPIAggregationManager.RemoveAPIServiceSpec(apiServiceName); err != nil {
		utilruntime.HandleError(fmt.Errorf("removing %q from AggregationController failed with: %v", apiServiceName, err))
	}
	// This will only remove it if it was failing before. If it was successful, processNextWorkItem will figure it out
	// and will not add it again to the queue.
	c.queue.Forget(apiServiceName)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The OpenEBS 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 v1alpha1

import (
	"fmt"
	menv "github.com/openebs/maya/pkg/env/v1alpha1"
)

// EnvLister abstracts listing of env structures based on version
type EnvLister func(v version) (l *envList, err error)

// EnvList returns a list of env instances based on version
func EnvList(v version) (l *envList, err error) {
	err = fmt.Errorf("invalid version '%+v': failed to list environment values", v)
	switch v {
	case version070:
		return envList070(), nil
	default:
		return
	}
	return
}

// envInstallConfig returns a list of environment variable info specific to
// install config
func envInstallConfig() (l *envList) {
	l = &envList{}
	l.Items = append(l.Items, &env{Key: InstallerConfigName, Value: "maya-install-config-default-0.7.0"})
	return
}

// envList070 returns a list of environment variable info specific to 0.7.0
// version
func envList070() (l *envList) {
	l = &envList{}
	l.Items = append(l.Items, &env{Key: DefaultCstorSparsePool, Value: "false"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateFeatureGateENVK, Value: "true"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreateJivaVolumeENVK, Value: "jiva-volume-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToReadJivaVolumeENVK, Value: "jiva-volume-read-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeleteJivaVolumeENVK, Value: "jiva-volume-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreateCStorVolumeENVK, Value: "cstor-volume-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToReadCStorVolumeENVK, Value: "cstor-volume-read-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeleteCStorVolumeENVK, Value: "cstor-volume-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreatePoolENVK, Value: "cstor-pool-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeletePoolENVK, Value: "cstor-pool-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToListVolumeENVK, Value: "jiva-volume-list-default-0.6.0,jiva-volume-list-default-0.7.0,cstor-volume-list-default-0.7.0"})

	return
}
<commit_msg>chore(install): Add snapshot cast as environment variables m-apiserver. (#646)<commit_after>/*
Copyright 2018 The OpenEBS 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 v1alpha1

import (
	"fmt"

	menv "github.com/openebs/maya/pkg/env/v1alpha1"
)

// EnvLister abstracts listing of env structures based on version
type EnvLister func(v version) (l *envList, err error)

// EnvList returns a list of env instances based on version
func EnvList(v version) (l *envList, err error) {
	err = fmt.Errorf("invalid version '%+v': failed to list environment values", v)
	switch v {
	case version070:
		return envList070(), nil
	default:
		return
	}
	return
}

// envInstallConfig returns a list of environment variable info specific to
// install config
func envInstallConfig() (l *envList) {
	l = &envList{}
	l.Items = append(l.Items, &env{Key: InstallerConfigName, Value: "maya-install-config-default-0.7.0"})
	return
}

// envList070 returns a list of environment variable info specific to 0.7.0
// version
func envList070() (l *envList) {
	l = &envList{}
	l.Items = append(l.Items, &env{Key: DefaultCstorSparsePool, Value: "false"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateFeatureGateENVK, Value: "true"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreateJivaVolumeENVK, Value: "jiva-volume-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToReadJivaVolumeENVK, Value: "jiva-volume-read-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeleteJivaVolumeENVK, Value: "jiva-volume-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreateCStorVolumeENVK, Value: "cstor-volume-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToReadCStorVolumeENVK, Value: "cstor-volume-read-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeleteCStorVolumeENVK, Value: "cstor-volume-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreatePoolENVK, Value: "cstor-pool-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeletePoolENVK, Value: "cstor-pool-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToListVolumeENVK, Value: "jiva-volume-list-default-0.6.0,jiva-volume-list-default-0.7.0,cstor-volume-list-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreateCStorSnapshotENVK, Value: "cstor-snapshot-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeleteCStorSnapshotENVK, Value: "cstor-snapshot-delete-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToCreateJivaSnapshotENVK, Value: "jiva-snapshot-create-default-0.7.0"})
	l.Items = append(l.Items, &env{Key: menv.CASTemplateToDeleteJivaSnapshotENVK, Value: "jiva-snapshot-delete-default-0.7.0"})
	return
}
<|endoftext|>