<commit_before>package provider

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/operator-framework/operator-registry/pkg/api"
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	operatorsv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators"
)

const (
	defaultConnectionTimeout = 5 * time.Second
)

type sourceKey struct {
	name      string
	namespace string
}

type registryClient struct {
	api.RegistryClient
	source *operatorsv1alpha1.CatalogSource
	conn   *grpc.ClientConn
}

func newRegistryClient(source *operatorsv1alpha1.CatalogSource, conn *grpc.ClientConn) registryClient {
	return registryClient{
		RegistryClient: api.NewRegistryClient(conn),
		source:         source,
		conn:           conn,
	}
}

// RegistryProvider aggregates several `CatalogSources` and establishes gRPC connections to their registry servers.
type RegistryProvider struct {
	*queueinformer.Operator

	mu              sync.RWMutex
	globalNamespace string
	clients         map[sourceKey]registryClient
}

var _ PackageManifestProvider = &RegistryProvider{}

func NewRegistryProvider(crClient versioned.Interface, operator *queueinformer.Operator, wakeupInterval time.Duration, watchedNamespaces []string, globalNamespace string) *RegistryProvider {
	p := &RegistryProvider{
		Operator: operator,

		globalNamespace: globalNamespace,
		clients:         make(map[sourceKey]registryClient),
	}

	sourceHandlers := &cache.ResourceEventHandlerFuncs{
		DeleteFunc: p.catalogSourceDeleted,
	}
	for _, namespace := range watchedNamespaces {
		factory := externalversions.NewSharedInformerFactoryWithOptions(crClient, wakeupInterval, externalversions.WithNamespace(namespace))
		sourceInformer := factory.Operators().V1alpha1().CatalogSources()

		// Register queue and QueueInformer
		logrus.WithField("namespace", namespace).Info("watching catalogsources")
		queueName := fmt.Sprintf("%s/catalogsources", namespace)
		sourceQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), queueName)
		sourceQueueInformer := queueinformer.NewInformer(sourceQueue, sourceInformer.Informer(), p.syncCatalogSource, sourceHandlers, queueName, metrics.NewMetricsNil(), logrus.New())
		p.RegisterQueueInformer(sourceQueueInformer)
	}

	return p
}

func (p *RegistryProvider) getClient(key sourceKey) (registryClient, bool) {
	p.mu.RLock()
	defer p.mu.RUnlock()

	client, ok := p.clients[key]
	return client, ok
}

func (p *RegistryProvider) setClient(client registryClient, key sourceKey) {
	p.mu.Lock()
	defer p.mu.Unlock()

	p.clients[key] = client
}

func (p *RegistryProvider) removeClient(key sourceKey) (registryClient, bool) {
	p.mu.Lock()
	defer p.mu.Unlock()

	client, ok := p.clients[key]
	if !ok {
		return registryClient{}, false
	}

	delete(p.clients, key)
	return client, true
}

func (p *RegistryProvider) syncCatalogSource(obj interface{}) (syncError error) {
	source, ok := obj.(*operatorsv1alpha1.CatalogSource)
	if !ok {
		logrus.Errorf("catalogsource type assertion failed: wrong type: %#v", obj)
	}

	logger := logrus.WithFields(logrus.Fields{
		"action":    "sync catalogsource",
		"name":      source.GetName(),
		"namespace": source.GetNamespace(),
	})

	if source.Status.RegistryServiceStatus == nil {
		logger.Debug("registry service is not ready for grpc connection")
		return
	}

	key := sourceKey{source.GetName(), source.GetNamespace()}
	client, ok := p.getClient(key)
	if ok && source.Status.RegistryServiceStatus.ServiceName != "" {
		logger.Info("update detected, attempting to reset grpc connection")
		client.conn.ResetConnectBackoff()

		ctx, cancel := context.WithTimeout(context.TODO(), defaultConnectionTimeout)
		defer cancel()

		changed := client.conn.WaitForStateChange(ctx, connectivity.TransientFailure)
		if !changed {
			logger.Debugf("grpc connection reset timeout")
			syncError = fmt.Errorf("grpc connection reset timeout")
			return
		}

		logger.Info("grpc connection reset")
		return
	} else if ok {
		// Address type grpc CatalogSource, drop the connection dial in to the new address
		client.conn.Close()
	}

	logger.Info("attempting to add a new grpc connection")
	conn, err := grpc.Dial(source.Address(), grpc.WithInsecure())
	if err != nil {
		logger.WithField("err", err.Error()).Errorf("could not connect to registry service")
		syncError = err
		return
	}

	p.setClient(newRegistryClient(source, conn), key)
	logger.Info("new grpc connection added")

	return
}

func (p *RegistryProvider) catalogSourceDeleted(obj interface{}) {
	catsrc, ok := obj.(metav1.Object)
	if !ok {
		if !ok {
			tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
			if !ok {
				utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
				return
			}

			catsrc, ok = tombstone.Obj.(metav1.Object)
			if !ok {
				utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a Namespace %#v", obj))
				return
			}
		}
	}

	logger := logrus.WithFields(logrus.Fields{
		"action":    "CatalogSource Deleted",
		"name":      catsrc.GetName(),
		"namespace": catsrc.GetNamespace(),
	})
	logger.Debugf("attempting to remove grpc connection")

	key := sourceKey{catsrc.GetName(), catsrc.GetNamespace()}
	client, removed := p.removeClient(key)
	if removed {
		err := client.conn.Close()
		if err != nil {
			logger.WithField("err", err.Error()).Error("error closing connection")
			utilruntime.HandleError(fmt.Errorf("error closing connection %s", err.Error()))
			return
		}
		logger.Debug("grpc connection removed")
		return
	}

	logger.Debugf("no gRPC connection to remove")

}

func (p *RegistryProvider) Get(namespace, name string) (*operators.PackageManifest, error) {
	logger := logrus.WithFields(logrus.Fields{
		"action":    "Get PackageManifest",
		"name":      name,
		"namespace": namespace,
	})

	pkgs, err := p.List(namespace)
	if err != nil {
		return nil, fmt.Errorf("could not list packages in namespace %s", namespace)
	}

	for _, pkg := range pkgs.Items {
		if pkg.GetName() == name {
			return &pkg, nil
		}
	}

	logger.Info("package not found")
	return nil, nil
}

func (p *RegistryProvider) List(namespace string) (*operators.PackageManifestList, error) {
	logger := logrus.WithFields(logrus.Fields{
		"action":    "List PackageManifests",
		"namespace": namespace,
	})

	p.mu.RLock()
	defer p.mu.RUnlock()

	pkgs := []operators.PackageManifest{}
	for _, client := range p.clients {
		if client.source.GetNamespace() == namespace || client.source.GetNamespace() == p.globalNamespace || namespace == metav1.NamespaceAll {
			logger.Debugf("found CatalogSource %s", client.source.GetName())

			stream, err := client.ListPackages(context.Background(), &api.ListPackageRequest{})
			if err != nil {
				logger.WithField("err", err.Error()).Warnf("error getting stream")
				continue
			}
			for {
				pkgName, err := stream.Recv()
				if err == io.EOF {
					break
				}

				if err != nil {
					logger.WithField("err", err.Error()).Warnf("error getting data")
					break
				}
				pkg, err := client.GetPackage(context.Background(), &api.GetPackageRequest{Name: pkgName.GetName()})
				if err != nil {
					logger.WithField("err", err.Error()).Warnf("error getting package")
					break
				}
				newPkg, err := toPackageManifest(pkg, client)
				if err != nil {
					logger.WithField("err", err.Error()).Warnf("error converting to packagemanifest")
					break
				}

				// Set request namespace to stop kube clients from complaining about global namespace mismatch.
				if namespace != metav1.NamespaceAll {
					newPkg.SetNamespace(namespace)
				}
				pkgs = append(pkgs, *newPkg)
			}
		}
	}

	return &operators.PackageManifestList{Items: pkgs}, nil
}

func toPackageManifest(pkg *api.Package, client registryClient) (*operators.PackageManifest, error) {
	pkgChannels := pkg.GetChannels()
	catsrc := client.source
	manifest := &operators.PackageManifest{
		ObjectMeta: metav1.ObjectMeta{
			Name:              pkg.GetName(),
			Namespace:         catsrc.GetNamespace(),
			Labels:            catsrc.GetLabels(),
			CreationTimestamp: catsrc.GetCreationTimestamp(),
		},
		Status: operators.PackageManifestStatus{
			CatalogSource:            catsrc.GetName(),
			CatalogSourceDisplayName: catsrc.Spec.DisplayName,
			CatalogSourcePublisher:   catsrc.Spec.Publisher,
			CatalogSourceNamespace:   catsrc.GetNamespace(),
			PackageName:              pkg.Name,
			Channels:                 make([]operators.PackageChannel, len(pkgChannels)),
			DefaultChannel:           pkg.GetDefaultChannelName(),
		},
	}
	if manifest.GetLabels() == nil {
		manifest.SetLabels(labels.Set{})
	}
	manifest.ObjectMeta.Labels["catalog"] = manifest.Status.CatalogSource
	manifest.ObjectMeta.Labels["catalog-namespace"] = manifest.Status.CatalogSourceNamespace

	for i, pkgChannel := range pkgChannels {
		bundle, err := client.GetBundleForChannel(context.Background(), &api.GetBundleInChannelRequest{PkgName: pkg.GetName(), ChannelName: pkgChannel.GetName()})
		if err != nil {
			return nil, err
		}

		csv := operatorsv1alpha1.ClusterServiceVersion{}
		err = json.Unmarshal([]byte(bundle.GetCsvJson()), &csv)
		if err != nil {
			return nil, err
		}
		manifest.Status.Channels[i] = operators.PackageChannel{
			Name:           pkgChannel.GetName(),
			CurrentCSV:     csv.GetName(),
			CurrentCSVDesc: operators.CreateCSVDescription(&csv),
		}

		if manifest.Status.DefaultChannel != "" && csv.GetName() == manifest.Status.DefaultChannel || i == 0 {
			manifest.Status.Provider = operators.AppLink{
				Name: csv.Spec.Provider.Name,
				URL:  csv.Spec.Provider.URL,
			}
			manifest.ObjectMeta.Labels["provider"] = manifest.Status.Provider.Name
			manifest.ObjectMeta.Labels["provider-url"] = manifest.Status.Provider.URL
		}
	}

	return manifest, nil
}
<commit_msg>fix labels.provider issue<commit_after>package provider

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"sync"
	"time"

	"github.com/operator-framework/operator-registry/pkg/api"
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	operatorsv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators"
)

const (
	defaultConnectionTimeout = 5 * time.Second
)

type sourceKey struct {
	name      string
	namespace string
}

type registryClient struct {
	api.RegistryClient
	source *operatorsv1alpha1.CatalogSource
	conn   *grpc.ClientConn
}

func newRegistryClient(source *operatorsv1alpha1.CatalogSource, conn *grpc.ClientConn) registryClient {
	return registryClient{
		RegistryClient: api.NewRegistryClient(conn),
		source:         source,
		conn:           conn,
	}
}

// RegistryProvider aggregates several `CatalogSources` and establishes gRPC connections to their registry servers.
type RegistryProvider struct {
	*queueinformer.Operator

	mu              sync.RWMutex
	globalNamespace string
	clients         map[sourceKey]registryClient
}

var _ PackageManifestProvider = &RegistryProvider{}

func NewRegistryProvider(crClient versioned.Interface, operator *queueinformer.Operator, wakeupInterval time.Duration, watchedNamespaces []string, globalNamespace string) *RegistryProvider {
	p := &RegistryProvider{
		Operator: operator,

		globalNamespace: globalNamespace,
		clients:         make(map[sourceKey]registryClient),
	}

	sourceHandlers := &cache.ResourceEventHandlerFuncs{
		DeleteFunc: p.catalogSourceDeleted,
	}
	for _, namespace := range watchedNamespaces {
		factory := externalversions.NewSharedInformerFactoryWithOptions(crClient, wakeupInterval, externalversions.WithNamespace(namespace))
		sourceInformer := factory.Operators().V1alpha1().CatalogSources()

		// Register queue and QueueInformer
		logrus.WithField("namespace", namespace).Info("watching catalogsources")
		queueName := fmt.Sprintf("%s/catalogsources", namespace)
		sourceQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), queueName)
		sourceQueueInformer := queueinformer.NewInformer(sourceQueue, sourceInformer.Informer(), p.syncCatalogSource, sourceHandlers, queueName, metrics.NewMetricsNil(), logrus.New())
		p.RegisterQueueInformer(sourceQueueInformer)
	}

	return p
}

func (p *RegistryProvider) getClient(key sourceKey) (registryClient, bool) {
	p.mu.RLock()
	defer p.mu.RUnlock()

	client, ok := p.clients[key]
	return client, ok
}

func (p *RegistryProvider) setClient(client registryClient, key sourceKey) {
	p.mu.Lock()
	defer p.mu.Unlock()

	p.clients[key] = client
}

func (p *RegistryProvider) removeClient(key sourceKey) (registryClient, bool) {
	p.mu.Lock()
	defer p.mu.Unlock()

	client, ok := p.clients[key]
	if !ok {
		return registryClient{}, false
	}

	delete(p.clients, key)
	return client, true
}

func (p *RegistryProvider) syncCatalogSource(obj interface{}) (syncError error) {
	source, ok := obj.(*operatorsv1alpha1.CatalogSource)
	if !ok {
		logrus.Errorf("catalogsource type assertion failed: wrong type: %#v", obj)
	}

	logger := logrus.WithFields(logrus.Fields{
		"action":    "sync catalogsource",
		"name":      source.GetName(),
		"namespace": source.GetNamespace(),
	})

	if source.Status.RegistryServiceStatus == nil {
		logger.Debug("registry service is not ready for grpc connection")
		return
	}

	key := sourceKey{source.GetName(), source.GetNamespace()}
	client, ok := p.getClient(key)
	if ok && source.Status.RegistryServiceStatus.ServiceName != "" {
		logger.Info("update detected, attempting to reset grpc connection")
		client.conn.ResetConnectBackoff()

		ctx, cancel := context.WithTimeout(context.TODO(), defaultConnectionTimeout)
		defer cancel()

		changed := client.conn.WaitForStateChange(ctx, connectivity.TransientFailure)
		if !changed {
			logger.Debugf("grpc connection reset timeout")
			syncError = fmt.Errorf("grpc connection reset timeout")
			return
		}

		logger.Info("grpc connection reset")
		return
	} else if ok {
		// Address type grpc CatalogSource, drop the connection dial in to the new address
		client.conn.Close()
	}

	logger.Info("attempting to add a new grpc connection")
	conn, err := grpc.Dial(source.Address(), grpc.WithInsecure())
	if err != nil {
		logger.WithField("err", err.Error()).Errorf("could not connect to registry service")
		syncError = err
		return
	}

	p.setClient(newRegistryClient(source, conn), key)
	logger.Info("new grpc connection added")

	return
}

func (p *RegistryProvider) catalogSourceDeleted(obj interface{}) {
	catsrc, ok := obj.(metav1.Object)
	if !ok {
		if !ok {
			tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
			if !ok {
				utilruntime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj))
				return
			}

			catsrc, ok = tombstone.Obj.(metav1.Object)
			if !ok {
				utilruntime.HandleError(fmt.Errorf("tombstone contained object that is not a Namespace %#v", obj))
				return
			}
		}
	}

	logger := logrus.WithFields(logrus.Fields{
		"action":    "CatalogSource Deleted",
		"name":      catsrc.GetName(),
		"namespace": catsrc.GetNamespace(),
	})
	logger.Debugf("attempting to remove grpc connection")

	key := sourceKey{catsrc.GetName(), catsrc.GetNamespace()}
	client, removed := p.removeClient(key)
	if removed {
		err := client.conn.Close()
		if err != nil {
			logger.WithField("err", err.Error()).Error("error closing connection")
			utilruntime.HandleError(fmt.Errorf("error closing connection %s", err.Error()))
			return
		}
		logger.Debug("grpc connection removed")
		return
	}

	logger.Debugf("no gRPC connection to remove")

}

func (p *RegistryProvider) Get(namespace, name string) (*operators.PackageManifest, error) {
	logger := logrus.WithFields(logrus.Fields{
		"action":    "Get PackageManifest",
		"name":      name,
		"namespace": namespace,
	})

	pkgs, err := p.List(namespace)
	if err != nil {
		return nil, fmt.Errorf("could not list packages in namespace %s", namespace)
	}

	for _, pkg := range pkgs.Items {
		if pkg.GetName() == name {
			return &pkg, nil
		}
	}

	logger.Info("package not found")
	return nil, nil
}

func (p *RegistryProvider) List(namespace string) (*operators.PackageManifestList, error) {
	logger := logrus.WithFields(logrus.Fields{
		"action":    "List PackageManifests",
		"namespace": namespace,
	})

	p.mu.RLock()
	defer p.mu.RUnlock()

	pkgs := []operators.PackageManifest{}
	for _, client := range p.clients {
		if client.source.GetNamespace() == namespace || client.source.GetNamespace() == p.globalNamespace || namespace == metav1.NamespaceAll {
			logger.Debugf("found CatalogSource %s", client.source.GetName())

			stream, err := client.ListPackages(context.Background(), &api.ListPackageRequest{})
			if err != nil {
				logger.WithField("err", err.Error()).Warnf("error getting stream")
				continue
			}
			for {
				pkgName, err := stream.Recv()
				if err == io.EOF {
					break
				}

				if err != nil {
					logger.WithField("err", err.Error()).Warnf("error getting data")
					break
				}
				pkg, err := client.GetPackage(context.Background(), &api.GetPackageRequest{Name: pkgName.GetName()})
				if err != nil {
					logger.WithField("err", err.Error()).Warnf("error getting package")
					break
				}
				newPkg, err := toPackageManifest(pkg, client)
				if err != nil {
					logger.WithField("err", err.Error()).Warnf("error converting to packagemanifest")
					break
				}

				// Set request namespace to stop kube clients from complaining about global namespace mismatch.
				if namespace != metav1.NamespaceAll {
					newPkg.SetNamespace(namespace)
				}
				pkgs = append(pkgs, *newPkg)
			}
		}
	}

	return &operators.PackageManifestList{Items: pkgs}, nil
}

func toPackageManifest(pkg *api.Package, client registryClient) (*operators.PackageManifest, error) {
	pkgChannels := pkg.GetChannels()
	catsrc := client.source
	manifest := &operators.PackageManifest{
		ObjectMeta: metav1.ObjectMeta{
			Name:              pkg.GetName(),
			Namespace:         catsrc.GetNamespace(),
			Labels:            catsrc.GetLabels(),
			CreationTimestamp: catsrc.GetCreationTimestamp(),
		},
		Status: operators.PackageManifestStatus{
			CatalogSource:            catsrc.GetName(),
			CatalogSourceDisplayName: catsrc.Spec.DisplayName,
			CatalogSourcePublisher:   catsrc.Spec.Publisher,
			CatalogSourceNamespace:   catsrc.GetNamespace(),
			PackageName:              pkg.Name,
			Channels:                 make([]operators.PackageChannel, len(pkgChannels)),
			DefaultChannel:           pkg.GetDefaultChannelName(),
		},
	}
	if manifest.GetLabels() == nil {
		manifest.SetLabels(labels.Set{})
	}
	manifest.ObjectMeta.Labels["catalog"] = manifest.Status.CatalogSource
	manifest.ObjectMeta.Labels["catalog-namespace"] = manifest.Status.CatalogSourceNamespace

	for i, pkgChannel := range pkgChannels {
		bundle, err := client.GetBundleForChannel(context.Background(), &api.GetBundleInChannelRequest{PkgName: pkg.GetName(), ChannelName: pkgChannel.GetName()})
		if err != nil {
			return nil, err
		}

		csv := operatorsv1alpha1.ClusterServiceVersion{}
		err = json.Unmarshal([]byte(bundle.GetCsvJson()), &csv)
		if err != nil {
			return nil, err
		}
		manifest.Status.Channels[i] = operators.PackageChannel{
			Name:           pkgChannel.GetName(),
			CurrentCSV:     csv.GetName(),
			CurrentCSVDesc: operators.CreateCSVDescription(&csv),
		}

		if manifest.Status.DefaultChannel != "" && pkgChannel.GetName() == manifest.Status.DefaultChannel || i == 0 {
			manifest.Status.Provider = operators.AppLink{
				Name: csv.Spec.Provider.Name,
				URL:  csv.Spec.Provider.URL,
			}
			manifest.ObjectMeta.Labels["provider"] = manifest.Status.Provider.Name
			manifest.ObjectMeta.Labels["provider-url"] = manifest.Status.Provider.URL
		}
	}

	return manifest, nil
}
<|endoftext|>


<commit_before>package rubyapp

import (
	"path/filepath"

	"github.com/hashicorp/otto/helper/compile"
	"github.com/hashicorp/otto/helper/schema"
)

const defaultLatestVersion = "2.2"

type customizations struct {
	Opts *compile.AppOptions
}

func (c *customizations) processRuby(d *schema.FieldData) error {
	vsn := d.Get("ruby_version")

	// If we were asked to detect the version, we attempt to do so.
	// If we can't detect it for non-erroneous reasons, we use our default.
	if vsn == "detect" {
		var err error
		vsn, err = detectRubyVersionGemfile(filepath.Dir(c.Opts.Ctx.Appfile.Path))
		if err != nil {
			return err
		}
		if vsn == "" {
			vsn = defaultLatestVersion
		}
	}

	c.Opts.Bindata.Context["ruby_version"] = vsn
	return nil
}
<commit_msg>app/ruby: output version during compile<commit_after>package rubyapp

import (
	"fmt"
	"path/filepath"

	"github.com/hashicorp/otto/helper/compile"
	"github.com/hashicorp/otto/helper/schema"
)

const defaultLatestVersion = "2.2"

type customizations struct {
	Opts *compile.AppOptions
}

func (c *customizations) processRuby(d *schema.FieldData) error {
	vsn := d.Get("ruby_version")

	// If we were asked to detect the version, we attempt to do so.
	// If we can't detect it for non-erroneous reasons, we use our default.
	if vsn == "detect" {
		var err error
		c.Opts.Ctx.Ui.Header("Detecting Ruby version to use...")
		vsn, err = detectRubyVersionGemfile(filepath.Dir(c.Opts.Ctx.Appfile.Path))
		if err != nil {
			return err
		}
		if vsn != "" {
			c.Opts.Ctx.Ui.Message(fmt.Sprintf(
				"Detected desired Ruby version: %s", vsn))
		}
		if vsn == "" {
			vsn = defaultLatestVersion
			c.Opts.Ctx.Ui.Message(fmt.Sprintf(
				"No desired Ruby version found! Will use the default: %s", vsn))
		}
	}

	c.Opts.Bindata.Context["ruby_version"] = vsn
	return nil
}
<|endoftext|>


<commit_before>package buildah

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/werf/lockgate"
	"github.com/werf/werf/pkg/buildah/types"
	"github.com/werf/werf/pkg/werf"

	"github.com/werf/logboek"
	"github.com/werf/werf/pkg/docker"
)

type DockerWithFuseBuildah struct {
	BaseBuildah
}

func NewDockerWithFuseBuildah(commonOpts CommonBuildahOpts, opts DockerWithFuseModeOpts) (*DockerWithFuseBuildah, error) {
	b := &DockerWithFuseBuildah{}

	baseBuildah, err := NewBaseBuildah(commonOpts.TmpDir)
	if err != nil {
		return nil, fmt.Errorf("unable to create BaseBuildah: %s", err)
	}
	b.BaseBuildah = *baseBuildah

	return b, nil
}

func (b *DockerWithFuseBuildah) Tag(ctx context.Context, ref, newRef string, opts TagOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"tag", ref, newRef}, opts.LogWriter)
	return err
}
func (b *DockerWithFuseBuildah) Push(ctx context.Context, ref string, opts PushOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"push", ref, fmt.Sprintf("docker://%s", ref)}, opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) BuildFromDockerfile(ctx context.Context, dockerfile []byte, opts BuildFromDockerfileOpts) (string, error) {
	sessionTmpDir, _, _, err := b.prepareBuildFromDockerfile(dockerfile, opts.ContextTar)
	if err != nil {
		return "", fmt.Errorf("error preparing for build from dockerfile: %s", err)
	}
	defer func() {
		if debug() {
			return
		}

		if err = os.RemoveAll(sessionTmpDir); err != nil {
			logboek.Warn().LogF("unable to remove session tmp dir %q: %s\n", sessionTmpDir, err)
		}
	}()

	output, _, err := b.runBuildah(
		ctx,
		[]string{
			"--volume", fmt.Sprintf("%s:/.werf/buildah/tmp", sessionTmpDir),
			"--workdir", "/.werf/buildah/tmp/context",
		},
		[]string{"bud", "-f", "/.werf/buildah/tmp/Dockerfile"}, opts.LogWriter,
	)
	if err != nil {
		return "", err
	}

	outputLines := scanLines(output)

	return outputLines[len(outputLines)-1], nil
}

func (b *DockerWithFuseBuildah) RunCommand(ctx context.Context, container string, command []string, opts RunCommandOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, append([]string{"run", container}, command...), opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) FromCommand(ctx context.Context, container string, image string, opts FromCommandOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"from", "--name", container, image}, opts.LogWriter)
	return err
}

// TODO(ilya-lesikov): make it more generic to handle not only images
func (b *DockerWithFuseBuildah) Inspect(ctx context.Context, ref string) (*types.BuilderInfo, error) {
	stdout, stderr, err := b.runBuildah(ctx, []string{}, []string{"inspect", "--type", "image", ref}, nil)
	if err != nil {
		if strings.Contains(stderr, "image not known") {
			return nil, nil
		}
		return nil, err
	}

	var res types.BuilderInfo
	if err := json.Unmarshal([]byte(stdout), &res); err != nil {
		return nil, fmt.Errorf("unable to unmarshal buildah inspect json output: %s", err)
	}

	return &res, nil
}

func (b *DockerWithFuseBuildah) Pull(ctx context.Context, ref string, opts PullOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"pull", ref}, opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) Rmi(ctx context.Context, ref string, opts RmiOpts) error {
	args := []string{"rmi"}
	if opts.Force {
		args = append(args, "-f")
	}
	args = append(args, ref)

	_, _, err := b.runBuildah(ctx, []string{}, args, opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) runBuildah(ctx context.Context, dockerArgs []string, buildahArgs []string, logWriter io.Writer) (string, string, error) {
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	var stdoutWriter io.Writer
	var stderrWriter io.Writer

	if logWriter != nil {
		stdoutWriter = io.MultiWriter(stdout, logWriter)
		stderrWriter = io.MultiWriter(stderr, logWriter)
	} else {
		stdoutWriter = stdout
		stderrWriter = stderr
	}

	if err := runStorageContainer(ctx, BuildahStorageContainerName, BuildahImage); err != nil {
		return "", "", fmt.Errorf("unable to run werf buildah storage container: %s", err)
	}

	args := []string{"--rm"}
	args = append(args, dockerArgs...)
	args = append(args, buildahWithFuseDockerArgs(BuildahStorageContainerName)...)
	args = append(args, buildahArgs...)

	if debug() {
		fmt.Printf("DEBUG CMD: docker run -ti %s\n", strings.Join(args, " "))
	}

	err := docker.CliRun_ProvidedOutput(ctx, stdoutWriter, stderrWriter, args...)
	return stdout.String(), stderr.String(), err
}

func runStorageContainer(ctx context.Context, name, image string) error {
	exist, err := docker.ContainerExist(ctx, name)
	if err != nil {
		return fmt.Errorf("unable to check existance of docker container %q: %s", name, err)
	}
	if exist {
		return nil
	}

	return werf.WithHostLock(ctx, fmt.Sprintf("buildah.container.%s", name), lockgate.AcquireOptions{Timeout: time.Second * 600}, func() error {
		return logboek.Context(ctx).LogProcess("Creating container %s using image %s", name, image).DoError(func() error {
			exist, err := docker.ContainerExist(ctx, name)
			if err != nil {
				return fmt.Errorf("unable to check existance of docker container %q: %s", name, err)
			}
			if exist {
				return nil
			}

			imageExist, err := docker.ImageExist(ctx, image)
			if err != nil {
				return fmt.Errorf("unable to check existance of docker image %q: %s", image, err)
			}
			if !imageExist {
				if err := docker.CliPullWithRetries(ctx, image); err != nil {
					return err
				}
			}

			return docker.CliCreate(ctx, "--name", name, image)
		})
	})
}

func buildahWithFuseDockerArgs(storageContainerName string) []string {
	return []string{
		"--user", "1000",
		"--device", "/dev/fuse",
		"--security-opt", "seccomp=unconfined",
		"--security-opt", "apparmor=unconfined",
		"--volume", fmt.Sprintf("%s:%s", docker.DockerConfigDir, "/home/build/.docker"),
		"--volumes-from", storageContainerName,
		BuildahImage, "buildah",
	}
}

func scanLines(data string) []string {
	var lines []string

	s := bufio.NewScanner(strings.NewReader(data))
	for s.Scan() {
		lines = append(lines, s.Text())
	}

	return lines
}
<commit_msg>refactor(buildah): public docker-with-fuse docker flags<commit_after>package buildah

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"github.com/werf/lockgate"
	"github.com/werf/werf/pkg/buildah/types"
	"github.com/werf/werf/pkg/werf"

	"github.com/werf/logboek"
	"github.com/werf/werf/pkg/docker"
)

type DockerWithFuseBuildah struct {
	BaseBuildah
}

func NewDockerWithFuseBuildah(commonOpts CommonBuildahOpts, opts DockerWithFuseModeOpts) (*DockerWithFuseBuildah, error) {
	b := &DockerWithFuseBuildah{}

	baseBuildah, err := NewBaseBuildah(commonOpts.TmpDir)
	if err != nil {
		return nil, fmt.Errorf("unable to create BaseBuildah: %s", err)
	}
	b.BaseBuildah = *baseBuildah

	return b, nil
}

func (b *DockerWithFuseBuildah) Tag(ctx context.Context, ref, newRef string, opts TagOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"tag", ref, newRef}, opts.LogWriter)
	return err
}
func (b *DockerWithFuseBuildah) Push(ctx context.Context, ref string, opts PushOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"push", ref, fmt.Sprintf("docker://%s", ref)}, opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) BuildFromDockerfile(ctx context.Context, dockerfile []byte, opts BuildFromDockerfileOpts) (string, error) {
	sessionTmpDir, _, _, err := b.prepareBuildFromDockerfile(dockerfile, opts.ContextTar)
	if err != nil {
		return "", fmt.Errorf("error preparing for build from dockerfile: %s", err)
	}
	defer func() {
		if debug() {
			return
		}

		if err = os.RemoveAll(sessionTmpDir); err != nil {
			logboek.Warn().LogF("unable to remove session tmp dir %q: %s\n", sessionTmpDir, err)
		}
	}()

	output, _, err := b.runBuildah(
		ctx,
		[]string{
			"--volume", fmt.Sprintf("%s:/.werf/buildah/tmp", sessionTmpDir),
			"--workdir", "/.werf/buildah/tmp/context",
		},
		[]string{"bud", "-f", "/.werf/buildah/tmp/Dockerfile"}, opts.LogWriter,
	)
	if err != nil {
		return "", err
	}

	outputLines := scanLines(output)

	return outputLines[len(outputLines)-1], nil
}

func (b *DockerWithFuseBuildah) RunCommand(ctx context.Context, container string, command []string, opts RunCommandOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, append([]string{"run", container}, command...), opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) FromCommand(ctx context.Context, container string, image string, opts FromCommandOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"from", "--name", container, image}, opts.LogWriter)
	return err
}

// TODO(ilya-lesikov): make it more generic to handle not only images
func (b *DockerWithFuseBuildah) Inspect(ctx context.Context, ref string) (*types.BuilderInfo, error) {
	stdout, stderr, err := b.runBuildah(ctx, []string{}, []string{"inspect", "--type", "image", ref}, nil)
	if err != nil {
		if strings.Contains(stderr, "image not known") {
			return nil, nil
		}
		return nil, err
	}

	var res types.BuilderInfo
	if err := json.Unmarshal([]byte(stdout), &res); err != nil {
		return nil, fmt.Errorf("unable to unmarshal buildah inspect json output: %s", err)
	}

	return &res, nil
}

func (b *DockerWithFuseBuildah) Pull(ctx context.Context, ref string, opts PullOpts) error {
	_, _, err := b.runBuildah(ctx, []string{}, []string{"pull", ref}, opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) Rmi(ctx context.Context, ref string, opts RmiOpts) error {
	args := []string{"rmi"}
	if opts.Force {
		args = append(args, "-f")
	}
	args = append(args, ref)

	_, _, err := b.runBuildah(ctx, []string{}, args, opts.LogWriter)
	return err
}

func (b *DockerWithFuseBuildah) runBuildah(ctx context.Context, dockerArgs []string, buildahArgs []string, logWriter io.Writer) (string, string, error) {
	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	var stdoutWriter io.Writer
	var stderrWriter io.Writer

	if logWriter != nil {
		stdoutWriter = io.MultiWriter(stdout, logWriter)
		stderrWriter = io.MultiWriter(stderr, logWriter)
	} else {
		stdoutWriter = stdout
		stderrWriter = stderr
	}

	if err := runStorageContainer(ctx, BuildahStorageContainerName, BuildahImage); err != nil {
		return "", "", fmt.Errorf("unable to run werf buildah storage container: %s", err)
	}

	args := []string{"--rm"}
	args = append(args, dockerArgs...)
	args = append(args, BuildahWithFuseDockerArgs(BuildahStorageContainerName, docker.DockerConfigDir)...)
	args = append(args, buildahArgs...)

	if debug() {
		fmt.Printf("DEBUG CMD: docker run -ti %s\n", strings.Join(args, " "))
	}

	err := docker.CliRun_ProvidedOutput(ctx, stdoutWriter, stderrWriter, args...)
	return stdout.String(), stderr.String(), err
}

func runStorageContainer(ctx context.Context, name, image string) error {
	exist, err := docker.ContainerExist(ctx, name)
	if err != nil {
		return fmt.Errorf("unable to check existance of docker container %q: %s", name, err)
	}
	if exist {
		return nil
	}

	return werf.WithHostLock(ctx, fmt.Sprintf("buildah.container.%s", name), lockgate.AcquireOptions{Timeout: time.Second * 600}, func() error {
		return logboek.Context(ctx).LogProcess("Creating container %s using image %s", name, image).DoError(func() error {
			exist, err := docker.ContainerExist(ctx, name)
			if err != nil {
				return fmt.Errorf("unable to check existance of docker container %q: %s", name, err)
			}
			if exist {
				return nil
			}

			imageExist, err := docker.ImageExist(ctx, image)
			if err != nil {
				return fmt.Errorf("unable to check existance of docker image %q: %s", image, err)
			}
			if !imageExist {
				if err := docker.CliPullWithRetries(ctx, image); err != nil {
					return err
				}
			}

			return docker.CliCreate(ctx, "--name", name, image)
		})
	})
}

func BuildahWithFuseDockerArgs(storageContainerName, dockerConfigDir string) []string {
	return []string{
		"--user", "1000",
		"--device", "/dev/fuse",
		"--security-opt", "seccomp=unconfined",
		"--security-opt", "apparmor=unconfined",
		"--volume", fmt.Sprintf("%s:%s", dockerConfigDir, "/home/build/.docker"),
		"--volumes-from", storageContainerName,
		BuildahImage, "buildah",
	}
}

func scanLines(data string) []string {
	var lines []string

	s := bufio.NewScanner(strings.NewReader(data))
	for s.Scan() {
		lines = append(lines, s.Text())
	}

	return lines
}
<|endoftext|>


<commit_before>package clideployment

import (
	"fmt"

	"github.com/containerum/chkit/pkg/context"
	containerControl "github.com/containerum/chkit/pkg/controls/container"
	"github.com/containerum/chkit/pkg/model/configmap"
	"github.com/containerum/chkit/pkg/model/container"
	"github.com/containerum/chkit/pkg/model/deployment"
	"github.com/containerum/chkit/pkg/porta"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/containerum/chkit/pkg/util/ferr"
	"github.com/octago/sflags/gen/gpflag"
	"github.com/spf13/cobra"
)

func ReplaceContainer(ctx *context.Context) *cobra.Command {
	var flags struct {
		Force         bool   `flag:"force f" desc:"suppress confirmation"`
		ContainerName string `flag:"container" desc:"container name, required on --force"`
		Deployment    string `desc:"deployment name, required on --force"`
		containerControl.ReplaceFlags
		porta.Importer
	}
	command := &cobra.Command{
		Use:     "deployment-container",
		Aliases: []string{"depl-cont", "container", "dc"},
		Short:   "Replace deployment container.",
		Long: "Replace deployment container.\n" +
			"Runs in one-line mode, suitable for integration with other tools, and in interactive wizard mode.",
		Run: func(cmd *cobra.Command, args []string) {
			var logger = ctx.Log.Command("replace deployment container")
			logger.Debugf("START")
			defer logger.Debugf("END")
			if flags.Force {
				if flags.Deployment == "" {
					ferr.Printf("deployment name must be provided as --deployment while using --force")
					ctx.Exit(1)
				}
				if flags.ContainerName == "" {
					ferr.Printf("container name must be provided as --container while using --force")
					ctx.Exit(1)
				}
				var depl, err = ctx.Client.GetDeployment(ctx.GetNamespace().ID, flags.Deployment)
				if err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont, ok := depl.Containers.GetByName(flags.ContainerName)
				if !ok {
					ferr.Printf("container %q doesn't exist", flags.ContainerName)
					ctx.Exit(1)
				}
				if flags.ImportActivated() {
					var importedCont container.Container
					if err := flags.Import(&importedCont); err != nil {
						ferr.Println(err)
						ctx.Exit(1)
					}
					cont, err = flags.Patch(importedCont)
				} else {
					flagCont, err := flags.Container()
					if err != nil {
						ferr.Println(err)
						ctx.Exit(1)
					}
					cont, err = flags.Patch(flagCont)
				}
				if err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont.Name = flags.ContainerName
				depl.Containers, _ = depl.Containers.Replace(cont)
				if err := ctx.Client.ReplaceDeployment(ctx.GetNamespace().ID, depl); err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				fmt.Println("Ok")
				return
			}

			var depl deployment.Deployment
			if flags.Deployment == "" {
				logger.Debugf("getting deployment list from namespace %q", ctx.GetNamespace())
				deplList, err := ctx.Client.GetDeploymentList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get deployment list from namespace %q", ctx.GetNamespace())
					ferr.Println(err)
					ctx.Exit(1)
				}
				logger.Debugf("selecting deployment")
				(&activekit.Menu{
					Title: "Select deployment",
					Items: activekit.ItemsFromIter(uint(deplList.Len()), func(index uint) *activekit.MenuItem {
						var d = deplList[index]
						return &activekit.MenuItem{
							Label: d.Name,
							Action: func() error {
								flags.Deployment = d.Name
								depl = d
								logger.Debugf("deployment %q selected", d.Name)
								return nil
							},
						}
					}),
				}).Run()
			} else {
				logger.Debugf("getting deployment %q", flags.Deployment)
				var err error
				depl, err = ctx.Client.GetDeployment(ctx.GetNamespace().ID, flags.Deployment)
				if err != nil {
					logger.WithError(err).Errorf("unable to get deployment %q", flags.Deployment)
					ferr.Println(err)
					ctx.Exit(1)
				}
			}

			var cont container.Container

			if flags.ContainerName == "" {
				logger.Debugf("selecting container")
				(&activekit.Menu{
					Title: fmt.Sprintf("Select container in deployment %q", depl.Name),
					Items: activekit.ItemsFromIter(uint(len(depl.Containers)), func(index uint) *activekit.MenuItem {
						var c = depl.Containers[index]
						return &activekit.MenuItem{
							Label: c.Name,
							Action: func() error {
								flags.ContainerName = c.Name
								logger.Debugf("selected container %q", c.Name)
								cont = c
								return nil
							},
						}
					}),
				}).Run()
			} else {
				var ok bool
				cont, ok = depl.Containers.GetByName(flags.ContainerName)
				if !ok {
					ferr.Printf("container %q not found in deployment %q", flags.ContainerName, depl.Name)
					ctx.Exit(1)
				}
			}

			if flags.ImportActivated() {
				var importedCont container.Container
				if err := flags.Import(&importedCont); err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont = cont.Patch(importedCont)
			} else {
				flagCont, err := flags.Container()
				if err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont = cont.Patch(flagCont)
			}

			logger.Debugf("building container from flags")
			cont, err := flags.Patch(cont)
			if err != nil {
				logger.WithError(err).Errorf("unable to build container from flags")
				ferr.Println(err)
				ctx.Exit(1)
			}
			cont.Name = flags.ContainerName
			fmt.Println(cont.RenderTable())

			/*var volumes = make(chan volume.VolumeList)
			go func() {
				logger := logger.Component("getting namespace list")
				logger.Debugf("START")
				defer logger.Debugf("END")
				defer close(volumes)
				var volumeList, err = ctx.Client.GetVolumeList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get volume list from namespace %q", ctx.GetNamespace())
					ferr.Println(err)
					ctx.Exit(1)
				}
				volumes <- volumeList
			}()*/

			var deployments = make(chan deployment.DeploymentList)
			go func() {
				logger := logger.Component("getting deployment list")
				logger.Debugf("START")
				defer logger.Debugf("END")
				defer close(deployments)
				deplList, err := ctx.Client.GetDeploymentList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get deployment list from namespace %q", ctx.GetNamespace())
					ferr.Println(err)
					ctx.Exit(1)
				}
				deployments <- deplList
			}()

			var configs = make(chan configmap.ConfigMapList)
			go func() {
				logger := logger.Component("getting configmap list")
				logger.Debugf("START")
				defer logger.Debugf("END")
				defer close(configs)
				configList, err := ctx.Client.GetConfigmapList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get configmap list")
					ferr.Println(err)
					ctx.Exit(1)
				}
				configs <- configList
			}()

			logger.Debugf("running wizard")
			cont = containerControl.Wizard{
				Container:  cont,
				Deployment: flags.Deployment,
				//	Volumes:     (<-volumes).Names(),
				Configs:     (<-configs).Names(),
				Deployments: (<-deployments).Names(),
			}.Run()

			if activekit.YesNo("Are you sure you want to update container %q in deployment %q?", cont.Name, flags.Deployment) {
				logger.Debugf("replacing container %q in deployment %q", cont.Name, flags.Deployment)
				if err := ctx.Client.ReplaceDeploymentContainer(ctx.GetNamespace().ID, flags.Deployment, cont); err != nil {
					logger.WithError(err).Errorf("unable to replace container %q in deployment %q", cont.Name, flags.Deployment)
					ferr.Println(err)
				}
			}
		},
	}
	if err := gpflag.ParseTo(&flags, command.PersistentFlags()); err != nil {
		panic(err)
	}
	return command
}
<commit_msg>fix error handling<commit_after>package clideployment

import (
	"fmt"

	"github.com/containerum/chkit/pkg/context"
	containerControl "github.com/containerum/chkit/pkg/controls/container"
	"github.com/containerum/chkit/pkg/model/configmap"
	"github.com/containerum/chkit/pkg/model/container"
	"github.com/containerum/chkit/pkg/model/deployment"
	"github.com/containerum/chkit/pkg/porta"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/containerum/chkit/pkg/util/ferr"
	"github.com/octago/sflags/gen/gpflag"
	"github.com/spf13/cobra"
)

func ReplaceContainer(ctx *context.Context) *cobra.Command {
	var flags struct {
		Force         bool   `flag:"force f" desc:"suppress confirmation"`
		ContainerName string `flag:"container" desc:"container name, required on --force"`
		Deployment    string `desc:"deployment name, required on --force"`
		containerControl.ReplaceFlags
		porta.Importer
	}
	command := &cobra.Command{
		Use:     "deployment-container",
		Aliases: []string{"depl-cont", "container", "dc"},
		Short:   "Replace deployment container.",
		Long: "Replace deployment container.\n" +
			"Runs in one-line mode, suitable for integration with other tools, and in interactive wizard mode.",
		Run: func(cmd *cobra.Command, args []string) {
			var logger = ctx.Log.Command("replace deployment container")
			logger.Debugf("START")
			defer logger.Debugf("END")
			if flags.Force {
				if flags.Deployment == "" {
					ferr.Printf("deployment name must be provided as --deployment while using --force")
					ctx.Exit(1)
				}
				if flags.ContainerName == "" {
					ferr.Printf("container name must be provided as --container while using --force")
					ctx.Exit(1)
				}
				var depl, err = ctx.Client.GetDeployment(ctx.GetNamespace().ID, flags.Deployment)
				if err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont, ok := depl.Containers.GetByName(flags.ContainerName)
				if !ok {
					ferr.Printf("container %q doesn't exist", flags.ContainerName)
					ctx.Exit(1)
				}
				if flags.ImportActivated() {
					var importedCont container.Container
					if err := flags.Import(&importedCont); err != nil {
						ferr.Println(err)
						ctx.Exit(1)
					}
					cont, err = flags.Patch(importedCont)
				} else {
					flagCont, err := flags.Container()
					if err != nil {
						ferr.Println(err)
						ctx.Exit(1)
					}
					cont, err = flags.Patch(flagCont)
					if err != nil {
						ferr.Println(err)
						ctx.Exit(1)
					}
				}
				if err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont.Name = flags.ContainerName
				depl.Containers, _ = depl.Containers.Replace(cont)
				if err := ctx.Client.ReplaceDeployment(ctx.GetNamespace().ID, depl); err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				fmt.Println("Ok")
				return
			}

			var depl deployment.Deployment
			if flags.Deployment == "" {
				logger.Debugf("getting deployment list from namespace %q", ctx.GetNamespace())
				deplList, err := ctx.Client.GetDeploymentList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get deployment list from namespace %q", ctx.GetNamespace())
					ferr.Println(err)
					ctx.Exit(1)
				}
				logger.Debugf("selecting deployment")
				(&activekit.Menu{
					Title: "Select deployment",
					Items: activekit.ItemsFromIter(uint(deplList.Len()), func(index uint) *activekit.MenuItem {
						var d = deplList[index]
						return &activekit.MenuItem{
							Label: d.Name,
							Action: func() error {
								flags.Deployment = d.Name
								depl = d
								logger.Debugf("deployment %q selected", d.Name)
								return nil
							},
						}
					}),
				}).Run()
			} else {
				logger.Debugf("getting deployment %q", flags.Deployment)
				var err error
				depl, err = ctx.Client.GetDeployment(ctx.GetNamespace().ID, flags.Deployment)
				if err != nil {
					logger.WithError(err).Errorf("unable to get deployment %q", flags.Deployment)
					ferr.Println(err)
					ctx.Exit(1)
				}
			}

			var cont container.Container

			if flags.ContainerName == "" {
				logger.Debugf("selecting container")
				(&activekit.Menu{
					Title: fmt.Sprintf("Select container in deployment %q", depl.Name),
					Items: activekit.ItemsFromIter(uint(len(depl.Containers)), func(index uint) *activekit.MenuItem {
						var c = depl.Containers[index]
						return &activekit.MenuItem{
							Label: c.Name,
							Action: func() error {
								flags.ContainerName = c.Name
								logger.Debugf("selected container %q", c.Name)
								cont = c
								return nil
							},
						}
					}),
				}).Run()
			} else {
				var ok bool
				cont, ok = depl.Containers.GetByName(flags.ContainerName)
				if !ok {
					ferr.Printf("container %q not found in deployment %q", flags.ContainerName, depl.Name)
					ctx.Exit(1)
				}
			}

			if flags.ImportActivated() {
				var importedCont container.Container
				if err := flags.Import(&importedCont); err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont = cont.Patch(importedCont)
			} else {
				flagCont, err := flags.Container()
				if err != nil {
					ferr.Println(err)
					ctx.Exit(1)
				}
				cont = cont.Patch(flagCont)
			}

			logger.Debugf("building container from flags")
			cont, err := flags.Patch(cont)
			if err != nil {
				logger.WithError(err).Errorf("unable to build container from flags")
				ferr.Println(err)
				ctx.Exit(1)
			}
			cont.Name = flags.ContainerName
			fmt.Println(cont.RenderTable())

			/*var volumes = make(chan volume.VolumeList)
			go func() {
				logger := logger.Component("getting namespace list")
				logger.Debugf("START")
				defer logger.Debugf("END")
				defer close(volumes)
				var volumeList, err = ctx.Client.GetVolumeList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get volume list from namespace %q", ctx.GetNamespace())
					ferr.Println(err)
					ctx.Exit(1)
				}
				volumes <- volumeList
			}()*/

			var deployments = make(chan deployment.DeploymentList)
			go func() {
				logger := logger.Component("getting deployment list")
				logger.Debugf("START")
				defer logger.Debugf("END")
				defer close(deployments)
				deplList, err := ctx.Client.GetDeploymentList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get deployment list from namespace %q", ctx.GetNamespace())
					ferr.Println(err)
					ctx.Exit(1)
				}
				deployments <- deplList
			}()

			var configs = make(chan configmap.ConfigMapList)
			go func() {
				logger := logger.Component("getting configmap list")
				logger.Debugf("START")
				defer logger.Debugf("END")
				defer close(configs)
				configList, err := ctx.Client.GetConfigmapList(ctx.GetNamespace().ID)
				if err != nil {
					logger.WithError(err).Errorf("unable to get configmap list")
					ferr.Println(err)
					ctx.Exit(1)
				}
				configs <- configList
			}()

			logger.Debugf("running wizard")
			cont = containerControl.Wizard{
				Container:  cont,
				Deployment: flags.Deployment,
				//	Volumes:     (<-volumes).Names(),
				Configs:     (<-configs).Names(),
				Deployments: (<-deployments).Names(),
			}.Run()

			if activekit.YesNo("Are you sure you want to update container %q in deployment %q?", cont.Name, flags.Deployment) {
				logger.Debugf("replacing container %q in deployment %q", cont.Name, flags.Deployment)
				if err := ctx.Client.ReplaceDeploymentContainer(ctx.GetNamespace().ID, flags.Deployment, cont); err != nil {
					logger.WithError(err).Errorf("unable to replace container %q in deployment %q", cont.Name, flags.Deployment)
					ferr.Println(err)
				}
			}
		},
	}
	if err := gpflag.ParseTo(&flags, command.PersistentFlags()); err != nil {
		panic(err)
	}
	return command
}
<|endoftext|>


<commit_before>package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"github.com/rancher/norman/httperror"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/proxy"
	"k8s.io/client-go/rest"
)

type RemoteService struct {
	sync.Mutex

	cluster   *v3.Cluster
	transport transportGetter
	url       urlGetter
	auth      authGetter

	factory       dialer.Factory
	clusterLister v3.ClusterLister
	caCert        string
	httpTransport *http.Transport
}

var (
	er = &errorResponder{}
)

type urlGetter func() (url.URL, error)

type authGetter func() (string, error)

type transportGetter func() (http.RoundTripper, error)

type errorResponder struct {
}

func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func prefix(cluster *v3.Cluster) string {
	return "/k8s/clusters/" + cluster.Name
}

func New(localConfig *rest.Config, cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if cluster.Spec.Internal {
		return NewLocal(localConfig, cluster)
	}
	return NewRemote(cluster, clusterLister, factory)
}

func NewLocal(localConfig *rest.Config, cluster *v3.Cluster) (*RemoteService, error) {
	// the gvk is ignored by us, so just pass in any gvk
	hostURL, _, err := rest.DefaultServerURL(localConfig.Host, localConfig.APIPath, schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	transport, err := rest.TransportFor(localConfig)
	if err != nil {
		return nil, err
	}

	transportGetter := func() (http.RoundTripper, error) {
		return transport, nil
	}

	rs := &RemoteService{
		cluster: cluster,
		url: func() (url.URL, error) {
			return *hostURL, nil
		},
		transport: transportGetter,
	}
	if localConfig.BearerToken != "" {
		rs.auth = func() (string, error) { return "Bearer " + localConfig.BearerToken, nil }
	} else if localConfig.Password != "" {
		rs.auth = func() (string, error) {
			return "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", localConfig.Username, localConfig.Password))), nil
		}
	}

	return rs, nil
}

func NewRemote(cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if !v3.ClusterConditionProvisioned.IsTrue(cluster) {
		return nil, httperror.NewAPIError(httperror.ClusterUnavailable, "cluster not provisioned")
	}

	urlGetter := func() (url.URL, error) {
		newCluster, err := clusterLister.Get("", cluster.Name)
		if err != nil {
			return url.URL{}, err
		}

		u, err := url.Parse(newCluster.Status.APIEndpoint)
		if err != nil {
			return url.URL{}, err
		}
		return *u, nil
	}

	authGetter := func() (string, error) {
		newCluster, err := clusterLister.Get("", cluster.Name)
		if err != nil {
			return "", err
		}

		return "Bearer " + newCluster.Status.ServiceAccountToken, nil
	}

	return &RemoteService{
		cluster:       cluster,
		url:           urlGetter,
		auth:          authGetter,
		clusterLister: clusterLister,
		factory:       factory,
	}, nil
}

func (r *RemoteService) getTransport() (http.RoundTripper, error) {
	if r.transport != nil {
		return r.transport()
	}

	newCluster, err := r.clusterLister.Get("", r.cluster.Name)
	if err != nil {
		return nil, err
	}

	r.Lock()
	defer r.Unlock()

	if r.httpTransport != nil && !r.cacertChanged(newCluster) {
		return r.httpTransport, nil
	}

	transport := &http.Transport{}
	if newCluster.Status.CACert != "" {
		certBytes, err := base64.StdEncoding.DecodeString(newCluster.Status.CACert)
		if err != nil {
			return nil, err
		}
		certs := x509.NewCertPool()
		certs.AppendCertsFromPEM(certBytes)
		transport.TLSClientConfig = &tls.Config{
			RootCAs: certs,
		}
	}

	if r.factory != nil {
		d, err := r.factory.ClusterDialer(newCluster.Name)
		if err != nil {
			return nil, err
		}
		transport.Dial = d
	}

	r.caCert = newCluster.Status.CACert
	if r.httpTransport != nil {
		r.httpTransport.CloseIdleConnections()
	}
	r.httpTransport = transport

	return transport, nil
}

func (r *RemoteService) cacertChanged(cluster *v3.Cluster) bool {
	return r.caCert != cluster.Status.CACert
}

func (r *RemoteService) Close() {
	if r.httpTransport != nil {
		r.httpTransport.CloseIdleConnections()
	}
}

func (r *RemoteService) Handler() http.Handler {
	return r
}

func (r *RemoteService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u, err := r.url()
	if err != nil {
		er.Error(rw, req, err)
		return
	}

	u.Path = strings.TrimPrefix(req.URL.Path, prefix(r.cluster))
	u.RawQuery = req.URL.RawQuery

	proto := req.Header.Get("X-Forwarded-Proto")
	if proto != "" {
		req.URL.Scheme = proto
	} else if req.TLS == nil {
		req.URL.Scheme = "http"
	} else {
		req.URL.Scheme = "https"
	}

	req.URL.Host = req.Host
	if r.auth == nil {
		req.Header.Del("Authorization")
	} else {
		token, err := r.auth()
		if err != nil {
			er.Error(rw, req, err)
			return
		}
		req.Header.Set("Authorization", token)
	}
	transport, err := r.getTransport()
	if err != nil {
		er.Error(rw, req, err)
		return
	}
	httpProxy := proxy.NewUpgradeAwareHandler(&u, transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)
}

func (r *RemoteService) Cluster() *v3.Cluster {
	return r.cluster
}

type SimpleProxy struct {
	url                *url.URL
	transport          http.RoundTripper
	overrideHostHeader bool
}

func NewSimpleProxy(host string, caData []byte, overrideHostHeader bool) (*SimpleProxy, error) {
	hostURL, _, err := rest.DefaultServerURL(host, "", schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	ht := &http.Transport{}
	if len(caData) > 0 {
		certPool := x509.NewCertPool()
		certPool.AppendCertsFromPEM(caData)
		ht.TLSClientConfig = &tls.Config{
			RootCAs: certPool,
		}
	}

	return &SimpleProxy{
		url:                hostURL,
		transport:          ht,
		overrideHostHeader: overrideHostHeader,
	}, nil
}

func (s *SimpleProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u := *s.url
	u.Path = req.URL.Path
	u.RawQuery = req.URL.RawQuery
	req.URL.Scheme = "https"
	req.URL.Host = req.Host
	if s.overrideHostHeader {
		req.Host = u.Host
	}
	httpProxy := proxy.NewUpgradeAwareHandler(&u, s.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)

}
<commit_msg>Removed unused code<commit_after>package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"github.com/rancher/norman/httperror"
	v3 "github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/proxy"
	"k8s.io/client-go/rest"
)

type RemoteService struct {
	sync.Mutex

	cluster   *v3.Cluster
	transport transportGetter
	url       urlGetter
	auth      authGetter

	factory       dialer.Factory
	clusterLister v3.ClusterLister
	caCert        string
	httpTransport *http.Transport
}

var (
	er = &errorResponder{}
)

type urlGetter func() (url.URL, error)

type authGetter func() (string, error)

type transportGetter func() (http.RoundTripper, error)

type errorResponder struct {
}

func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func prefix(cluster *v3.Cluster) string {
	return "/k8s/clusters/" + cluster.Name
}

func New(localConfig *rest.Config, cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if cluster.Spec.Internal {
		return NewLocal(localConfig, cluster)
	}
	return NewRemote(cluster, clusterLister, factory)
}

func NewLocal(localConfig *rest.Config, cluster *v3.Cluster) (*RemoteService, error) {
	// the gvk is ignored by us, so just pass in any gvk
	hostURL, _, err := rest.DefaultServerURL(localConfig.Host, localConfig.APIPath, schema.GroupVersion{}, true)
	if err != nil {
		return nil, err
	}

	transport, err := rest.TransportFor(localConfig)
	if err != nil {
		return nil, err
	}

	transportGetter := func() (http.RoundTripper, error) {
		return transport, nil
	}

	rs := &RemoteService{
		cluster: cluster,
		url: func() (url.URL, error) {
			return *hostURL, nil
		},
		transport: transportGetter,
	}
	if localConfig.BearerToken != "" {
		rs.auth = func() (string, error) { return "Bearer " + localConfig.BearerToken, nil }
	} else if localConfig.Password != "" {
		rs.auth = func() (string, error) {
			return "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", localConfig.Username, localConfig.Password))), nil
		}
	}

	return rs, nil
}

func NewRemote(cluster *v3.Cluster, clusterLister v3.ClusterLister, factory dialer.Factory) (*RemoteService, error) {
	if !v3.ClusterConditionProvisioned.IsTrue(cluster) {
		return nil, httperror.NewAPIError(httperror.ClusterUnavailable, "cluster not provisioned")
	}

	urlGetter := func() (url.URL, error) {
		newCluster, err := clusterLister.Get("", cluster.Name)
		if err != nil {
			return url.URL{}, err
		}

		u, err := url.Parse(newCluster.Status.APIEndpoint)
		if err != nil {
			return url.URL{}, err
		}
		return *u, nil
	}

	authGetter := func() (string, error) {
		newCluster, err := clusterLister.Get("", cluster.Name)
		if err != nil {
			return "", err
		}

		return "Bearer " + newCluster.Status.ServiceAccountToken, nil
	}

	return &RemoteService{
		cluster:       cluster,
		url:           urlGetter,
		auth:          authGetter,
		clusterLister: clusterLister,
		factory:       factory,
	}, nil
}

func (r *RemoteService) getTransport() (http.RoundTripper, error) {
	if r.transport != nil {
		return r.transport()
	}

	newCluster, err := r.clusterLister.Get("", r.cluster.Name)
	if err != nil {
		return nil, err
	}

	r.Lock()
	defer r.Unlock()

	if r.httpTransport != nil && !r.cacertChanged(newCluster) {
		return r.httpTransport, nil
	}

	transport := &http.Transport{}
	if newCluster.Status.CACert != "" {
		certBytes, err := base64.StdEncoding.DecodeString(newCluster.Status.CACert)
		if err != nil {
			return nil, err
		}
		certs := x509.NewCertPool()
		certs.AppendCertsFromPEM(certBytes)
		transport.TLSClientConfig = &tls.Config{
			RootCAs: certs,
		}
	}

	if r.factory != nil {
		d, err := r.factory.ClusterDialer(newCluster.Name)
		if err != nil {
			return nil, err
		}
		transport.Dial = d
	}

	r.caCert = newCluster.Status.CACert
	if r.httpTransport != nil {
		r.httpTransport.CloseIdleConnections()
	}
	r.httpTransport = transport

	return transport, nil
}

func (r *RemoteService) cacertChanged(cluster *v3.Cluster) bool {
	return r.caCert != cluster.Status.CACert
}

func (r *RemoteService) Close() {
	if r.httpTransport != nil {
		r.httpTransport.CloseIdleConnections()
	}
}

func (r *RemoteService) Handler() http.Handler {
	return r
}

func (r *RemoteService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u, err := r.url()
	if err != nil {
		er.Error(rw, req, err)
		return
	}

	u.Path = strings.TrimPrefix(req.URL.Path, prefix(r.cluster))
	u.RawQuery = req.URL.RawQuery

	proto := req.Header.Get("X-Forwarded-Proto")
	if proto != "" {
		req.URL.Scheme = proto
	} else if req.TLS == nil {
		req.URL.Scheme = "http"
	} else {
		req.URL.Scheme = "https"
	}

	req.URL.Host = req.Host
	if r.auth == nil {
		req.Header.Del("Authorization")
	} else {
		token, err := r.auth()
		if err != nil {
			er.Error(rw, req, err)
			return
		}
		req.Header.Set("Authorization", token)
	}
	transport, err := r.getTransport()
	if err != nil {
		er.Error(rw, req, err)
		return
	}
	httpProxy := proxy.NewUpgradeAwareHandler(&u, transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)
}

func (r *RemoteService) Cluster() *v3.Cluster {
	return r.cluster
}
<|endoftext|>


<commit_before>package graph

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestSort1(t *testing.T) {
	t.Parallel()
	graph := initGraph()

	// a -> b -> c
	require.NoError(t, graph.AddEdge("a", "b"))
	require.NoError(t, graph.AddEdge("b", "c"))

	assertSortResult(t, graph, []V{"c", "b", "a", "d"})
}

func TestSort2(t *testing.T) {
	t.Parallel()
	graph := initGraph()

	// a -> c
	// a -> b
	// b -> c
	require.NoError(t, graph.AddEdge("a", "c"))
	require.NoError(t, graph.AddEdge("a", "b"))
	require.NoError(t, graph.AddEdge("b", "c"))

	assertSortResult(t, graph, []V{"c", "b", "a", "d"})
}

func TestSort3(t *testing.T) {
	t.Parallel()
	graph := initGraph()

	// a -> b
	// a -> d
	// d -> c
	// c -> b
	require.NoError(t, graph.AddEdge("a", "b"))
	require.NoError(t, graph.AddEdge("a", "d"))
	require.NoError(t, graph.AddEdge("d", "c"))
	require.NoError(t, graph.AddEdge("c", "b"))

	assertSortResult(t, graph, []V{"b", "c", "d", "a"})
}

func TestSortCycleError1(t *testing.T) {
	t.Parallel()
	graph := initGraph()

	// a -> b
	// b -> a
	require.NoError(t, graph.AddEdge("a", "b"))
	require.NoError(t, graph.AddEdge("b", "a"))

	assertCycleDetection(t, graph)
}

func TestSortCycleError2(t *testing.T) {
	t.Parallel()
	graph := initGraph()

	// a -> b
	// b -> c
	// c -> a
	require.NoError(t, graph.AddEdge("a", "b"))
	require.NoError(t, graph.AddEdge("b", "c"))
	require.NoError(t, graph.AddEdge("c", "a"))

	assertCycleDetection(t, graph)
}

func TestSortCycleError3(t *testing.T) {
	t.Parallel()
	graph := initGraph()

	// a -> b
	// b -> c
	// c -> b
	require.NoError(t, graph.AddEdge("a", "b"))
	require.NoError(t, graph.AddEdge("b", "c"))
	require.NoError(t, graph.AddEdge("c", "b"))

	assertCycleDetection(t, graph)
}

func initGraph() *Graph {
	graph := NewGraph(4)
	graph.AddVertex("a", 1)
	graph.AddVertex("b", 2)
	graph.AddVertex("c", 3)
	graph.AddVertex("d", 4)
	return graph
}

func assertSortResult(t *testing.T, graph *Graph, expected []V) {
	result, err := graph.TopologicalSort()
	require.NoError(t, err)
	assert.Equal(t, expected, result)
}

func assertCycleDetection(t *testing.T, graph *Graph) {
	_, err := graph.TopologicalSort()
	require.Error(t, err)
}
<commit_msg>Rename variable<commit_after>package graph

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestSort1(t *testing.T) {
	t.Parallel()
	g := initGraph()

	// a -> b -> c
	require.NoError(t, g.AddEdge("a", "b"))
	require.NoError(t, g.AddEdge("b", "c"))

	assertSortResult(t, g, []V{"c", "b", "a", "d"})
}

func TestSort2(t *testing.T) {
	t.Parallel()
	g := initGraph()

	// a -> c
	// a -> b
	// b -> c
	require.NoError(t, g.AddEdge("a", "c"))
	require.NoError(t, g.AddEdge("a", "b"))
	require.NoError(t, g.AddEdge("b", "c"))

	assertSortResult(t, g, []V{"c", "b", "a", "d"})
}

func TestSort3(t *testing.T) {
	t.Parallel()
	g := initGraph()

	// a -> b
	// a -> d
	// d -> c
	// c -> b
	require.NoError(t, g.AddEdge("a", "b"))
	require.NoError(t, g.AddEdge("a", "d"))
	require.NoError(t, g.AddEdge("d", "c"))
	require.NoError(t, g.AddEdge("c", "b"))

	assertSortResult(t, g, []V{"b", "c", "d", "a"})
}

func TestSortCycleError1(t *testing.T) {
	t.Parallel()
	g := initGraph()

	// a -> b
	// b -> a
	require.NoError(t, g.AddEdge("a", "b"))
	require.NoError(t, g.AddEdge("b", "a"))

	assertCycleDetection(t, g)
}

func TestSortCycleError2(t *testing.T) {
	t.Parallel()
	g := initGraph()

	// a -> b
	// b -> c
	// c -> a
	require.NoError(t, g.AddEdge("a", "b"))
	require.NoError(t, g.AddEdge("b", "c"))
	require.NoError(t, g.AddEdge("c", "a"))

	assertCycleDetection(t, g)
}

func TestSortCycleError3(t *testing.T) {
	t.Parallel()
	g := initGraph()

	// a -> b
	// b -> c
	// c -> b
	require.NoError(t, g.AddEdge("a", "b"))
	require.NoError(t, g.AddEdge("b", "c"))
	require.NoError(t, g.AddEdge("c", "b"))

	assertCycleDetection(t, g)
}

func initGraph() *Graph {
	g := NewGraph(4)
	g.AddVertex("a", 1)
	g.AddVertex("b", 2)
	g.AddVertex("c", 3)
	g.AddVertex("d", 4)
	return g
}

func assertSortResult(t *testing.T, g *Graph, expected []V) {
	result, err := g.TopologicalSort()
	require.NoError(t, err)
	assert.Equal(t, expected, result)
}

func assertCycleDetection(t *testing.T, g *Graph) {
	_, err := g.TopologicalSort()
	require.Error(t, err)
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 The Jaeger Authors.
// Copyright (c) 2018 Uber Technologies, 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 integration

import (
	"os"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/uber/jaeger-lib/metrics"
	"go.uber.org/zap"

	"github.com/jaegertracing/jaeger/pkg/config"
	"github.com/jaegertracing/jaeger/pkg/testutils"
	"github.com/jaegertracing/jaeger/plugin/storage/grpc"
)

const defaultPluginBinaryPath = "../../../examples/memstore-plugin/memstore-plugin"

type GRPCStorageIntegrationTestSuite struct {
	StorageIntegration
	logger           *zap.Logger
	pluginBinaryPath string
}

func (s *GRPCStorageIntegrationTestSuite) initialize() error {
	s.logger, _ = testutils.NewLogger()

	f := grpc.NewFactory()
	v, command := config.Viperize(f.AddFlags)
	err := command.ParseFlags([]string{
		"--grpc-storage-plugin.binary",
		s.pluginBinaryPath,
		"--grpc-storage-plugin.log-level",
		"debug",
	})
	if err != nil {
		return err
	}
	f.InitFromViper(v, zap.NewNop())
	if err := f.Initialize(metrics.NullFactory, s.logger); err != nil {
		return err
	}

	if s.SpanWriter, err = f.CreateSpanWriter(); err != nil {
		return err
	}
	if s.SpanReader, err = f.CreateSpanReader(); err != nil {
		return err
	}

	// TODO DependencyWriter is not implemented in grpc store

	s.Refresh = s.refresh
	s.CleanUp = s.cleanUp
	return nil
}

func (s *GRPCStorageIntegrationTestSuite) refresh() error {
	return nil
}

func (s *GRPCStorageIntegrationTestSuite) cleanUp() error {
	return s.initialize()
}

func TestGRPCStorage(t *testing.T) {
	if os.Getenv("STORAGE") != "grpc-plugin" {
		t.Skip("Integration test against grpc skipped; set STORAGE env var to grpc-plugin to run this")
	}
	path := os.Getenv("PLUGIN_BINARY_PATH")
	if path == "" {
		t.Logf("PLUGIN_BINARY_PATH env var not set, using %s", defaultPluginBinaryPath)
		path = defaultPluginBinaryPath
	}
	s := &GRPCStorageIntegrationTestSuite{
		pluginBinaryPath: path,
	}
	require.NoError(t, s.initialize())
	s.IntegrationTestAll(t)
}
<commit_msg>Added ability to pass config file to grpc plugin in integration tests (#3253)<commit_after>// Copyright (c) 2019 The Jaeger Authors.
// Copyright (c) 2018 Uber Technologies, 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 integration

import (
	"os"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/uber/jaeger-lib/metrics"
	"go.uber.org/zap"

	"github.com/jaegertracing/jaeger/pkg/config"
	"github.com/jaegertracing/jaeger/pkg/testutils"
	"github.com/jaegertracing/jaeger/plugin/storage/grpc"
)

const defaultPluginBinaryPath = "../../../examples/memstore-plugin/memstore-plugin"

type GRPCStorageIntegrationTestSuite struct {
	StorageIntegration
	logger           *zap.Logger
	pluginBinaryPath string
	pluginConfigPath string
}

func (s *GRPCStorageIntegrationTestSuite) initialize() error {
	s.logger, _ = testutils.NewLogger()

	f := grpc.NewFactory()
	v, command := config.Viperize(f.AddFlags)
	flags := []string{
		"--grpc-storage-plugin.binary",
		s.pluginBinaryPath,
		"--grpc-storage-plugin.log-level",
		"debug",
	}
	if s.pluginConfigPath != "" {
		flags = append(flags,
			"--grpc-storage-plugin.configuration-file",
			s.pluginConfigPath,
		)
	}
	err := command.ParseFlags(flags)
	if err != nil {
		return err
	}
	f.InitFromViper(v, zap.NewNop())
	if err := f.Initialize(metrics.NullFactory, s.logger); err != nil {
		return err
	}

	if s.SpanWriter, err = f.CreateSpanWriter(); err != nil {
		return err
	}
	if s.SpanReader, err = f.CreateSpanReader(); err != nil {
		return err
	}

	// TODO DependencyWriter is not implemented in grpc store

	s.Refresh = s.refresh
	s.CleanUp = s.cleanUp
	return nil
}

func (s *GRPCStorageIntegrationTestSuite) refresh() error {
	return nil
}

func (s *GRPCStorageIntegrationTestSuite) cleanUp() error {
	return s.initialize()
}

func TestGRPCStorage(t *testing.T) {
	if os.Getenv("STORAGE") != "grpc-plugin" {
		t.Skip("Integration test against grpc skipped; set STORAGE env var to grpc-plugin to run this")
	}
	binaryPath := os.Getenv("PLUGIN_BINARY_PATH")
	if binaryPath == "" {
		t.Logf("PLUGIN_BINARY_PATH env var not set, using %s", defaultPluginBinaryPath)
		binaryPath = defaultPluginBinaryPath
	}
	configPath := os.Getenv("PLUGIN_CONFIG_PATH")
	if configPath == "" {
		t.Log("PLUGIN_CONFIG_PATH env var not set")
	}
	s := &GRPCStorageIntegrationTestSuite{
		pluginBinaryPath: binaryPath,
		pluginConfigPath: configPath,
	}
	require.NoError(t, s.initialize())
	s.IntegrationTestAll(t)
}
<|endoftext|>


<commit_before>package application

import (
	"go.uber.org/fx"

	"github.com/tidepool-org/platform/clinics"
	"github.com/tidepool-org/platform/devices"
	structuredMongo "github.com/tidepool-org/platform/store/structured/mongo"

	"github.com/tidepool-org/platform/auth/client"

	"github.com/tidepool-org/platform/prescription/api"
	"github.com/tidepool-org/platform/prescription/service"
	prescriptionMongo "github.com/tidepool-org/platform/prescription/store/mongo"
	"github.com/tidepool-org/platform/status"
)

var Prescription = fx.Options(
	client.ProvideServiceName("prescription"),
	client.ExternalClientModule,
	fx.Provide(
		prescriptionMongo.NewStore,
		prescriptionMongo.NewStatusReporter,
		service.NewDeviceSettingsValidator,
		service.NewService,
		fx.Annotated{
			Group:  "routers",
			Target: api.NewRouter,
		},
	),
	status.RouterModule,
)

var Dependencies = fx.Options(
	devices.ClientModule,
	structuredMongo.StoreModule,
	clinics.ClientModule,
	fx.Provide(mailer()),
)
<commit_msg>Fix incorrect fx provide function<commit_after>package application

import (
	"go.uber.org/fx"

	"github.com/tidepool-org/platform/clinics"
	"github.com/tidepool-org/platform/devices"
	structuredMongo "github.com/tidepool-org/platform/store/structured/mongo"

	"github.com/tidepool-org/platform/auth/client"

	"github.com/tidepool-org/platform/prescription/api"
	"github.com/tidepool-org/platform/prescription/service"
	prescriptionMongo "github.com/tidepool-org/platform/prescription/store/mongo"
	"github.com/tidepool-org/platform/status"
)

var Prescription = fx.Options(
	client.ProvideServiceName("prescription"),
	client.ExternalClientModule,
	fx.Provide(
		prescriptionMongo.NewStore,
		prescriptionMongo.NewStatusReporter,
		service.NewDeviceSettingsValidator,
		service.NewService,
		fx.Annotated{
			Group:  "routers",
			Target: api.NewRouter,
		},
	),
	status.RouterModule,
)

var Dependencies = fx.Options(
	devices.ClientModule,
	structuredMongo.StoreModule,
	clinics.ClientModule,
	fx.Provide(mailer),
)
<|endoftext|>


<commit_before>// +build integration
// To turn on this test use -tags=integration in go test command

package client

import (
	"encoding/hex"
	"github.com/jcmturner/gokrb5/config"
	"github.com/jcmturner/gokrb5/credentials"
	"github.com/jcmturner/gokrb5/iana/etypeID"
	"github.com/jcmturner/gokrb5/keytab"
	"github.com/jcmturner/gokrb5/testdata"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestClient_SuccessfulLogin(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_TCPOnly(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	c.LibDefaults.Udp_preference_limit = 1
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_OlderKDC(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_OLDERKDC)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_ETYPE_DES3_CBC_SHA1_KD(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	c.LibDefaults.Default_tkt_enctypes = []string{"des3-cbc-sha1-kd"}
	c.LibDefaults.Default_tkt_enctype_ids = []int{etypeID.DES3_CBC_SHA1_KD}
	c.LibDefaults.Default_tgs_enctypes = []string{"des3-cbc-sha1-kd"}
	c.LibDefaults.Default_tgs_enctype_ids = []int{etypeID.DES3_CBC_SHA1_KD}
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_ETYPE_AES128_CTS_HMAC_SHA256_128(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_LATESTKDC)
	c.LibDefaults.Default_tkt_enctypes = []string{"aes128-cts-hmac-sha256-128"}
	c.LibDefaults.Default_tkt_enctype_ids = []int{etypeID.AES128_CTS_HMAC_SHA256_128}
	c.LibDefaults.Default_tgs_enctypes = []string{"aes128-cts-hmac-sha256-128"}
	c.LibDefaults.Default_tgs_enctype_ids = []int{etypeID.AES128_CTS_HMAC_SHA256_128}
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_ETYPE_AES256_CTS_HMAC_SHA384_192(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_LATESTKDC)
	c.LibDefaults.Default_tkt_enctypes = []string{"aes256-cts-hmac-sha384-192"}
	c.LibDefaults.Default_tkt_enctype_ids = []int{etypeID.AES256_CTS_HMAC_SHA384_192}
	c.LibDefaults.Default_tgs_enctypes = []string{"aes256-cts-hmac-sha384-192"}
	c.LibDefaults.Default_tgs_enctype_ids = []int{etypeID.AES256_CTS_HMAC_SHA384_192}
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_AD(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_AD)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_FailedLogin(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_WRONGPASSWD)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err == nil {
		t.Fatal("Login with incorrect password did not error")
	}
}

func TestClient_SuccessfulLogin_UserRequiringPreAuth(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_UserRequiringPreAuth_TCPOnly(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	c.LibDefaults.Udp_preference_limit = 1
	cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_NetworkTimeout(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_BAD_KDC_ADDRESS)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err == nil {
		t.Fatal("Login with incorrect KDC address did not error")
	}
}

func TestClient_GetServiceTicket(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
	spn := "HTTP/host.test.gokrb5"
	tkt, key, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
	assert.Equal(t, 18, key.KeyType)

	//Check cache use - should get the same values back again
	tkt2, key2, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
	assert.Equal(t, key.KeyValue, key2.KeyValue)
}

func TestClient_GetServiceTicket_OlderKDC(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_OLDERKDC)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
	spn := "HTTP/host.test.gokrb5"
	tkt, key, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
	assert.Equal(t, 18, key.KeyType)
}

func TestClient_GetServiceTicket_AD(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_AD)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
	spn := "HTTP/host.test.gokrb5"
	tkt, key, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
	assert.Equal(t, 18, key.KeyType)
}

func TestClient_SetSPNEGOHeader(t *testing.T) {
	b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err := cl.Login()
	if err != nil {
		t.Fatalf("Error on AS_REQ: %v\n", err)
	}
	r, _ := http.NewRequest("GET", "http://10.80.88.90/index.html", nil)
	httpResp, err := http.DefaultClient.Do(r)
	if err != nil {
		t.Fatalf("Request error: %v\n", err)
	}
	assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
	err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
	if err != nil {
		t.Fatalf("Error setting client SPNEGO header: %v", err)
	}
	httpResp, err = http.DefaultClient.Do(r)
	if err != nil {
		t.Fatalf("Request error: %v\n", err)
	}
	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
}

func TestNewClientFromCCache(t *testing.T) {
	b, err := hex.DecodeString(testdata.CCACHE_TEST)
	if err != nil {
		t.Fatalf("Error decoding test data")
	}
	cc, err := credentials.ParseCCache(b)
	if err != nil {
		t.Fatal("Error getting test CCache")
	}
	cl, err := NewClientFromCCache(cc)
	if err != nil {
		t.Fatalf("Error creating client from CCache: %v", err)
	}
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl.WithConfig(c)
	assert.True(t, cl.IsConfigured(), "Client was not configured from CCache")
}
<commit_msg>intg test fix<commit_after>// +build integration
// To turn on this test use -tags=integration in go test command

package client

import (
	"encoding/hex"
	"github.com/jcmturner/gokrb5/config"
	"github.com/jcmturner/gokrb5/credentials"
	"github.com/jcmturner/gokrb5/iana/etypeID"
	"github.com/jcmturner/gokrb5/keytab"
	"github.com/jcmturner/gokrb5/testdata"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestClient_SuccessfulLogin(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_TCPOnly(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	c.LibDefaults.UDPPreferenceLimit = 1
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_OlderKDC(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_OLDERKDC)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_ETYPE_DES3_CBC_SHA1_KD(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	c.LibDefaults.DefaultTktEnctypes = []string{"des3-cbc-sha1-kd"}
	c.LibDefaults.DefaultTktEnctypeIDs = []int{etypeID.DES3_CBC_SHA1_KD}
	c.LibDefaults.DefaultTGSEnctypes = []string{"des3-cbc-sha1-kd"}
	c.LibDefaults.DefaultTGSEnctypeIDs = []int{etypeID.DES3_CBC_SHA1_KD}
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_ETYPE_AES128_CTS_HMAC_SHA256_128(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_LATESTKDC)
	c.LibDefaults.DefaultTktEnctypes = []string{"aes128-cts-hmac-sha256-128"}
	c.LibDefaults.DefaultTktEnctypeIDs = []int{etypeID.AES128_CTS_HMAC_SHA256_128}
	c.LibDefaults.DefaultTGSEnctypes = []string{"aes128-cts-hmac-sha256-128"}
	c.LibDefaults.DefaultTGSEnctypeIDs = []int{etypeID.AES128_CTS_HMAC_SHA256_128}
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_ETYPE_AES256_CTS_HMAC_SHA384_192(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_LATESTKDC)
	c.LibDefaults.DefaultTktEnctypes = []string{"aes256-cts-hmac-sha384-192"}
	c.LibDefaults.DefaultTktEnctypeIDs = []int{etypeID.AES256_CTS_HMAC_SHA384_192}
	c.LibDefaults.DefaultTGSEnctypes = []string{"aes256-cts-hmac-sha384-192"}
	c.LibDefaults.DefaultTGSEnctypeIDs = []int{etypeID.AES256_CTS_HMAC_SHA384_192}
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_AD(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_AD)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_FailedLogin(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_WRONGPASSWD)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err == nil {
		t.Fatal("Login with incorrect password did not error")
	}
}

func TestClient_SuccessfulLogin_UserRequiringPreAuth(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_SuccessfulLogin_UserRequiringPreAuth_TCPOnly(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER2_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	c.LibDefaults.UDPPreferenceLimit = 1
	cl := NewClientWithKeytab("testuser2", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
}

func TestClient_NetworkTimeout(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_BAD_KDC_ADDRESS)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err == nil {
		t.Fatal("Login with incorrect KDC address did not error")
	}
}

func TestClient_GetServiceTicket(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
	spn := "HTTP/host.test.gokrb5"
	tkt, key, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
	assert.Equal(t, 18, key.KeyType)

	//Check cache use - should get the same values back again
	tkt2, key2, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, tkt.EncPart.Cipher, tkt2.EncPart.Cipher)
	assert.Equal(t, key.KeyValue, key2.KeyValue)
}

func TestClient_GetServiceTicket_OlderKDC(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_OLDERKDC)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
	spn := "HTTP/host.test.gokrb5"
	tkt, key, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
	assert.Equal(t, 18, key.KeyType)
}

func TestClient_GetServiceTicket_AD(t *testing.T) {
	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_AD)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err = cl.Login()
	if err != nil {
		t.Fatalf("Error on login: %v\n", err)
	}
	spn := "HTTP/host.test.gokrb5"
	tkt, key, err := cl.GetServiceTicket(spn)
	if err != nil {
		t.Fatalf("Error getting service ticket: %v\n", err)
	}
	assert.Equal(t, spn, tkt.SName.GetPrincipalNameString())
	assert.Equal(t, 18, key.KeyType)
}

func TestClient_SetSPNEGOHeader(t *testing.T) {
	b, _ := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
	kt, _ := keytab.Parse(b)
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
	cl.WithConfig(c)

	err := cl.Login()
	if err != nil {
		t.Fatalf("Error on AS_REQ: %v\n", err)
	}
	r, _ := http.NewRequest("GET", "http://10.80.88.90/index.html", nil)
	httpResp, err := http.DefaultClient.Do(r)
	if err != nil {
		t.Fatalf("Request error: %v\n", err)
	}
	assert.Equal(t, http.StatusUnauthorized, httpResp.StatusCode, "Status code in response to client with no SPNEGO not as expected")
	err = cl.SetSPNEGOHeader(r, "HTTP/host.test.gokrb5")
	if err != nil {
		t.Fatalf("Error setting client SPNEGO header: %v", err)
	}
	httpResp, err = http.DefaultClient.Do(r)
	if err != nil {
		t.Fatalf("Request error: %v\n", err)
	}
	assert.Equal(t, http.StatusOK, httpResp.StatusCode, "Status code in response to client SPNEGO request not as expected")
}

func TestNewClientFromCCache(t *testing.T) {
	b, err := hex.DecodeString(testdata.CCACHE_TEST)
	if err != nil {
		t.Fatalf("Error decoding test data")
	}
	cc, err := credentials.ParseCCache(b)
	if err != nil {
		t.Fatal("Error getting test CCache")
	}
	cl, err := NewClientFromCCache(cc)
	if err != nil {
		t.Fatalf("Error creating client from CCache: %v", err)
	}
	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
	cl.WithConfig(c)
	assert.True(t, cl.IsConfigured(), "Client was not configured from CCache")
}
<|endoftext|>