<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 apiserver

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

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/apimachinery/announced"
	"k8s.io/apimachinery/pkg/apimachinery/registered"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/version"
	"k8s.io/apiserver/pkg/endpoints/discovery"
	genericregistry "k8s.io/apiserver/pkg/registry/generic"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset"
	internalinformers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion"
	"k8s.io/apiextensions-apiserver/pkg/controller/finalizer"
	"k8s.io/apiextensions-apiserver/pkg/controller/status"
	"k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition"

	// make sure the generated client works
	_ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	_ "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
	_ "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion"
)

var (
	groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
	Registry             = registered.NewOrDie("")
	Scheme               = runtime.NewScheme()
	Codecs               = serializer.NewCodecFactory(Scheme)

	// if you modify this, make sure you update the crEncoder
	unversionedVersion = schema.GroupVersion{Group: "", Version: "v1"}
	unversionedTypes   = []runtime.Object{
		&metav1.Status{},
		&metav1.WatchEvent{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	}
)

func init() {
	install.Install(groupFactoryRegistry, Registry, Scheme)

	// we need to add the options to empty v1
	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})

	Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...)
}

type ExtraConfig struct {
	CRDRESTOptionsGetter genericregistry.RESTOptionsGetter
}

type Config struct {
	GenericConfig *genericapiserver.RecommendedConfig
	ExtraConfig   ExtraConfig
}

type completedConfig struct {
	GenericConfig genericapiserver.CompletedConfig
	ExtraConfig   *ExtraConfig
}

type CompletedConfig struct {
	// Embed a private pointer that cannot be instantiated outside of this package.
	*completedConfig
}

type CustomResourceDefinitions struct {
	GenericAPIServer *genericapiserver.GenericAPIServer

	// provided for easier embedding
	Informers internalinformers.SharedInformerFactory
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (cfg *Config) Complete() CompletedConfig {
	c := completedConfig{
		cfg.GenericConfig.Complete(),
		&cfg.ExtraConfig,
	}

	c.GenericConfig.EnableDiscovery = false
	c.GenericConfig.Version = &version.Info{
		Major: "0",
		Minor: "1",
	}

	return CompletedConfig{&c}
}

// New returns a new instance of CustomResourceDefinitions from the given config.
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {
	genericServer, err := c.GenericConfig.New("apiextensions-apiserver", delegationTarget)
	if err != nil {
		return nil, err
	}

	s := &CustomResourceDefinitions{
		GenericAPIServer: genericServer,
	}

	apiResourceConfig := c.GenericConfig.MergedResourceConfig
	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Registry, Scheme, metav1.ParameterCodec, Codecs)
	if apiResourceConfig.VersionEnabled(v1beta1.SchemeGroupVersion) {
		apiGroupInfo.GroupMeta.GroupVersion = v1beta1.SchemeGroupVersion
		storage := map[string]rest.Storage{}
		// customresourcedefinitions
		customResourceDefintionStorage := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
		storage["customresourcedefinitions"] = customResourceDefintionStorage
		storage["customresourcedefinitions/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefintionStorage)

		apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = storage
	}

	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	crdClient, err := internalclientset.NewForConfig(s.GenericAPIServer.LoopbackClientConfig)
	if err != nil {
		// it's really bad that this is leaking here, but until we can fix the test (which I'm pretty sure isn't even testing what it wants to test),
		// we need to be able to move forward
		kubeAPIVersions := os.Getenv("KUBE_API_VERSIONS")
		if len(kubeAPIVersions) == 0 {
			return nil, fmt.Errorf("failed to create clientset: %v", err)
		}

		// KUBE_API_VERSIONS is used in test-update-storage-objects.sh, disabling a number of API
		// groups. This leads to a nil client above and undefined behaviour further down.
		//
		// TODO: get rid of KUBE_API_VERSIONS or define sane behaviour if set
		glog.Errorf("Failed to create clientset with KUBE_API_VERSIONS=%q. KUBE_API_VERSIONS is only for testing. Things will break.", kubeAPIVersions)
	}
	s.Informers = internalinformers.NewSharedInformerFactory(crdClient, 5*time.Minute)

	delegateHandler := delegationTarget.UnprotectedHandler()
	if delegateHandler == nil {
		delegateHandler = http.NotFoundHandler()
	}

	versionDiscoveryHandler := &versionDiscoveryHandler{
		discovery: map[schema.GroupVersion]*discovery.APIVersionHandler{},
		delegate:  delegateHandler,
	}
	groupDiscoveryHandler := &groupDiscoveryHandler{
		discovery: map[string]*discovery.APIGroupHandler{},
		delegate:  delegateHandler,
	}
	crdHandler := NewCustomResourceDefinitionHandler(
		versionDiscoveryHandler,
		groupDiscoveryHandler,
		s.GenericAPIServer.RequestContextMapper(),
		s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
		delegateHandler,
		c.ExtraConfig.CRDRESTOptionsGetter,
		c.GenericConfig.AdmissionControl,
	)
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", crdHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.HandlePrefix("/apis/", crdHandler)

	// this only happens when KUBE_API_VERSIONS is set.  We must return without adding controllers or poststarthooks which would affect healthz
	if crdClient == nil {
		return s, nil
	}

	crdController := NewDiscoveryController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler, c.GenericConfig.RequestContextMapper)
	namingController := status.NewNamingConditionController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions())
	finalizingController := finalizer.NewCRDFinalizer(
		s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
		crdClient.Apiextensions(),
		crdHandler,
	)

	s.GenericAPIServer.AddPostStartHook("start-apiextensions-informers", func(context genericapiserver.PostStartHookContext) error {
		s.Informers.Start(context.StopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error {
		go crdController.Run(context.StopCh)
		go namingController.Run(context.StopCh)
		go finalizingController.Run(5, context.StopCh)
		return nil
	})

	return s, nil
}

func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
	ret := serverstorage.NewResourceConfig()
	// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
	ret.EnableVersions(
		v1beta1.SchemeGroupVersion,
	)

	return ret
}
<commit_msg>Include original error in the error message.<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 apiserver

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

	"github.com/golang/glog"

	"k8s.io/apimachinery/pkg/apimachinery/announced"
	"k8s.io/apimachinery/pkg/apimachinery/registered"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/version"
	"k8s.io/apiserver/pkg/endpoints/discovery"
	genericregistry "k8s.io/apiserver/pkg/registry/generic"
	"k8s.io/apiserver/pkg/registry/rest"
	genericapiserver "k8s.io/apiserver/pkg/server"
	serverstorage "k8s.io/apiserver/pkg/server/storage"

	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset"
	internalinformers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion"
	"k8s.io/apiextensions-apiserver/pkg/controller/finalizer"
	"k8s.io/apiextensions-apiserver/pkg/controller/status"
	"k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition"

	// make sure the generated client works
	_ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	_ "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
	_ "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion"
)

var (
	groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
	Registry             = registered.NewOrDie("")
	Scheme               = runtime.NewScheme()
	Codecs               = serializer.NewCodecFactory(Scheme)

	// if you modify this, make sure you update the crEncoder
	unversionedVersion = schema.GroupVersion{Group: "", Version: "v1"}
	unversionedTypes   = []runtime.Object{
		&metav1.Status{},
		&metav1.WatchEvent{},
		&metav1.APIVersions{},
		&metav1.APIGroupList{},
		&metav1.APIGroup{},
		&metav1.APIResourceList{},
	}
)

func init() {
	install.Install(groupFactoryRegistry, Registry, Scheme)

	// we need to add the options to empty v1
	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})

	Scheme.AddUnversionedTypes(unversionedVersion, unversionedTypes...)
}

type ExtraConfig struct {
	CRDRESTOptionsGetter genericregistry.RESTOptionsGetter
}

type Config struct {
	GenericConfig *genericapiserver.RecommendedConfig
	ExtraConfig   ExtraConfig
}

type completedConfig struct {
	GenericConfig genericapiserver.CompletedConfig
	ExtraConfig   *ExtraConfig
}

type CompletedConfig struct {
	// Embed a private pointer that cannot be instantiated outside of this package.
	*completedConfig
}

type CustomResourceDefinitions struct {
	GenericAPIServer *genericapiserver.GenericAPIServer

	// provided for easier embedding
	Informers internalinformers.SharedInformerFactory
}

// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (cfg *Config) Complete() CompletedConfig {
	c := completedConfig{
		cfg.GenericConfig.Complete(),
		&cfg.ExtraConfig,
	}

	c.GenericConfig.EnableDiscovery = false
	c.GenericConfig.Version = &version.Info{
		Major: "0",
		Minor: "1",
	}

	return CompletedConfig{&c}
}

// New returns a new instance of CustomResourceDefinitions from the given config.
func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*CustomResourceDefinitions, error) {
	genericServer, err := c.GenericConfig.New("apiextensions-apiserver", delegationTarget)
	if err != nil {
		return nil, err
	}

	s := &CustomResourceDefinitions{
		GenericAPIServer: genericServer,
	}

	apiResourceConfig := c.GenericConfig.MergedResourceConfig
	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Registry, Scheme, metav1.ParameterCodec, Codecs)
	if apiResourceConfig.VersionEnabled(v1beta1.SchemeGroupVersion) {
		apiGroupInfo.GroupMeta.GroupVersion = v1beta1.SchemeGroupVersion
		storage := map[string]rest.Storage{}
		// customresourcedefinitions
		customResourceDefintionStorage := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
		storage["customresourcedefinitions"] = customResourceDefintionStorage
		storage["customresourcedefinitions/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefintionStorage)

		apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = storage
	}

	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
		return nil, err
	}

	crdClient, err := internalclientset.NewForConfig(s.GenericAPIServer.LoopbackClientConfig)
	if err != nil {
		// it's really bad that this is leaking here, but until we can fix the test (which I'm pretty sure isn't even testing what it wants to test),
		// we need to be able to move forward
		kubeAPIVersions := os.Getenv("KUBE_API_VERSIONS")
		if len(kubeAPIVersions) == 0 {
			return nil, fmt.Errorf("failed to create clientset: %v", err)
		}

		// KUBE_API_VERSIONS is used in test-update-storage-objects.sh, disabling a number of API
		// groups. This leads to a nil client above and undefined behaviour further down.
		//
		// TODO: get rid of KUBE_API_VERSIONS or define sane behaviour if set
		glog.Errorf("Failed to create clientset with KUBE_API_VERSIONS=%q: %v. KUBE_API_VERSIONS is only for testing. Things will break.",
			kubeAPIVersions, err)
	}
	s.Informers = internalinformers.NewSharedInformerFactory(crdClient, 5*time.Minute)

	delegateHandler := delegationTarget.UnprotectedHandler()
	if delegateHandler == nil {
		delegateHandler = http.NotFoundHandler()
	}

	versionDiscoveryHandler := &versionDiscoveryHandler{
		discovery: map[schema.GroupVersion]*discovery.APIVersionHandler{},
		delegate:  delegateHandler,
	}
	groupDiscoveryHandler := &groupDiscoveryHandler{
		discovery: map[string]*discovery.APIGroupHandler{},
		delegate:  delegateHandler,
	}
	crdHandler := NewCustomResourceDefinitionHandler(
		versionDiscoveryHandler,
		groupDiscoveryHandler,
		s.GenericAPIServer.RequestContextMapper(),
		s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
		delegateHandler,
		c.ExtraConfig.CRDRESTOptionsGetter,
		c.GenericConfig.AdmissionControl,
	)
	s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", crdHandler)
	s.GenericAPIServer.Handler.NonGoRestfulMux.HandlePrefix("/apis/", crdHandler)

	// this only happens when KUBE_API_VERSIONS is set.  We must return without adding controllers or poststarthooks which would affect healthz
	if crdClient == nil {
		return s, nil
	}

	crdController := NewDiscoveryController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler, c.GenericConfig.RequestContextMapper)
	namingController := status.NewNamingConditionController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions())
	finalizingController := finalizer.NewCRDFinalizer(
		s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
		crdClient.Apiextensions(),
		crdHandler,
	)

	s.GenericAPIServer.AddPostStartHook("start-apiextensions-informers", func(context genericapiserver.PostStartHookContext) error {
		s.Informers.Start(context.StopCh)
		return nil
	})
	s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error {
		go crdController.Run(context.StopCh)
		go namingController.Run(context.StopCh)
		go finalizingController.Run(5, context.StopCh)
		return nil
	})

	return s, nil
}

func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
	ret := serverstorage.NewResourceConfig()
	// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
	ret.EnableVersions(
		v1beta1.SchemeGroupVersion,
	)

	return ret
}
<|endoftext|>


<commit_before>// Copyright 2017 The nats-operator 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 constants

const (
	DefaultNatsVersion = "1.1.0"

	// ClientPort is the port for the clients.
	ClientPort = 4222

	// ClusterPort is the port for server routes.
	ClusterPort = 6222

	// MonitoringPort is the port for the server monitoring endpoint.
	MonitoringPort = 8222

	// ConfigMapVolumeName is the name of the volume use for the shared config map.
	ConfigMapVolumeName = "nats-config"

	// ConfigMapMountPath is the path on which the shared ConfigMap
	// for the NATS cluster will be located.
	ConfigMapMountPath = "/etc/nats-config"

	// ConfigFileName is the name of the config file used by the NATS server.
	ConfigFileName = "nats.conf"

	// ConfigFilePath is the absolute path to the NATS config file.
	ConfigFilePath = ConfigMapMountPath + "/" + ConfigFileName

	// PidFileVolumeName is the name of the volume used for the NATS server pid file.
	PidFileVolumeName = "pid"

	// PidFileName is the pid file name.
	PidFileName = "gnatsd.pid"

	// PidFileMountPath is the absolute path to the directory where NATS
	// will be leaving its pid file.
	PidFileMountPath = "/var/run/nats"

	// PidFilePath is the location of the pid file.
	PidFilePath = PidFileMountPath + "/" + PidFileName

	// ServerSecretVolumeName is the name of the volume used for the server certs.
	ServerSecretVolumeName = "server-tls-certs"

	// ServerCertsMountPath is the path where the server certificates
	// to secure clients connections are located.
	ServerCertsMountPath = "/etc/nats-server-tls-certs"
	ServerCAFilePath     = ServerCertsMountPath + "/ca.pem"
	ServerCertFilePath   = ServerCertsMountPath + "/server.pem"
	ServerKeyFilePath    = ServerCertsMountPath + "/server-key.pem"

	// RoutesSecretVolumeName is the name of the volume used for the routes certs.
	RoutesSecretVolumeName = "routes-tls-certs"

	// RoutesCertsMountPath is the path where the certificates
	// to secure routes connections are located.
	RoutesCertsMountPath = "/etc/nats-routes-tls-certs"
	RoutesCAFilePath     = RoutesCertsMountPath + "/ca.pem"
	RoutesCertFilePath   = RoutesCertsMountPath + "/route.pem"
	RoutesKeyFilePath    = RoutesCertsMountPath + "/route-key.pem"

	// Default Docker Images
	DefaultServerImage = "nats"
	DefaultReloaderImage = "connecteverything/nats-server-config-reloader"
	DefaultReloaderImageTag = "0.2.2-v1alpha2"
	DefaultReloaderImagePullPolicy = "IfNotPresent"
)

<commit_msg>Add comment to DefaultNatsVersion constant<commit_after>// Copyright 2017 The nats-operator 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 constants

const (
	// DefaultNatsVersion is the nats server version to use.
	DefaultNatsVersion = "1.1.0"

	// ClientPort is the port for the clients.
	ClientPort = 4222

	// ClusterPort is the port for server routes.
	ClusterPort = 6222

	// MonitoringPort is the port for the server monitoring endpoint.
	MonitoringPort = 8222

	// ConfigMapVolumeName is the name of the volume use for the shared config map.
	ConfigMapVolumeName = "nats-config"

	// ConfigMapMountPath is the path on which the shared ConfigMap
	// for the NATS cluster will be located.
	ConfigMapMountPath = "/etc/nats-config"

	// ConfigFileName is the name of the config file used by the NATS server.
	ConfigFileName = "nats.conf"

	// ConfigFilePath is the absolute path to the NATS config file.
	ConfigFilePath = ConfigMapMountPath + "/" + ConfigFileName

	// PidFileVolumeName is the name of the volume used for the NATS server pid file.
	PidFileVolumeName = "pid"

	// PidFileName is the pid file name.
	PidFileName = "gnatsd.pid"

	// PidFileMountPath is the absolute path to the directory where NATS
	// will be leaving its pid file.
	PidFileMountPath = "/var/run/nats"

	// PidFilePath is the location of the pid file.
	PidFilePath = PidFileMountPath + "/" + PidFileName

	// ServerSecretVolumeName is the name of the volume used for the server certs.
	ServerSecretVolumeName = "server-tls-certs"

	// ServerCertsMountPath is the path where the server certificates
	// to secure clients connections are located.
	ServerCertsMountPath = "/etc/nats-server-tls-certs"
	ServerCAFilePath     = ServerCertsMountPath + "/ca.pem"
	ServerCertFilePath   = ServerCertsMountPath + "/server.pem"
	ServerKeyFilePath    = ServerCertsMountPath + "/server-key.pem"

	// RoutesSecretVolumeName is the name of the volume used for the routes certs.
	RoutesSecretVolumeName = "routes-tls-certs"

	// RoutesCertsMountPath is the path where the certificates
	// to secure routes connections are located.
	RoutesCertsMountPath = "/etc/nats-routes-tls-certs"
	RoutesCAFilePath     = RoutesCertsMountPath + "/ca.pem"
	RoutesCertFilePath   = RoutesCertsMountPath + "/route.pem"
	RoutesKeyFilePath    = RoutesCertsMountPath + "/route-key.pem"

	// Default Docker Images
	DefaultServerImage             = "nats"
	DefaultReloaderImage           = "connecteverything/nats-server-config-reloader"
	DefaultReloaderImageTag        = "0.2.2-v1alpha2"
	DefaultReloaderImagePullPolicy = "IfNotPresent"
)
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 fileutil

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"testing"
	"time"
)

func TestPurgeFile(t *testing.T) {
	dir, err := ioutil.TempDir("", "purgefile")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	for i := 0; i < 5; i++ {
		_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
		if err != nil {
			t.Fatal(err)
		}
	}

	stop := make(chan struct{})
	errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
	for i := 5; i < 10; i++ {
		_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
		if err != nil {
			t.Fatal(err)
		}
		time.Sleep(2 * time.Millisecond)
	}
	fnames, err := ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	wnames := []string{"7.test", "8.test", "9.test"}
	if !reflect.DeepEqual(fnames, wnames) {
		t.Errorf("filenames = %v, want %v", fnames, wnames)
	}
	select {
	case err := <-errch:
		t.Errorf("unexpected purge error %v", err)
	case <-time.After(time.Millisecond):
	}
	close(stop)
}

func TestPurgeFileHoldingLock(t *testing.T) {
	dir, err := ioutil.TempDir("", "purgefile")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	for i := 0; i < 10; i++ {
		_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
		if err != nil {
			t.Fatal(err)
		}
	}

	// create a purge barrier at 5
	l, err := NewLock(path.Join(dir, fmt.Sprintf("%d.test", 5)))
	err = l.Lock()
	if err != nil {
		t.Fatal(err)
	}

	stop := make(chan struct{})
	errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
	time.Sleep(5 * time.Millisecond)

	fnames, err := ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	wnames := []string{"5.test", "6.test", "7.test", "8.test", "9.test"}
	if !reflect.DeepEqual(fnames, wnames) {
		t.Errorf("filenames = %v, want %v", fnames, wnames)
	}
	select {
	case err := <-errch:
		t.Errorf("unexpected purge error %v", err)
	case <-time.After(time.Millisecond):
	}

	// remove the purge barrier
	err = l.Unlock()
	if err != nil {
		t.Fatal(err)
	}
	err = l.Destroy()
	if err != nil {
		t.Fatal(err)
	}

	time.Sleep(5 * time.Millisecond)

	fnames, err = ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	wnames = []string{"7.test", "8.test", "9.test"}
	if !reflect.DeepEqual(fnames, wnames) {
		t.Errorf("filenames = %v, want %v", fnames, wnames)
	}
	select {
	case err := <-errch:
		t.Errorf("unexpected purge error %v", err)
	case <-time.After(time.Millisecond):
	}

	close(stop)
}
<commit_msg>pkg/fileutil: wait longer before checking purge results<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 fileutil

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"testing"
	"time"
)

func TestPurgeFile(t *testing.T) {
	dir, err := ioutil.TempDir("", "purgefile")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	for i := 0; i < 5; i++ {
		_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
		if err != nil {
			t.Fatal(err)
		}
	}

	stop := make(chan struct{})
	errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
	for i := 5; i < 10; i++ {
		_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
		if err != nil {
			t.Fatal(err)
		}
		time.Sleep(2 * time.Millisecond)
	}
	fnames, err := ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	wnames := []string{"7.test", "8.test", "9.test"}
	if !reflect.DeepEqual(fnames, wnames) {
		t.Errorf("filenames = %v, want %v", fnames, wnames)
	}
	select {
	case err := <-errch:
		t.Errorf("unexpected purge error %v", err)
	case <-time.After(time.Millisecond):
	}
	close(stop)
}

func TestPurgeFileHoldingLock(t *testing.T) {
	dir, err := ioutil.TempDir("", "purgefile")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	for i := 0; i < 10; i++ {
		_, err := os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
		if err != nil {
			t.Fatal(err)
		}
	}

	// create a purge barrier at 5
	l, err := NewLock(path.Join(dir, fmt.Sprintf("%d.test", 5)))
	err = l.Lock()
	if err != nil {
		t.Fatal(err)
	}

	stop := make(chan struct{})
	errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
	time.Sleep(20 * time.Millisecond)

	fnames, err := ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	wnames := []string{"5.test", "6.test", "7.test", "8.test", "9.test"}
	if !reflect.DeepEqual(fnames, wnames) {
		t.Errorf("filenames = %v, want %v", fnames, wnames)
	}
	select {
	case err := <-errch:
		t.Errorf("unexpected purge error %v", err)
	case <-time.After(time.Millisecond):
	}

	// remove the purge barrier
	err = l.Unlock()
	if err != nil {
		t.Fatal(err)
	}
	err = l.Destroy()
	if err != nil {
		t.Fatal(err)
	}

	time.Sleep(20 * time.Millisecond)

	fnames, err = ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	wnames = []string{"7.test", "8.test", "9.test"}
	if !reflect.DeepEqual(fnames, wnames) {
		t.Errorf("filenames = %v, want %v", fnames, wnames)
	}
	select {
	case err := <-errch:
		t.Errorf("unexpected purge error %v", err)
	case <-time.After(time.Millisecond):
	}

	close(stop)
}
<|endoftext|>


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

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cluster

import (
	"fmt"
	"net"
	"os/exec"
	"reflect"
	"regexp"

	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/libmachine/host"
	"github.com/pkg/errors"
	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/machine"
)

// HostIP gets the ip address to be used for mapping host -> VM and VM -> host
func HostIP(host *host.Host) (net.IP, error) {
	switch host.DriverName {
	case driver.Docker:
		return oci.RoutableHostIPFromInside(oci.Docker, host.Name)
	case driver.Podman:
		return oci.RoutableHostIPFromInside(oci.Podman, host.Name)
	case driver.KVM2:
		return net.ParseIP("192.168.39.1"), nil
	case driver.HyperV:
		v := reflect.ValueOf(host.Driver).Elem()
		var hypervVirtualSwitch string
		// We don't have direct access to hyperv.Driver so use reflection to retrieve the virtual switch name
		for i := 0; i < v.NumField(); i++ {
			if v.Type().Field(i).Name == "VSwitch" {
				hypervVirtualSwitch = v.Field(i).Interface().(string)
				break
			}
		}
		if hypervVirtualSwitch == "" {
			return nil, errors.New("No virtual switch found")
		}
		ip, err := getIPForInterface(fmt.Sprintf("vEthernet (%s)", hypervVirtualSwitch))
		if err != nil {
			return []byte{}, errors.Wrap(err, fmt.Sprintf("ip for interface (%s)", hypervVirtualSwitch))
		}
		return ip, nil
	case driver.VirtualBox:
		out, err := exec.Command(driver.VBoxManagePath(), "showvminfo", host.Name, "--machinereadable").Output()
		if err != nil {
			return []byte{}, errors.Wrap(err, "vboxmanage")
		}
		re := regexp.MustCompile(`hostonlyadapter2="(.*?)"`)
		iface := re.FindStringSubmatch(string(out))[1]
		ip, err := getIPForInterface(iface)
		if err != nil {
			return []byte{}, errors.Wrap(err, "Error getting VM/Host IP address")
		}
		return ip, nil
	case driver.HyperKit:
		return net.ParseIP("192.168.64.1"), nil
	case driver.VMware:
		vmIPString, err := host.Driver.GetIP()
		if err != nil {
			return []byte{}, errors.Wrap(err, "Error getting VM IP address")
		}
		vmIP := net.ParseIP(vmIPString).To4()
		if vmIP == nil {
			return []byte{}, errors.Wrap(err, "Error converting VM IP address to IPv4 address")
		}
		return net.IPv4(vmIP[0], vmIP[1], vmIP[2], byte(1)), nil
	case driver.None:
		return net.ParseIP("127.0.0.1"), nil
	default:
		return []byte{}, fmt.Errorf("HostIP not yet implemented for %q driver", host.DriverName)
	}
}

// DriverIP gets the ip address of the current minikube cluster
func DriverIP(api libmachine.API, machineName string) (net.IP, error) {
	host, err := machine.LoadHost(api, machineName)
	if err != nil {
		return nil, err
	}

	ipStr, err := host.Driver.GetIP()
	if err != nil {
		return nil, errors.Wrap(err, "getting IP")
	}
	if driver.IsKIC(host.DriverName) {
		ipStr = oci.DefaultBindIPV4
	}
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return nil, fmt.Errorf("parsing IP: %s", ipStr)
	}
	return ip, nil
}

// Based on code from http://stackoverflow.com/questions/23529663/how-to-get-all-addresses-and-masks-from-local-interfaces-in-go
func getIPForInterface(name string) (net.IP, error) {
	i, _ := net.InterfaceByName(name)
	addrs, _ := i.Addrs()
	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok {
			if ip := ipnet.IP.To4(); ip != nil {
				return ip, nil
			}
		}
	}
	return nil, errors.Errorf("Error finding IPV4 address for %s", name)
}
<commit_msg>fix virtualbox IP address retrieval<commit_after>/*
Copyright 2020 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cluster

import (
	"fmt"
	"net"
	"os/exec"
	"reflect"
	"regexp"

	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/libmachine/host"
	"github.com/pkg/errors"
	"k8s.io/minikube/pkg/drivers/kic/oci"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/machine"
)

// HostIP gets the ip address to be used for mapping host -> VM and VM -> host
func HostIP(host *host.Host) (net.IP, error) {
	switch host.DriverName {
	case driver.Docker:
		return oci.RoutableHostIPFromInside(oci.Docker, host.Name)
	case driver.Podman:
		return oci.RoutableHostIPFromInside(oci.Podman, host.Name)
	case driver.KVM2:
		return net.ParseIP("192.168.39.1"), nil
	case driver.HyperV:
		v := reflect.ValueOf(host.Driver).Elem()
		var hypervVirtualSwitch string
		// We don't have direct access to hyperv.Driver so use reflection to retrieve the virtual switch name
		for i := 0; i < v.NumField(); i++ {
			if v.Type().Field(i).Name == "VSwitch" {
				hypervVirtualSwitch = v.Field(i).Interface().(string)
				break
			}
		}
		if hypervVirtualSwitch == "" {
			return nil, errors.New("No virtual switch found")
		}
		ip, err := getIPForInterface(fmt.Sprintf("vEthernet (%s)", hypervVirtualSwitch))
		if err != nil {
			return []byte{}, errors.Wrap(err, fmt.Sprintf("ip for interface (%s)", hypervVirtualSwitch))
		}
		return ip, nil
	case driver.VirtualBox:
		out, err := exec.Command(driver.VBoxManagePath(), "showvminfo", host.Name, "--machinereadable").Output()
		if err != nil {
			return []byte{}, errors.Wrap(err, "vboxmanage")
		}
		re := regexp.MustCompile(`hostonlyadapter2="(.*?)"`)
		iface := re.FindStringSubmatch(string(out))[1]
		ipList, err := exec.Command(driver.VBoxManagePath(), "list", "hostonlyifs").Output()
		if err != nil {
			return []byte{}, errors.Wrap(err, "Error getting VM/Host IP address")
		}
		re = regexp.MustCompile(`(?s)Name:\s*` + iface + `.+IPAddress:\s*(\S+)`)
		ip := re.FindStringSubmatch(string(ipList))[1]
		return net.ParseIP(ip), nil
	case driver.HyperKit:
		return net.ParseIP("192.168.64.1"), nil
	case driver.VMware:
		vmIPString, err := host.Driver.GetIP()
		if err != nil {
			return []byte{}, errors.Wrap(err, "Error getting VM IP address")
		}
		vmIP := net.ParseIP(vmIPString).To4()
		if vmIP == nil {
			return []byte{}, errors.Wrap(err, "Error converting VM IP address to IPv4 address")
		}
		return net.IPv4(vmIP[0], vmIP[1], vmIP[2], byte(1)), nil
	case driver.None:
		return net.ParseIP("127.0.0.1"), nil
	default:
		return []byte{}, fmt.Errorf("HostIP not yet implemented for %q driver", host.DriverName)
	}
}

// DriverIP gets the ip address of the current minikube cluster
func DriverIP(api libmachine.API, machineName string) (net.IP, error) {
	host, err := machine.LoadHost(api, machineName)
	if err != nil {
		return nil, err
	}

	ipStr, err := host.Driver.GetIP()
	if err != nil {
		return nil, errors.Wrap(err, "getting IP")
	}
	if driver.IsKIC(host.DriverName) {
		ipStr = oci.DefaultBindIPV4
	}
	ip := net.ParseIP(ipStr)
	if ip == nil {
		return nil, fmt.Errorf("parsing IP: %s", ipStr)
	}
	return ip, nil
}

// Based on code from http://stackoverflow.com/questions/23529663/how-to-get-all-addresses-and-masks-from-local-interfaces-in-go
func getIPForInterface(name string) (net.IP, error) {
	i, _ := net.InterfaceByName(name)
	addrs, _ := i.Addrs()
	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok {
			if ip := ipnet.IP.To4(); ip != nil {
				return ip, nil
			}
		}
	}
	return nil, errors.Errorf("Error finding IPV4 address for %s", name)
}
<|endoftext|>


<commit_before>package oauth2

import (
	"errors"
	"sync"

	"github.com/Knetic/govaluate"
	"github.com/hellofresh/janus/pkg/jwt"
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/mitchellh/mapstructure"
)

// AccessRequestType is the type for OAuth param `grant_type`
type AccessRequestType string

// AuthorizeRequestType is the type for OAuth param `response_type`
type AuthorizeRequestType string

// Spec Holds an api definition and basic options
type Spec struct {
	*OAuth
	Manager Manager
}

// OAuth holds the configuration for oauth proxies
type OAuth struct {
	Name                   string                 `bson:"name" json:"name" valid:"required"`
	Endpoints              Endpoints              `bson:"oauth_endpoints" json:"oauth_endpoints" mapstructure:"oauth_endpoints"`
	ClientEndpoints        ClientEndpoints        `bson:"oauth_client_endpoints" json:"oauth_client_endpoints" mapstructure:"oauth_client_endpoints"`
	AllowedAccessTypes     []AccessRequestType    `bson:"allowed_access_types" json:"allowed_access_types" mapstructure:"allowed_access_types" `
	AllowedAuthorizeTypes  []AuthorizeRequestType `bson:"allowed_authorize_types" json:"allowed_authorize_types" mapstructure:"allowed_authorize_types"`
	AuthorizeLoginRedirect string                 `bson:"auth_login_redirect" json:"auth_login_redirect" mapstructure:"auth_login_redirect"`
	Secrets                map[string]string      `bson:"secrets" json:"secrets"`
	CorsMeta               corsMeta               `bson:"cors_meta" json:"cors_meta" mapstructure:"cors_meta"`
	RateLimit              rateLimitMeta          `bson:"rate_limit" json:"rate_limit"`
	TokenStrategy          TokenStrategy          `bson:"token_strategy" json:"token_strategy" mapstructure:"token_strategy"`
	AccessRules            []*AccessRule          `bson:"access_rules" json:"access_rules"`
}

// Endpoints defines the oauth endpoints that wil be proxied
type Endpoints struct {
	Authorize  *proxy.Definition `bson:"authorize" json:"authorize"`
	Token      *proxy.Definition `bson:"token" json:"token"`
	Introspect *proxy.Definition `bson:"introspect" json:"introspect"`
	Revoke     *proxy.Definition `bson:"revoke" json:"revoke"`
}

// ClientEndpoints defines the oauth client endpoints that wil be proxied
type ClientEndpoints struct {
	Create *proxy.Definition `bson:"create" json:"create"`
	Remove *proxy.Definition `bson:"remove" json:"remove"`
}

type rateLimitMeta struct {
	Limit   string `bson:"limit" json:"limit"`
	Enabled bool   `bson:"enabled" json:"enabled"`
}

type corsMeta struct {
	Domains        []string `mapstructure:"domains" bson:"domains" json:"domains"`
	Methods        []string `mapstructure:"methods" bson:"methods" json:"methods"`
	RequestHeaders []string `mapstructure:"request_headers" bson:"request_headers" json:"request_headers"`
	ExposedHeaders []string `mapstructure:"exposed_headers" bson:"exposed_headers" json:"exposed_headers"`
	Enabled        bool     `bson:"enabled" json:"enabled"`
}

// TokenStrategy defines the token strategy fields
type TokenStrategy struct {
	Name     string      `bson:"name" json:"name"`
	Settings interface{} `bson:"settings" json:"settings"`
}

// GetJWTSigningMethods parses and returns chain of JWT signing methods for token signature validation.
// Supports fallback to legacy format with {"secret": "key"} as single signing method with HS256 alg.
func (t TokenStrategy) GetJWTSigningMethods() ([]jwt.SigningMethod, error) {
	var methods []jwt.SigningMethod
	err := mapstructure.Decode(t.Settings, &methods)
	if err != nil {
		var legacy struct {
			Secret string `json:"secret"`
		}
		err = mapstructure.Decode(t.Settings, &legacy)
		if nil != err {
			return methods, err
		}
		if legacy.Secret == "" {
			return nil, ErrJWTSecretMissing
		}

		return []jwt.SigningMethod{{Alg: "HS256", Key: legacy.Secret}}, nil
	}
	return methods, err
}

// AccessRule represents a rule that will be applied to a JWT that could be revoked
type AccessRule struct {
	mu        sync.Mutex
	Predicate string `bson:"predicate" json:"predicate"`
	Action    string `bson:"action" json:"action"`
	parsed    bool
}

// IsAllowed checks if the rule is allowed to
func (r *AccessRule) IsAllowed(claims map[string]interface{}) (bool, error) {
	var err error

	if !r.parsed {
		matched, err := r.parse(claims)
		if err != nil {
			return false, err
		}

		if !matched {
			return true, nil
		}
	}

	return r.Action == "allow", err
}

func (r *AccessRule) parse(claims map[string]interface{}) (bool, error) {
	expression, err := govaluate.NewEvaluableExpression(r.Predicate)
	if err != nil {
		return false, errors.New("Could not create an expression with this predicate")
	}

	result, err := expression.Evaluate(claims)
	if err != nil {
		return false, errors.New("Cannot evaluate the expression")
	}

	r.mu.Lock()
	r.parsed = true
	r.mu.Unlock()

	return result.(bool), nil
}
<commit_msg>Added instrospection settings<commit_after>package oauth2

import (
	"sync"

	"github.com/Knetic/govaluate"
	"github.com/hellofresh/janus/pkg/jwt"
	"github.com/hellofresh/janus/pkg/proxy"
	"github.com/mitchellh/mapstructure"
	"github.com/pkg/errors"
)

// AccessRequestType is the type for OAuth param `grant_type`
type AccessRequestType string

// AuthorizeRequestType is the type for OAuth param `response_type`
type AuthorizeRequestType string

// Spec Holds an api definition and basic options
type Spec struct {
	*OAuth
	Manager Manager
}

// OAuth holds the configuration for oauth proxies
type OAuth struct {
	Name                   string                 `bson:"name" json:"name" valid:"required"`
	Endpoints              Endpoints              `bson:"oauth_endpoints" json:"oauth_endpoints" mapstructure:"oauth_endpoints"`
	ClientEndpoints        ClientEndpoints        `bson:"oauth_client_endpoints" json:"oauth_client_endpoints" mapstructure:"oauth_client_endpoints"`
	AllowedAccessTypes     []AccessRequestType    `bson:"allowed_access_types" json:"allowed_access_types" mapstructure:"allowed_access_types" `
	AllowedAuthorizeTypes  []AuthorizeRequestType `bson:"allowed_authorize_types" json:"allowed_authorize_types" mapstructure:"allowed_authorize_types"`
	AuthorizeLoginRedirect string                 `bson:"auth_login_redirect" json:"auth_login_redirect" mapstructure:"auth_login_redirect"`
	Secrets                map[string]string      `bson:"secrets" json:"secrets"`
	CorsMeta               corsMeta               `bson:"cors_meta" json:"cors_meta" mapstructure:"cors_meta"`
	RateLimit              rateLimitMeta          `bson:"rate_limit" json:"rate_limit"`
	TokenStrategy          TokenStrategy          `bson:"token_strategy" json:"token_strategy" mapstructure:"token_strategy"`
	AccessRules            []*AccessRule          `bson:"access_rules" json:"access_rules"`
}

// Endpoints defines the oauth endpoints that wil be proxied
type Endpoints struct {
	Authorize  *proxy.Definition `bson:"authorize" json:"authorize"`
	Token      *proxy.Definition `bson:"token" json:"token"`
	Introspect *proxy.Definition `bson:"introspect" json:"introspect"`
	Revoke     *proxy.Definition `bson:"revoke" json:"revoke"`
}

// ClientEndpoints defines the oauth client endpoints that wil be proxied
type ClientEndpoints struct {
	Create *proxy.Definition `bson:"create" json:"create"`
	Remove *proxy.Definition `bson:"remove" json:"remove"`
}

type rateLimitMeta struct {
	Limit   string `bson:"limit" json:"limit"`
	Enabled bool   `bson:"enabled" json:"enabled"`
}

type corsMeta struct {
	Domains        []string `mapstructure:"domains" bson:"domains" json:"domains"`
	Methods        []string `mapstructure:"methods" bson:"methods" json:"methods"`
	RequestHeaders []string `mapstructure:"request_headers" bson:"request_headers" json:"request_headers"`
	ExposedHeaders []string `mapstructure:"exposed_headers" bson:"exposed_headers" json:"exposed_headers"`
	Enabled        bool     `bson:"enabled" json:"enabled"`
}

// IntrospectionSettings represents the settings for introspection
type IntrospectionSettings struct {
	UseCustomHeader bool   `bson:"use_custom_header" json:"use_custom_header"`
	HeaderName      string `bson:"header_name" json:"header_name"`
	UseAuthHeader   bool   `bson:"use_auth_header" json:"use_auth_header"`
	AuthHeaderType  string `bson:"auth_header_type" json:"auth_header_type"`
	UseBody         bool   `bson:"use_body" json:"use_body"`
	ParamName       string `bson:"param_name" json:"param_name"`
}

// TokenStrategy defines the token strategy fields
type TokenStrategy struct {
	Name     string      `bson:"name" json:"name"`
	Settings interface{} `bson:"settings" json:"settings"`
}

// GetIntrospectionSettings returns the settings for introspection
func (t TokenStrategy) GetIntrospectionSettings() (*IntrospectionSettings, error) {
	var settings *IntrospectionSettings
	err := mapstructure.Decode(t.Settings, &settings)
	if err != nil {
		return nil, errors.Wrap(err, "could not decode introspection settings")
	}
	return settings, nil
}

// GetJWTSigningMethods parses and returns chain of JWT signing methods for token signature validation.
// Supports fallback to legacy format with {"secret": "key"} as single signing method with HS256 alg.
func (t TokenStrategy) GetJWTSigningMethods() ([]jwt.SigningMethod, error) {
	var methods []jwt.SigningMethod
	err := mapstructure.Decode(t.Settings, &methods)
	if err != nil {
		var legacy struct {
			Secret string `json:"secret"`
		}
		err = mapstructure.Decode(t.Settings, &legacy)
		if nil != err {
			return methods, err
		}
		if legacy.Secret == "" {
			return nil, ErrJWTSecretMissing
		}

		return []jwt.SigningMethod{{Alg: "HS256", Key: legacy.Secret}}, nil
	}
	return methods, err
}

// AccessRule represents a rule that will be applied to a JWT that could be revoked
type AccessRule struct {
	mu        sync.Mutex
	Predicate string `bson:"predicate" json:"predicate"`
	Action    string `bson:"action" json:"action"`
	parsed    bool
}

// IsAllowed checks if the rule is allowed to
func (r *AccessRule) IsAllowed(claims map[string]interface{}) (bool, error) {
	var err error

	if !r.parsed {
		matched, err := r.parse(claims)
		if err != nil {
			return false, err
		}

		if !matched {
			return true, nil
		}
	}

	return r.Action == "allow", err
}

func (r *AccessRule) parse(claims map[string]interface{}) (bool, error) {
	expression, err := govaluate.NewEvaluableExpression(r.Predicate)
	if err != nil {
		return false, errors.New("Could not create an expression with this predicate")
	}

	result, err := expression.Evaluate(claims)
	if err != nil {
		return false, errors.New("Cannot evaluate the expression")
	}

	r.mu.Lock()
	r.parsed = true
	r.mu.Unlock()

	return result.(bool), nil
}
<|endoftext|>


<commit_before>/*
Copyright The Helm 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 repo

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"helm.sh/helm/pkg/chart"
	"helm.sh/helm/pkg/cli"
	"helm.sh/helm/pkg/getter"
)

const (
	testRepository = "testdata/repository"
	testURL        = "http://example-charts.com"
)

func TestLoadChartRepository(t *testing.T) {
	r, err := NewChartRepository(&Entry{
		Name: testRepository,
		URL:  testURL,
	}, getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
	}

	if err := r.Load(); err != nil {
		t.Errorf("Problem loading chart repository from %s: %v", testRepository, err)
	}

	paths := []string{
		filepath.Join(testRepository, "frobnitz-1.2.3.tgz"),
		filepath.Join(testRepository, "sprocket-1.1.0.tgz"),
		filepath.Join(testRepository, "sprocket-1.2.0.tgz"),
		filepath.Join(testRepository, "universe/zarthal-1.0.0.tgz"),
	}

	if r.Config.Name != testRepository {
		t.Errorf("Expected %s as Name but got %s", testRepository, r.Config.Name)
	}

	if !reflect.DeepEqual(r.ChartPaths, paths) {
		t.Errorf("Expected %#v but got %#v\n", paths, r.ChartPaths)
	}

	if r.Config.URL != testURL {
		t.Errorf("Expected url for chart repository to be %s but got %s", testURL, r.Config.URL)
	}
}

func TestIndex(t *testing.T) {
	r, err := NewChartRepository(&Entry{
		Name: testRepository,
		URL:  testURL,
	}, getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
	}

	if err := r.Load(); err != nil {
		t.Errorf("Problem loading chart repository from %s: %v", testRepository, err)
	}

	err = r.Index()
	if err != nil {
		t.Errorf("Error performing index: %v\n", err)
	}

	tempIndexPath := filepath.Join(testRepository, indexPath)
	actual, err := LoadIndexFile(tempIndexPath)
	defer os.Remove(tempIndexPath) // clean up
	if err != nil {
		t.Errorf("Error loading index file %v", err)
	}
	verifyIndex(t, actual)

	// Re-index and test again.
	err = r.Index()
	if err != nil {
		t.Errorf("Error performing re-index: %s\n", err)
	}
	second, err := LoadIndexFile(tempIndexPath)
	if err != nil {
		t.Errorf("Error re-loading index file %v", err)
	}
	verifyIndex(t, second)
}

func verifyIndex(t *testing.T, actual *IndexFile) {
	var empty time.Time
	if actual.Generated == empty {
		t.Errorf("Generated should be greater than 0: %s", actual.Generated)
	}

	if actual.APIVersion != APIVersionV1 {
		t.Error("Expected v1 API")
	}

	entries := actual.Entries
	if numEntries := len(entries); numEntries != 3 {
		t.Errorf("Expected 3 charts to be listed in index file but got %v", numEntries)
	}

	expects := map[string]ChartVersions{
		"frobnitz": {
			{
				Metadata: &chart.Metadata{
					Name:    "frobnitz",
					Version: "1.2.3",
				},
			},
		},
		"sprocket": {
			{
				Metadata: &chart.Metadata{
					Name:    "sprocket",
					Version: "1.2.0",
				},
			},
			{
				Metadata: &chart.Metadata{
					Name:    "sprocket",
					Version: "1.1.0",
				},
			},
		},
		"zarthal": {
			{
				Metadata: &chart.Metadata{
					Name:    "zarthal",
					Version: "1.0.0",
				},
			},
		},
	}

	for name, versions := range expects {
		got, ok := entries[name]
		if !ok {
			t.Errorf("Could not find %q entry", name)
			continue
		}
		if len(versions) != len(got) {
			t.Errorf("Expected %d versions, got %d", len(versions), len(got))
			continue
		}
		for i, e := range versions {
			g := got[i]
			if e.Name != g.Name {
				t.Errorf("Expected %q, got %q", e.Name, g.Name)
			}
			if e.Version != g.Version {
				t.Errorf("Expected %q, got %q", e.Version, g.Version)
			}
			if len(g.Keywords) != 3 {
				t.Error("Expected 3 keyrwords.")
			}
			if len(g.Maintainers) != 2 {
				t.Error("Expected 2 maintainers.")
			}
			if g.Created == empty {
				t.Error("Expected created to be non-empty")
			}
			if g.Description == "" {
				t.Error("Expected description to be non-empty")
			}
			if g.Home == "" {
				t.Error("Expected home to be non-empty")
			}
			if g.Digest == "" {
				t.Error("Expected digest to be non-empty")
			}
			if len(g.URLs) != 1 {
				t.Error("Expected exactly 1 URL")
			}
		}
	}
}

// startLocalServerForTests Start the local helm server
func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) {
	if handler == nil {
		fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml")
		if err != nil {
			return nil, err
		}
		handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write(fileBytes)
		})
	}

	return httptest.NewServer(handler), nil
}

func TestFindChartInRepoURL(t *testing.T) {
	srv, err := startLocalServerForTests(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer srv.Close()

	chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" {
		t.Errorf("%s is not the valid URL", chartURL)
	}

	chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz" {
		t.Errorf("%s is not the valid URL", chartURL)
	}
}

func TestErrorFindChartInRepoURL(t *testing.T) {
	_, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for bad chart URL, but did not get any errors")
	}
	if err != nil && !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) {
		t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err)
	}

	srv, err := startLocalServerForTests(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer srv.Close()

	_, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for chart not found, but did not get any errors")
	}
	if err != nil && err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` {
		t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
	}

	_, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for chart not found, but did not get any errors")
	}
	if err != nil && err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` {
		t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
	}

	_, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for no chart URLs available, but did not get any errors")
	}
	if err != nil && err.Error() != `chart "chartWithNoURL" has no downloadable URLs` {
		t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
	}
}

func TestResolveReferenceURL(t *testing.T) {
	chartURL, err := ResolveReferenceURL("http://localhost:8123/charts/", "nginx-0.2.0.tgz")
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "http://localhost:8123/charts/nginx-0.2.0.tgz" {
		t.Errorf("%s", chartURL)
	}

	chartURL, err = ResolveReferenceURL("http://localhost:8123", "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz")
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" {
		t.Errorf("%s", chartURL)
	}
}
<commit_msg>test: add test<commit_after>/*
Copyright The Helm 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 repo

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"helm.sh/helm/pkg/chart"
	"helm.sh/helm/pkg/cli"
	"helm.sh/helm/pkg/getter"

	"sigs.k8s.io/yaml"
)

const (
	testRepository = "testdata/repository"
	testURL        = "http://example-charts.com"
)

func TestLoadChartRepository(t *testing.T) {
	r, err := NewChartRepository(&Entry{
		Name: testRepository,
		URL:  testURL,
	}, getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
	}

	if err := r.Load(); err != nil {
		t.Errorf("Problem loading chart repository from %s: %v", testRepository, err)
	}

	paths := []string{
		filepath.Join(testRepository, "frobnitz-1.2.3.tgz"),
		filepath.Join(testRepository, "sprocket-1.1.0.tgz"),
		filepath.Join(testRepository, "sprocket-1.2.0.tgz"),
		filepath.Join(testRepository, "universe/zarthal-1.0.0.tgz"),
	}

	if r.Config.Name != testRepository {
		t.Errorf("Expected %s as Name but got %s", testRepository, r.Config.Name)
	}

	if !reflect.DeepEqual(r.ChartPaths, paths) {
		t.Errorf("Expected %#v but got %#v\n", paths, r.ChartPaths)
	}

	if r.Config.URL != testURL {
		t.Errorf("Expected url for chart repository to be %s but got %s", testURL, r.Config.URL)
	}
}

func TestIndex(t *testing.T) {
	r, err := NewChartRepository(&Entry{
		Name: testRepository,
		URL:  testURL,
	}, getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
	}

	if err := r.Load(); err != nil {
		t.Errorf("Problem loading chart repository from %s: %v", testRepository, err)
	}

	err = r.Index()
	if err != nil {
		t.Errorf("Error performing index: %v\n", err)
	}

	tempIndexPath := filepath.Join(testRepository, indexPath)
	actual, err := LoadIndexFile(tempIndexPath)
	defer os.Remove(tempIndexPath) // clean up
	if err != nil {
		t.Errorf("Error loading index file %v", err)
	}
	verifyIndex(t, actual)

	// Re-index and test again.
	err = r.Index()
	if err != nil {
		t.Errorf("Error performing re-index: %s\n", err)
	}
	second, err := LoadIndexFile(tempIndexPath)
	if err != nil {
		t.Errorf("Error re-loading index file %v", err)
	}
	verifyIndex(t, second)
}

type CustomGetter struct {
	repoUrls []string
}

func (g *CustomGetter) Get(href string) (*bytes.Buffer, error) {
	index := &IndexFile{
		APIVersion: "v1",
		Generated:  time.Now(),
	}
	indexBytes, err := yaml.Marshal(index)
	if err != nil {
		return nil, err
	}
	g.repoUrls = append(g.repoUrls, href)
	return bytes.NewBuffer(indexBytes), nil
}

func TestIndexCustomSchemeDownload(t *testing.T) {
	repoName := "gcs-repo"
	repoURL := "gs://some-gcs-bucket"
	myCustomGetter := &CustomGetter{}
	customGetterConstructor := func(options ...getter.Option) (getter.Getter, error) {
		return myCustomGetter, nil
	}
	providers := getter.Providers{
		{
			Schemes: []string{"gs"},
			New:     customGetterConstructor,
		},
	}
	repo, err := NewChartRepository(&Entry{
		Name: repoName,
		URL:  repoURL,
	}, providers)
	if err != nil {
		t.Fatalf("Problem loading chart repository from %s: %v", repoURL, err)
	}

	tempIndexFile, err := ioutil.TempFile("", "test-repo")
	if err != nil {
		t.Fatalf("Failed to create temp index file: %v", err)
	}
	defer os.Remove(tempIndexFile.Name())

	if err := repo.DownloadIndexFile(tempIndexFile.Name()); err != nil {
		t.Fatalf("Failed to download index file: %v", err)
	}

	if len(myCustomGetter.repoUrls) != 1 {
		t.Fatalf("Custom Getter.Get should be called once")
	}

	expectedRepoIndexURL := repoURL + "/index.yaml"
	if myCustomGetter.repoUrls[0] != expectedRepoIndexURL {
		t.Fatalf("Custom Getter.Get should be called with %s", expectedRepoIndexURL)
	}
}

func verifyIndex(t *testing.T, actual *IndexFile) {
	var empty time.Time
	if actual.Generated == empty {
		t.Errorf("Generated should be greater than 0: %s", actual.Generated)
	}

	if actual.APIVersion != APIVersionV1 {
		t.Error("Expected v1 API")
	}

	entries := actual.Entries
	if numEntries := len(entries); numEntries != 3 {
		t.Errorf("Expected 3 charts to be listed in index file but got %v", numEntries)
	}

	expects := map[string]ChartVersions{
		"frobnitz": {
			{
				Metadata: &chart.Metadata{
					Name:    "frobnitz",
					Version: "1.2.3",
				},
			},
		},
		"sprocket": {
			{
				Metadata: &chart.Metadata{
					Name:    "sprocket",
					Version: "1.2.0",
				},
			},
			{
				Metadata: &chart.Metadata{
					Name:    "sprocket",
					Version: "1.1.0",
				},
			},
		},
		"zarthal": {
			{
				Metadata: &chart.Metadata{
					Name:    "zarthal",
					Version: "1.0.0",
				},
			},
		},
	}

	for name, versions := range expects {
		got, ok := entries[name]
		if !ok {
			t.Errorf("Could not find %q entry", name)
			continue
		}
		if len(versions) != len(got) {
			t.Errorf("Expected %d versions, got %d", len(versions), len(got))
			continue
		}
		for i, e := range versions {
			g := got[i]
			if e.Name != g.Name {
				t.Errorf("Expected %q, got %q", e.Name, g.Name)
			}
			if e.Version != g.Version {
				t.Errorf("Expected %q, got %q", e.Version, g.Version)
			}
			if len(g.Keywords) != 3 {
				t.Error("Expected 3 keyrwords.")
			}
			if len(g.Maintainers) != 2 {
				t.Error("Expected 2 maintainers.")
			}
			if g.Created == empty {
				t.Error("Expected created to be non-empty")
			}
			if g.Description == "" {
				t.Error("Expected description to be non-empty")
			}
			if g.Home == "" {
				t.Error("Expected home to be non-empty")
			}
			if g.Digest == "" {
				t.Error("Expected digest to be non-empty")
			}
			if len(g.URLs) != 1 {
				t.Error("Expected exactly 1 URL")
			}
		}
	}
}

// startLocalServerForTests Start the local helm server
func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) {
	if handler == nil {
		fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml")
		if err != nil {
			return nil, err
		}
		handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write(fileBytes)
		})
	}

	return httptest.NewServer(handler), nil
}

func TestFindChartInRepoURL(t *testing.T) {
	srv, err := startLocalServerForTests(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer srv.Close()

	chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" {
		t.Errorf("%s is not the valid URL", chartURL)
	}

	chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(cli.EnvSettings{}))
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz" {
		t.Errorf("%s is not the valid URL", chartURL)
	}
}

func TestErrorFindChartInRepoURL(t *testing.T) {
	_, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for bad chart URL, but did not get any errors")
	}
	if err != nil && !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) {
		t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err)
	}

	srv, err := startLocalServerForTests(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer srv.Close()

	_, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for chart not found, but did not get any errors")
	}
	if err != nil && err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` {
		t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
	}

	_, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for chart not found, but did not get any errors")
	}
	if err != nil && err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` {
		t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
	}

	_, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(cli.EnvSettings{}))
	if err == nil {
		t.Errorf("Expected error for no chart URLs available, but did not get any errors")
	}
	if err != nil && err.Error() != `chart "chartWithNoURL" has no downloadable URLs` {
		t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
	}
}

func TestResolveReferenceURL(t *testing.T) {
	chartURL, err := ResolveReferenceURL("http://localhost:8123/charts/", "nginx-0.2.0.tgz")
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "http://localhost:8123/charts/nginx-0.2.0.tgz" {
		t.Errorf("%s", chartURL)
	}

	chartURL, err = ResolveReferenceURL("http://localhost:8123", "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz")
	if err != nil {
		t.Errorf("%s", err)
	}
	if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" {
		t.Errorf("%s", chartURL)
	}
}
<|endoftext|>


<commit_before>package scheduler

import (
	"context"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/realtime"
)

type (
	// Trigger interface is used to represent a trigger.
	Trigger interface {
		permissions.Validable
		Type() string
		Infos() *TriggerInfos
		// Schedule should return a channel on which the trigger can send job
		// requests when it decides to.
		Schedule() <-chan *jobs.JobRequest
		// Unschedule should be used to clean the trigger states and should close
		// the returns jobs channel.
		Unschedule()
	}

	// Scheduler interface is used to represent a scheduler that is responsible
	// to listen respond to triggers jobs requests and send them to the broker.
	Scheduler interface {
		Start(broker jobs.Broker) error
		Shutdown(ctx context.Context) error
		Add(trigger Trigger) error
		Get(domain, id string) (Trigger, error)
		Delete(domain, id string) error
		GetAll(domain string) ([]Trigger, error)
		RebuildRedis(domain string) error
	}

	// TriggerInfos is a struct containing all the options of a trigger.
	TriggerInfos struct {
		TID          string           `json:"_id,omitempty"`
		TRev         string           `json:"_rev,omitempty"`
		Domain       string           `json:"domain"`
		Type         string           `json:"type"`
		WorkerType   string           `json:"worker"`
		Arguments    string           `json:"arguments"`
		Debounce     string           `json:"debounce"`
		Options      *jobs.JobOptions `json:"options"`
		Message      jobs.Message     `json:"message"`
		CurrentState *TriggerState    `json:"current_state,omitempty"`
	}

	// TriggerState represent the current state of the trigger
	TriggerState struct {
		TID                 string     `json:"trigger_id"`
		Status              jobs.State `json:"status"`
		LastSuccess         *time.Time `json:"last_success,omitempty"`
		LastSuccessfulJobID string     `json:"last_successful_job_id,omitempty"`
		LastExecution       *time.Time `json:"last_execution,omitempty"`
		LastExecutedJobID   string     `json:"last_executed_job_id,omitempty"`
		LastFailure         *time.Time `json:"last_failure,omitempty"`
		LastFailedJobID     string     `json:"last_failed_job_id,omitempty"`
		LastError           string     `json:"last_error,omitempty"`
		LastManualExecution *time.Time `json:"last_manual_execution,omitempty"`
		LastManualJobID     string     `json:"last_manual_job_id,omitempty"`
	}
)

// NewTrigger creates the trigger associates with the specified trigger
// options.
func NewTrigger(infos *TriggerInfos) (Trigger, error) {
	switch infos.Type {
	case "@at":
		return NewAtTrigger(infos)
	case "@in":
		return NewInTrigger(infos)
	case "@cron":
		return NewCronTrigger(infos)
	case "@every":
		return NewEveryTrigger(infos)
	case "@event":
		return NewEventTrigger(infos)
	default:
		return nil, ErrUnknownTrigger
	}
}

// ID implements the couchdb.Doc interface
func (t *TriggerInfos) ID() string { return t.TID }

// Rev implements the couchdb.Doc interface
func (t *TriggerInfos) Rev() string { return t.TRev }

// DocType implements the couchdb.Doc interface
func (t *TriggerInfos) DocType() string { return consts.Triggers }

// Clone implements the couchdb.Doc interface
func (t *TriggerInfos) Clone() couchdb.Doc {
	cloned := *t
	if t.Options != nil {
		tmp := *t.Options
		cloned.Options = &tmp
	}
	if t.Message != nil {
		tmp := t.Message
		t.Message = make([]byte, len(tmp))
		copy(t.Message[:], tmp)
	}
	return &cloned
}

// JobRequest returns a job request associated with the scheduler informations.
func (t *TriggerInfos) JobRequest() *jobs.JobRequest {
	return &jobs.JobRequest{
		Domain:     t.Domain,
		WorkerType: t.WorkerType,
		TriggerID:  t.ID(),
		Message:    t.Message,
		Options:    t.Options,
	}
}

// JobRequestWithEvent returns a job request associated with the scheduler
// informations associated to the specified realtime event.
func (t *TriggerInfos) JobRequestWithEvent(event *realtime.Event) (*jobs.JobRequest, error) {
	evt, err := jobs.NewEvent(event)
	if err != nil {
		return nil, err
	}
	req := t.JobRequest()
	req.Event = evt
	return req, nil
}

// SetID implements the couchdb.Doc interface
func (t *TriggerInfos) SetID(id string) { t.TID = id }

// SetRev implements the couchdb.Doc interface
func (t *TriggerInfos) SetRev(rev string) { t.TRev = rev }

// Valid implements the permissions.Validable interface
func (t *TriggerInfos) Valid(key, value string) bool {
	switch key {
	case "worker":
		return t.WorkerType == value
	}
	return false
}

// GetJobs returns the jobs launched by the given trigger.
func GetJobs(t Trigger, limit int) ([]*jobs.Job, error) {
	triggerInfos := t.Infos()
	db := couchdb.SimpleDatabasePrefix(triggerInfos.Domain)
	if limit <= 0 || limit > 50 {
		limit = 50
	}
	var jobs []*jobs.Job
	req := &couchdb.FindRequest{
		UseIndex: "by-trigger-id",
		Selector: mango.Equal("trigger_id", triggerInfos.ID()),
		Sort: mango.SortBy{
			{Field: "trigger_id", Direction: mango.Desc},
			{Field: "queued_at", Direction: mango.Desc},
		},
		Limit: limit,
	}
	err := couchdb.FindDocs(db, consts.Jobs, req, &jobs)
	if err != nil {
		return nil, err
	}
	return jobs, nil
}

// GetTriggerState returns the state of the trigger, calculated from the last
// launched jobs.
func GetTriggerState(t Trigger) (*TriggerState, error) {
	js, err := GetJobs(t, 0)
	if err != nil {
		return nil, err
	}

	var state TriggerState

	state.Status = jobs.Done
	state.TID = t.ID()

	for _, j := range js {
		startedAt := &j.StartedAt
		switch j.State {
		case jobs.Errored:
			state.LastFailure = startedAt
			state.LastFailedJobID = j.ID()
			state.LastError = j.Error
		case jobs.Done:
			state.LastSuccess = startedAt
			state.LastSuccessfulJobID = j.ID()
		}
		if j.Manual && (j.State == jobs.Done || j.State == jobs.Errored) {
			state.LastManualExecution = startedAt
			state.LastManualJobID = j.ID()
		}
		state.LastExecution = startedAt
		state.LastExecutedJobID = j.ID()
		state.Status = j.State
	}

	return &state, nil
}

var _ couchdb.Doc = &TriggerInfos{}
<commit_msg>Fix order of jobs to gather their result<commit_after>package scheduler

import (
	"context"
	"time"

	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/realtime"
)

type (
	// Trigger interface is used to represent a trigger.
	Trigger interface {
		permissions.Validable
		Type() string
		Infos() *TriggerInfos
		// Schedule should return a channel on which the trigger can send job
		// requests when it decides to.
		Schedule() <-chan *jobs.JobRequest
		// Unschedule should be used to clean the trigger states and should close
		// the returns jobs channel.
		Unschedule()
	}

	// Scheduler interface is used to represent a scheduler that is responsible
	// to listen respond to triggers jobs requests and send them to the broker.
	Scheduler interface {
		Start(broker jobs.Broker) error
		Shutdown(ctx context.Context) error
		Add(trigger Trigger) error
		Get(domain, id string) (Trigger, error)
		Delete(domain, id string) error
		GetAll(domain string) ([]Trigger, error)
		RebuildRedis(domain string) error
	}

	// TriggerInfos is a struct containing all the options of a trigger.
	TriggerInfos struct {
		TID          string           `json:"_id,omitempty"`
		TRev         string           `json:"_rev,omitempty"`
		Domain       string           `json:"domain"`
		Type         string           `json:"type"`
		WorkerType   string           `json:"worker"`
		Arguments    string           `json:"arguments"`
		Debounce     string           `json:"debounce"`
		Options      *jobs.JobOptions `json:"options"`
		Message      jobs.Message     `json:"message"`
		CurrentState *TriggerState    `json:"current_state,omitempty"`
	}

	// TriggerState represent the current state of the trigger
	TriggerState struct {
		TID                 string     `json:"trigger_id"`
		Status              jobs.State `json:"status"`
		LastSuccess         *time.Time `json:"last_success,omitempty"`
		LastSuccessfulJobID string     `json:"last_successful_job_id,omitempty"`
		LastExecution       *time.Time `json:"last_execution,omitempty"`
		LastExecutedJobID   string     `json:"last_executed_job_id,omitempty"`
		LastFailure         *time.Time `json:"last_failure,omitempty"`
		LastFailedJobID     string     `json:"last_failed_job_id,omitempty"`
		LastError           string     `json:"last_error,omitempty"`
		LastManualExecution *time.Time `json:"last_manual_execution,omitempty"`
		LastManualJobID     string     `json:"last_manual_job_id,omitempty"`
	}
)

// NewTrigger creates the trigger associates with the specified trigger
// options.
func NewTrigger(infos *TriggerInfos) (Trigger, error) {
	switch infos.Type {
	case "@at":
		return NewAtTrigger(infos)
	case "@in":
		return NewInTrigger(infos)
	case "@cron":
		return NewCronTrigger(infos)
	case "@every":
		return NewEveryTrigger(infos)
	case "@event":
		return NewEventTrigger(infos)
	default:
		return nil, ErrUnknownTrigger
	}
}

// ID implements the couchdb.Doc interface
func (t *TriggerInfos) ID() string { return t.TID }

// Rev implements the couchdb.Doc interface
func (t *TriggerInfos) Rev() string { return t.TRev }

// DocType implements the couchdb.Doc interface
func (t *TriggerInfos) DocType() string { return consts.Triggers }

// Clone implements the couchdb.Doc interface
func (t *TriggerInfos) Clone() couchdb.Doc {
	cloned := *t
	if t.Options != nil {
		tmp := *t.Options
		cloned.Options = &tmp
	}
	if t.Message != nil {
		tmp := t.Message
		t.Message = make([]byte, len(tmp))
		copy(t.Message[:], tmp)
	}
	return &cloned
}

// JobRequest returns a job request associated with the scheduler informations.
func (t *TriggerInfos) JobRequest() *jobs.JobRequest {
	return &jobs.JobRequest{
		Domain:     t.Domain,
		WorkerType: t.WorkerType,
		TriggerID:  t.ID(),
		Message:    t.Message,
		Options:    t.Options,
	}
}

// JobRequestWithEvent returns a job request associated with the scheduler
// informations associated to the specified realtime event.
func (t *TriggerInfos) JobRequestWithEvent(event *realtime.Event) (*jobs.JobRequest, error) {
	evt, err := jobs.NewEvent(event)
	if err != nil {
		return nil, err
	}
	req := t.JobRequest()
	req.Event = evt
	return req, nil
}

// SetID implements the couchdb.Doc interface
func (t *TriggerInfos) SetID(id string) { t.TID = id }

// SetRev implements the couchdb.Doc interface
func (t *TriggerInfos) SetRev(rev string) { t.TRev = rev }

// Valid implements the permissions.Validable interface
func (t *TriggerInfos) Valid(key, value string) bool {
	switch key {
	case "worker":
		return t.WorkerType == value
	}
	return false
}

// GetJobs returns the jobs launched by the given trigger.
func GetJobs(t Trigger, limit int) ([]*jobs.Job, error) {
	triggerInfos := t.Infos()
	db := couchdb.SimpleDatabasePrefix(triggerInfos.Domain)
	if limit <= 0 || limit > 50 {
		limit = 50
	}
	var jobs []*jobs.Job
	req := &couchdb.FindRequest{
		UseIndex: "by-trigger-id",
		Selector: mango.Equal("trigger_id", triggerInfos.ID()),
		Sort: mango.SortBy{
			{Field: "trigger_id", Direction: mango.Desc},
			{Field: "queued_at", Direction: mango.Desc},
		},
		Limit: limit,
	}
	err := couchdb.FindDocs(db, consts.Jobs, req, &jobs)
	if err != nil {
		return nil, err
	}
	return jobs, nil
}

// GetTriggerState returns the state of the trigger, calculated from the last
// launched jobs.
func GetTriggerState(t Trigger) (*TriggerState, error) {
	js, err := GetJobs(t, 0)
	if err != nil {
		return nil, err
	}

	var state TriggerState

	state.Status = jobs.Done
	state.TID = t.ID()

	// jobs are ordered from the oldest to most recent job
	for i := len(js) - 1; i >= 0; i-- {
		j := js[i]
		startedAt := &j.StartedAt
		switch j.State {
		case jobs.Errored:
			state.LastFailure = startedAt
			state.LastFailedJobID = j.ID()
			state.LastError = j.Error
		case jobs.Done:
			state.LastSuccess = startedAt
			state.LastSuccessfulJobID = j.ID()
		}
		if j.Manual && (j.State == jobs.Done || j.State == jobs.Errored) {
			state.LastManualExecution = startedAt
			state.LastManualJobID = j.ID()
		}
		state.LastExecution = startedAt
		state.LastExecutedJobID = j.ID()
		state.Status = j.State
	}

	return &state, nil
}

var _ couchdb.Doc = &TriggerInfos{}
<|endoftext|>


<commit_before>package serverapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/skatteetaten/aoc/pkg/jsonutil"
	"github.com/skatteetaten/aoc/pkg/openshift"
	"io/ioutil"
	"net/http"
	"strings"
)

const apiNotInstalledResponse = "Application is not available"

// Structs to represent return data from the API interface
type ApiReturnObjects struct {
	Sources          json.RawMessage            `json:"sources"`
	Errors           []string                   `json:"errors"`
	Valid            bool                       `json:"valid"`
	Config           json.RawMessage            `json:"config"`
	OpenshiftObjects map[string]json.RawMessage `json:"openshiftObjects"`
}

type ApiReturn struct {
	Sources          json.RawMessage `json:"sources"`
	Errors           []string        `json:"errors"`
	Valid            bool            `json:"valid"`
	Config           json.RawMessage `json:"config"`
	OpenshiftObjects json.RawMessage `json:"openshiftObjects"`
}

func GetApiAddress(clusterName string, localhost bool) (apiAddress string) {
	if localhost {
		apiAddress = "http://localhost:8080"
	} else {
		apiAddress = "http://boober-aos-bas-dev." + clusterName + ".paas.skead.no"
	}
	return
}

// Check for valid login, that is we have a configuration with at least one reachable cluster
func ValidateLogin(openshiftConfig *openshift.OpenshiftConfig) (output bool) {
	var openshiftCluster *openshift.OpenshiftCluster
	openshiftCluster, _ = openshiftConfig.GetApiCluster()
	if openshiftCluster != nil {
		if !openshiftCluster.HasValidToken() {
			return false
		}
	}
	return true
}

func GetApiSetupUrl(clusterName string, localhost bool) string {
	return GetApiAddress(clusterName, localhost) + "/setup"
}

func CallApi(combindedJson string, showConfig bool, showObjects bool, api bool, localhost bool, verbose bool,
	openshiftConfig *openshift.OpenshiftConfig, dryRun bool) (output string, err error) {
	//var openshiftConfig *openshift.OpenshiftConfig
	var apiCluster *openshift.OpenshiftCluster

	if localhost {
		var token string = ""
		apiCluster, err = openshiftConfig.GetApiCluster()
		if apiCluster != nil {
			token = apiCluster.Token
		}
		output, err = callApiInstance(combindedJson, showConfig, showObjects, verbose,
			GetApiSetupUrl("localhost", localhost), token, dryRun)
		if err != nil {
			return
		}
	} else {
		var errorString string
		var newline string
		for i := range openshiftConfig.Clusters {
			if openshiftConfig.Clusters[i].Reachable {
				if !api || openshiftConfig.Clusters[i].Name == openshiftConfig.APICluster {
					out, err := callApiInstance(combindedJson, showConfig, showObjects, verbose,
						GetApiSetupUrl(openshiftConfig.Clusters[i].Name, localhost),
						openshiftConfig.Clusters[i].Token, dryRun)
					if err == nil {
						output += fmt.Sprintf("%v\n", out)
					} else {
						if err.Error() != "" {
							errorString += newline + err.Error()
							newline = "\n"
						}
					}
				}
			}
		}
		if errorString != "" {
			return output, errors.New(errorString)
		}
	}
	return output, nil
}

func callApiInstance(combindedJson string, showConfig bool, showObjects bool, verbose bool, url string, token string, dryRun bool) (string, error) {
	var output string

	if verbose {
		fmt.Print("Sending config to Boober at " + url + "... ")
	}

	var jsonStr = []byte(combindedJson)

	req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")
	if err != nil {
		return "", errors.New(fmt.Sprintf("Internal error in NewRequest: %v", err))
	}

	req.Header.Set("Authentication", "Bearer: "+token)
	req.Header.Add("dryrun", fmt.Sprintf("%v", dryRun))
	client := &http.Client{}

	resp, err := client.Do(req)
	if err != nil {
		if verbose {
			fmt.Println("FAIL.  Error connecting to Boober service")
		}
		return "", errors.New(fmt.Sprintf("Error connecting to the Boober service on %v: %v", url, err))
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	bodyStr := string(body)

	//fmt.Println("HTTP Status code: " + strconv.Itoa(resp.StatusCode))
	if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != http.StatusBadRequest) {
		//fmt.Println("Not StatusOK and not StatusBadRequest")
		var errorstring string
		if !strings.Contains(bodyStr, apiNotInstalledResponse) {
			errorstring = fmt.Sprintf("Internal error on %v: %v", url, bodyStr)
		}
		if verbose {
			if strings.Contains(bodyStr, apiNotInstalledResponse) {
				fmt.Println("WARN.  Boober not available")
			} else {
				fmt.Println("FAIL.  Internal error")
			}
		}
		return "", errors.New(fmt.Sprintf(errorstring))
	}

	var booberReturn ApiReturn

	if resp.StatusCode == http.StatusBadRequest {
		// We have a validation situation, give error
		if verbose {
			fmt.Println("FAIL.  Error in configuration")
		}
		return "", errors.New(fmt.Sprintf(bodyStr))
	}

	err = json.Unmarshal(body, &booberReturn)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Error unmarshalling Boober return: %v\n", err.Error()))
	}
	for _, message := range booberReturn.Errors {
		fmt.Println("DEBUG: Error from Boober:  " + message)
	}
	if !(booberReturn.Valid) {
		fmt.Println("Error in configuration: ")
		for _, message := range booberReturn.Errors {
			fmt.Println("  " + message)
		}
	} else {
		if verbose {
			var apiReturnObjects ApiReturnObjects
			err = json.Unmarshal(body, &apiReturnObjects)
			if err != nil {
				return "", errors.New(fmt.Sprintf("Error unmarshalling Boober return: %v\n", err.Error()))
			}
			var countMap map[string]int = make(map[string]int)
			for key := range apiReturnObjects.OpenshiftObjects {
				countMap[key]++
			}

			var space string
			var count int
			var out string
			for key := range countMap {
				out += fmt.Sprintf("%v%v: %v", space, key, countMap[key])
				space = "  "
				count++
			}
			if count > 0 {
				output := fmt.Sprintf("OK.  Objects: %v  (%v)", count, out)
				fmt.Println(output)
			}
		}
	}

	if showConfig {
		output += jsonutil.PrettyPrintJson(string(booberReturn.Config))
	}

	if showObjects {
		output += jsonutil.PrettyPrintJson(string(booberReturn.OpenshiftObjects))
	}

	return output, nil
}
<commit_msg>Removed check on result valid to avoid incorrect error message<commit_after>package serverapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/skatteetaten/aoc/pkg/jsonutil"
	"github.com/skatteetaten/aoc/pkg/openshift"
	"io/ioutil"
	"net/http"
	"strings"
)

const apiNotInstalledResponse = "Application is not available"

// Structs to represent return data from the API interface
type ApiReturnObjects struct {
	Sources          json.RawMessage            `json:"sources"`
	Errors           []string                   `json:"errors"`
	Valid            bool                       `json:"valid"`
	Config           json.RawMessage            `json:"config"`
	OpenshiftObjects map[string]json.RawMessage `json:"openshiftObjects"`
}

type ApiReturn struct {
	Sources          json.RawMessage `json:"sources"`
	Errors           []string        `json:"errors"`
	Valid            bool            `json:"valid"`
	Config           json.RawMessage `json:"config"`
	OpenshiftObjects json.RawMessage `json:"openshiftObjects"`
}

func GetApiAddress(clusterName string, localhost bool) (apiAddress string) {
	if localhost {
		apiAddress = "http://localhost:8080"
	} else {
		apiAddress = "http://boober-aos-bas-dev." + clusterName + ".paas.skead.no"
	}
	return
}

// Check for valid login, that is we have a configuration with at least one reachable cluster
func ValidateLogin(openshiftConfig *openshift.OpenshiftConfig) (output bool) {
	var openshiftCluster *openshift.OpenshiftCluster
	openshiftCluster, _ = openshiftConfig.GetApiCluster()
	if openshiftCluster != nil {
		if !openshiftCluster.HasValidToken() {
			return false
		}
	}
	return true
}

func GetApiSetupUrl(clusterName string, localhost bool) string {
	return GetApiAddress(clusterName, localhost) + "/setup"
}

func CallApi(combindedJson string, showConfig bool, showObjects bool, api bool, localhost bool, verbose bool,
	openshiftConfig *openshift.OpenshiftConfig, dryRun bool) (output string, err error) {
	//var openshiftConfig *openshift.OpenshiftConfig
	var apiCluster *openshift.OpenshiftCluster

	if localhost {
		var token string = ""
		apiCluster, err = openshiftConfig.GetApiCluster()
		if apiCluster != nil {
			token = apiCluster.Token
		}
		output, err = callApiInstance(combindedJson, showConfig, showObjects, verbose,
			GetApiSetupUrl("localhost", localhost), token, dryRun)
		if err != nil {
			return
		}
	} else {
		var errorString string
		var newline string
		for i := range openshiftConfig.Clusters {
			if openshiftConfig.Clusters[i].Reachable {
				if !api || openshiftConfig.Clusters[i].Name == openshiftConfig.APICluster {
					out, err := callApiInstance(combindedJson, showConfig, showObjects, verbose,
						GetApiSetupUrl(openshiftConfig.Clusters[i].Name, localhost),
						openshiftConfig.Clusters[i].Token, dryRun)
					if err == nil {
						output += fmt.Sprintf("%v\n", out)
					} else {
						if err.Error() != "" {
							errorString += newline + err.Error()
							newline = "\n"
						}
					}
				}
			}
		}
		if errorString != "" {
			return output, errors.New(errorString)
		}
	}
	return output, nil
}

func callApiInstance(combindedJson string, showConfig bool, showObjects bool, verbose bool, url string, token string, dryRun bool) (string, error) {
	var output string

	if verbose {
		fmt.Print("Sending config to Boober at " + url + "... ")
	}

	var jsonStr = []byte(combindedJson)

	req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")
	if err != nil {
		return "", errors.New(fmt.Sprintf("Internal error in NewRequest: %v", err))
	}

	req.Header.Set("Authentication", "Bearer: "+token)
	req.Header.Add("dryrun", fmt.Sprintf("%v", dryRun))
	client := &http.Client{}

	resp, err := client.Do(req)
	if err != nil {
		if verbose {
			fmt.Println("FAIL.  Error connecting to Boober service")
		}
		return "", errors.New(fmt.Sprintf("Error connecting to the Boober service on %v: %v", url, err))
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	bodyStr := string(body)

	//fmt.Println("HTTP Status code: " + strconv.Itoa(resp.StatusCode))
	if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != http.StatusBadRequest) {
		//fmt.Println("Not StatusOK and not StatusBadRequest")
		var errorstring string
		if !strings.Contains(bodyStr, apiNotInstalledResponse) {
			errorstring = fmt.Sprintf("Internal error on %v: %v", url, bodyStr)
		}
		if verbose {
			if strings.Contains(bodyStr, apiNotInstalledResponse) {
				fmt.Println("WARN.  Boober not available")
			} else {
				fmt.Println("FAIL.  Internal error")
			}
		}
		return "", errors.New(fmt.Sprintf(errorstring))
	}

	var booberReturn ApiReturn

	if resp.StatusCode == http.StatusBadRequest {
		// We have a validation situation, give error
		if verbose {
			fmt.Println("FAIL.  Error in configuration")
		}
		return "", errors.New(fmt.Sprintf(bodyStr))
	}

	err = json.Unmarshal(body, &booberReturn)
	if err != nil {
		return "", errors.New(fmt.Sprintf("Error unmarshalling Boober return: %v\n", err.Error()))
	}
	for _, message := range booberReturn.Errors {
		fmt.Println("DEBUG: Error from Boober:  " + message)
	}
	if verbose {
		var apiReturnObjects ApiReturnObjects
		err = json.Unmarshal(body, &apiReturnObjects)
		if err != nil {
			return "", errors.New(fmt.Sprintf("Error unmarshalling Boober return: %v\n", err.Error()))
		}
		var countMap map[string]int = make(map[string]int)
		for key := range apiReturnObjects.OpenshiftObjects {
			countMap[key]++
		}

		var space string
		var count int
		var out string
		for key := range countMap {
			out += fmt.Sprintf("%v%v: %v", space, key, countMap[key])
			space = "  "
			count++
		}
		if count > 0 {
			output := fmt.Sprintf("OK.  Objects: %v  (%v)", count, out)
			fmt.Println(output)
		}
	}

	if showConfig {
		output += jsonutil.PrettyPrintJson(string(booberReturn.Config))
	}

	if showObjects {
		output += jsonutil.PrettyPrintJson(string(booberReturn.OpenshiftObjects))
	}

	return output, nil
}
<|endoftext|>


<commit_before>package system // import "github.com/docker/docker/pkg/system"

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

// Lgetxattr retrieves the value of the extended attribute identified by attr
// and associated with the given path in the file system.
// It will returns a nil slice and nil error if the xattr is not set.
func Lgetxattr(path string, attr string) ([]byte, error) {
	// Start with a 128 length byte array
	dest := make([]byte, 128)
	sz, errno := unix.Lgetxattr(path, attr, dest)

	switch {
	case errno == unix.ENODATA:
		return nil, nil
	case errno == unix.ERANGE:
		// 128 byte array might just not be good enough. A dummy buffer is used
		// to get the real size of the xattrs on disk
		sz, errno = unix.Lgetxattr(path, attr, []byte{})
		if errno != nil {
			return nil, errno
		}
		dest = make([]byte, sz)
		sz, errno = unix.Lgetxattr(path, attr, dest)
		if errno != nil {
			return nil, errno
		}
	case errno != nil:
		return nil, errno
	}
	return dest[:sz], nil
}

// Lsetxattr sets the value of the extended attribute identified by attr
// and associated with the given path in the file system.
func Lsetxattr(path string, attr string, data []byte, flags int) error {
	return unix.Lsetxattr(path, attr, data, flags)
}
<commit_msg>pkg/system.getxattr: handle changed size case<commit_after>package system // import "github.com/docker/docker/pkg/system"

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

// Lgetxattr retrieves the value of the extended attribute identified by attr
// and associated with the given path in the file system.
// It will returns a nil slice and nil error if the xattr is not set.
func Lgetxattr(path string, attr string) ([]byte, error) {
	// Start with a 128 length byte array
	dest := make([]byte, 128)
	sz, errno := unix.Lgetxattr(path, attr, dest)

	for errno == unix.ERANGE {
		// Buffer too small, use zero-sized buffer to get the actual size
		sz, errno = unix.Lgetxattr(path, attr, []byte{})
		if errno != nil {
			return nil, errno
		}
		dest = make([]byte, sz)
		sz, errno = unix.Lgetxattr(path, attr, dest)
	}

	switch {
	case errno == unix.ENODATA:
		return nil, nil
	case errno != nil:
		return nil, errno
	}

	return dest[:sz], nil
}

// Lsetxattr sets the value of the extended attribute identified by attr
// and associated with the given path in the file system.
func Lsetxattr(path string, attr string, data []byte, flags int) error {
	return unix.Lsetxattr(path, attr, data, flags)
}
<|endoftext|>