<commit_before>package builder

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"time"

	"github.com/golang/glog"

	s2iapi "github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/api/describe"
	"github.com/openshift/source-to-image/pkg/api/validation"
	s2ibuild "github.com/openshift/source-to-image/pkg/build"
	s2i "github.com/openshift/source-to-image/pkg/build/strategies"

	"github.com/openshift/origin/pkg/build/api"
	"github.com/openshift/origin/pkg/build/builder/cmd/dockercfg"
	"github.com/openshift/origin/pkg/build/controller/strategy"
	"github.com/openshift/origin/pkg/client"
)

// builderFactory is the internal interface to decouple S2I-specific code from Origin builder code
type builderFactory interface {
	// Create S2I Builder based on S2I configuration
	Builder(config *s2iapi.Config, overrides s2ibuild.Overrides) (s2ibuild.Builder, error)
}

// validator is the interval interface to decouple S2I-specific code from Origin builder code
type validator interface {
	// Perform validation of S2I configuration, returns slice of validation errors
	ValidateConfig(config *s2iapi.Config) []validation.ValidationError
}

// runtimeBuilderFactory is the default implementation of stiBuilderFactory
type runtimeBuilderFactory struct{}

// Builder delegates execution to S2I-specific code
func (_ runtimeBuilderFactory) Builder(config *s2iapi.Config, overrides s2ibuild.Overrides) (s2ibuild.Builder, error) {
	return s2i.Strategy(config, overrides)
}

// runtimeConfigValidator is the default implementation of stiConfigValidator
type runtimeConfigValidator struct{}

// ValidateConfig delegates execution to S2I-specific code
func (_ runtimeConfigValidator) ValidateConfig(config *s2iapi.Config) []validation.ValidationError {
	return validation.ValidateConfig(config)
}

// S2IBuilder performs an STI build given the build object
type S2IBuilder struct {
	builder   builderFactory
	validator validator
	gitClient GitClient

	dockerClient DockerClient
	dockerSocket string
	build        *api.Build
	client       client.BuildInterface
	cgLimits     *s2iapi.CGroupLimits
}

// NewS2IBuilder creates a new STIBuilder instance
func NewS2IBuilder(dockerClient DockerClient, dockerSocket string, buildsClient client.BuildInterface, build *api.Build, gitClient GitClient, cgLimits *s2iapi.CGroupLimits) *S2IBuilder {
	// delegate to internal implementation passing default implementation of builderFactory and validator
	return newS2IBuilder(dockerClient, dockerSocket, buildsClient, build, gitClient, runtimeBuilderFactory{}, runtimeConfigValidator{}, cgLimits)

}

// newS2IBuilder is the internal factory function to create STIBuilder based on parameters. Used for testing.
func newS2IBuilder(dockerClient DockerClient, dockerSocket string, buildsClient client.BuildInterface, build *api.Build,
	gitClient GitClient, builder builderFactory, validator validator, cgLimits *s2iapi.CGroupLimits) *S2IBuilder {
	// just create instance
	return &S2IBuilder{
		builder:      builder,
		validator:    validator,
		gitClient:    gitClient,
		dockerClient: dockerClient,
		dockerSocket: dockerSocket,
		build:        build,
		client:       buildsClient,
		cgLimits:     cgLimits,
	}
}

// Build executes STI build based on configured builder, S2I builder factory and S2I config validator
func (s *S2IBuilder) Build() error {
	if s.build.Spec.Strategy.SourceStrategy == nil {
		return fmt.Errorf("the source to image builder must be used with the source strategy")
	}

	var push bool

	contextDir := filepath.Clean(s.build.Spec.Source.ContextDir)
	if contextDir == "." || contextDir == "/" {
		contextDir = ""
	}
	buildDir, err := ioutil.TempDir("", "s2i-build")
	if err != nil {
		return err
	}
	srcDir := filepath.Join(buildDir, s2iapi.Source)
	if err := os.MkdirAll(srcDir, os.ModePerm); err != nil {
		return err
	}
	tmpDir := filepath.Join(buildDir, "tmp")
	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
		return err
	}

	download := &downloader{
		s:       s,
		in:      os.Stdin,
		timeout: urlCheckTimeout,

		dir:        srcDir,
		contextDir: contextDir,
		tmpDir:     tmpDir,
	}
	// if there is no output target, set one up so the docker build logic
	// (which requires a tag) will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Status.OutputDockerImageReference = s.build.Name
	} else {
		push = true
	}
	pushTag := s.build.Status.OutputDockerImageReference
	git := s.build.Spec.Source.Git

	var ref string
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		len(s.build.Spec.Revision.Git.Commit) != 0 {
		ref = s.build.Spec.Revision.Git.Commit
	} else if git != nil && len(git.Ref) != 0 {
		ref = git.Ref
	}

	sourceURI := &url.URL{
		Scheme:   "file",
		Path:     srcDir,
		Fragment: ref,
	}

	injections := s2iapi.InjectionList{}
	for _, s := range s.build.Spec.Source.Secrets {
		glog.V(3).Infof("Injecting secret %q into a build into %q", s.Secret.Name, filepath.Clean(s.DestinationDir))
		secretSourcePath := filepath.Join(strategy.SecretBuildSourceBaseMountPath, s.Secret.Name)
		injections = append(injections, s2iapi.InjectPath{
			SourcePath:     secretSourcePath,
			DestinationDir: s.DestinationDir,
		})
	}

	buildTag := randomBuildTag(s.build.Namespace, s.build.Name)

	config := &s2iapi.Config{
		WorkingDir:     buildDir,
		DockerConfig:   &s2iapi.DockerConfig{Endpoint: s.dockerSocket},
		DockerCfgPath:  os.Getenv(dockercfg.PullAuthType),
		LabelNamespace: api.DefaultDockerLabelNamespace,

		ScriptsURL: s.build.Spec.Strategy.SourceStrategy.Scripts,

		BuilderImage: s.build.Spec.Strategy.SourceStrategy.From.Name,
		Incremental:  s.build.Spec.Strategy.SourceStrategy.Incremental,

		Environment:       buildEnvVars(s.build),
		DockerNetworkMode: getDockerNetworkMode(),

		Source:       sourceURI.String(),
		Tag:          buildTag,
		ContextDir:   s.build.Spec.Source.ContextDir,
		CGroupLimits: s.cgLimits,
		Injections:   injections,
	}

	if s.build.Spec.Strategy.SourceStrategy.ForcePull {
		glog.V(4).Infof("With force pull true, setting policies to %s", s2iapi.PullAlways)
		config.BuilderPullPolicy = s2iapi.PullAlways
	} else {
		glog.V(4).Infof("With force pull false, setting policies to %s", s2iapi.PullIfNotPresent)
		config.BuilderPullPolicy = s2iapi.PullIfNotPresent
	}
	config.PreviousImagePullPolicy = s2iapi.PullAlways

	allowedUIDs := os.Getenv("ALLOWED_UIDS")
	glog.V(2).Infof("The value of ALLOWED_UIDS is [%s]", allowedUIDs)
	if len(allowedUIDs) > 0 {
		err := config.AllowedUIDs.Set(allowedUIDs)
		if err != nil {
			return err
		}
	}

	if errs := s.validator.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	config.PullAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType)
	config.IncrementalAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(pushTag, dockercfg.PushAuthType)

	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := s.builder.Builder(config, s2ibuild.Overrides{Downloader: download})
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	if _, err = builder.Build(config); err != nil {
		return err
	}

	cname := containerName("s2i", s.build.Name, s.build.Namespace, "post-commit")
	if err := execPostCommitHook(s.dockerClient, s.build.Spec.PostCommit, buildTag, cname); err != nil {
		return err
	}

	if push {
		if err := tagImage(s.dockerClient, buildTag, pushTag); err != nil {
			return err
		}
	}

	if err := removeImage(s.dockerClient, buildTag); err != nil {
		glog.Warningf("Failed to remove temporary build tag %v: %v", buildTag, err)
	}

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			pushTag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", pushTag)
		} else {
			glog.Infof("No push secret provided")
		}
		glog.Infof("Pushing %s image ...", pushTag)
		if err := pushImage(s.dockerClient, pushTag, pushAuthConfig); err != nil {
			// write extended error message to assist in problem resolution
			msg := fmt.Sprintf("Failed to push image. Response from registry is: %v", err)
			if authPresent {
				glog.Infof("Registry server Address: %s", pushAuthConfig.ServerAddress)
				glog.Infof("Registry server User Name: %s", pushAuthConfig.Username)
				glog.Infof("Registry server Email: %s", pushAuthConfig.Email)
				passwordPresent := "<<empty>>"
				if len(pushAuthConfig.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.Infof("Registry server Password: %s", passwordPresent)
			}
			return errors.New(msg)
		}
		glog.Infof("Successfully pushed %s", pushTag)
		glog.Flush()
	}
	return nil
}

type downloader struct {
	s       *S2IBuilder
	in      io.Reader
	timeout time.Duration

	dir        string
	contextDir string
	tmpDir     string
}

func (d *downloader) Download(config *s2iapi.Config) (*s2iapi.SourceInfo, error) {
	var targetDir string
	if len(d.contextDir) > 0 {
		targetDir = d.tmpDir
	} else {
		targetDir = d.dir
	}

	// fetch source
	sourceInfo, err := fetchSource(d.s.dockerClient, targetDir, d.s.build, d.timeout, d.in, d.s.gitClient)
	if err != nil {
		return nil, err
	}
	if sourceInfo != nil {
		updateBuildRevision(d.s.client, d.s.build, sourceInfo)
	}
	if sourceInfo != nil {
		sourceInfo.ContextDir = config.ContextDir
	}

	// if a context dir is provided, move the context dir contents into the src location
	if len(d.contextDir) > 0 {
		srcDir := filepath.Join(targetDir, d.contextDir)
		if err := os.Remove(d.dir); err != nil {
			return nil, err
		}
		if err := os.Rename(srcDir, d.dir); err != nil {
			return nil, err
		}
	}
	if sourceInfo != nil {
		return &sourceInfo.SourceInfo, nil
	}
	return nil, nil
}

// buildEnvVars returns a map with build metadata to be inserted into Docker
// images produced by build. It transforms the output from buildInfo into the
// input format expected by s2iapi.Config.Environment.
// Note that using a map has at least two downsides:
// 1. The order of metadata KeyValue pairs is lost;
// 2. In case of repeated Keys, the last Value takes precedence right here,
//    instead of deferring what to do with repeated environment variables to the
//    Docker runtime.
func buildEnvVars(build *api.Build) map[string]string {
	bi := buildInfo(build)
	envVars := make(map[string]string, len(bi))
	for _, item := range bi {
		envVars[item.Key] = item.Value
	}
	return envVars
}
<commit_msg>Set source of incremental build artifacts<commit_after>package builder

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"time"

	"github.com/golang/glog"

	s2iapi "github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/api/describe"
	"github.com/openshift/source-to-image/pkg/api/validation"
	s2ibuild "github.com/openshift/source-to-image/pkg/build"
	s2i "github.com/openshift/source-to-image/pkg/build/strategies"

	"github.com/openshift/origin/pkg/build/api"
	"github.com/openshift/origin/pkg/build/builder/cmd/dockercfg"
	"github.com/openshift/origin/pkg/build/controller/strategy"
	"github.com/openshift/origin/pkg/client"
)

// builderFactory is the internal interface to decouple S2I-specific code from Origin builder code
type builderFactory interface {
	// Create S2I Builder based on S2I configuration
	Builder(config *s2iapi.Config, overrides s2ibuild.Overrides) (s2ibuild.Builder, error)
}

// validator is the interval interface to decouple S2I-specific code from Origin builder code
type validator interface {
	// Perform validation of S2I configuration, returns slice of validation errors
	ValidateConfig(config *s2iapi.Config) []validation.ValidationError
}

// runtimeBuilderFactory is the default implementation of stiBuilderFactory
type runtimeBuilderFactory struct{}

// Builder delegates execution to S2I-specific code
func (_ runtimeBuilderFactory) Builder(config *s2iapi.Config, overrides s2ibuild.Overrides) (s2ibuild.Builder, error) {
	return s2i.Strategy(config, overrides)
}

// runtimeConfigValidator is the default implementation of stiConfigValidator
type runtimeConfigValidator struct{}

// ValidateConfig delegates execution to S2I-specific code
func (_ runtimeConfigValidator) ValidateConfig(config *s2iapi.Config) []validation.ValidationError {
	return validation.ValidateConfig(config)
}

// S2IBuilder performs an STI build given the build object
type S2IBuilder struct {
	builder   builderFactory
	validator validator
	gitClient GitClient

	dockerClient DockerClient
	dockerSocket string
	build        *api.Build
	client       client.BuildInterface
	cgLimits     *s2iapi.CGroupLimits
}

// NewS2IBuilder creates a new STIBuilder instance
func NewS2IBuilder(dockerClient DockerClient, dockerSocket string, buildsClient client.BuildInterface, build *api.Build, gitClient GitClient, cgLimits *s2iapi.CGroupLimits) *S2IBuilder {
	// delegate to internal implementation passing default implementation of builderFactory and validator
	return newS2IBuilder(dockerClient, dockerSocket, buildsClient, build, gitClient, runtimeBuilderFactory{}, runtimeConfigValidator{}, cgLimits)

}

// newS2IBuilder is the internal factory function to create STIBuilder based on parameters. Used for testing.
func newS2IBuilder(dockerClient DockerClient, dockerSocket string, buildsClient client.BuildInterface, build *api.Build,
	gitClient GitClient, builder builderFactory, validator validator, cgLimits *s2iapi.CGroupLimits) *S2IBuilder {
	// just create instance
	return &S2IBuilder{
		builder:      builder,
		validator:    validator,
		gitClient:    gitClient,
		dockerClient: dockerClient,
		dockerSocket: dockerSocket,
		build:        build,
		client:       buildsClient,
		cgLimits:     cgLimits,
	}
}

// Build executes STI build based on configured builder, S2I builder factory and S2I config validator
func (s *S2IBuilder) Build() error {
	if s.build.Spec.Strategy.SourceStrategy == nil {
		return fmt.Errorf("the source to image builder must be used with the source strategy")
	}

	var push bool

	contextDir := filepath.Clean(s.build.Spec.Source.ContextDir)
	if contextDir == "." || contextDir == "/" {
		contextDir = ""
	}
	buildDir, err := ioutil.TempDir("", "s2i-build")
	if err != nil {
		return err
	}
	srcDir := filepath.Join(buildDir, s2iapi.Source)
	if err := os.MkdirAll(srcDir, os.ModePerm); err != nil {
		return err
	}
	tmpDir := filepath.Join(buildDir, "tmp")
	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
		return err
	}

	download := &downloader{
		s:       s,
		in:      os.Stdin,
		timeout: urlCheckTimeout,

		dir:        srcDir,
		contextDir: contextDir,
		tmpDir:     tmpDir,
	}
	// if there is no output target, set one up so the docker build logic
	// (which requires a tag) will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Status.OutputDockerImageReference = s.build.Name
	} else {
		push = true
	}
	pushTag := s.build.Status.OutputDockerImageReference
	git := s.build.Spec.Source.Git

	var ref string
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		len(s.build.Spec.Revision.Git.Commit) != 0 {
		ref = s.build.Spec.Revision.Git.Commit
	} else if git != nil && len(git.Ref) != 0 {
		ref = git.Ref
	}

	sourceURI := &url.URL{
		Scheme:   "file",
		Path:     srcDir,
		Fragment: ref,
	}

	injections := s2iapi.InjectionList{}
	for _, s := range s.build.Spec.Source.Secrets {
		glog.V(3).Infof("Injecting secret %q into a build into %q", s.Secret.Name, filepath.Clean(s.DestinationDir))
		secretSourcePath := filepath.Join(strategy.SecretBuildSourceBaseMountPath, s.Secret.Name)
		injections = append(injections, s2iapi.InjectPath{
			SourcePath:     secretSourcePath,
			DestinationDir: s.DestinationDir,
		})
	}

	buildTag := randomBuildTag(s.build.Namespace, s.build.Name)

	config := &s2iapi.Config{
		WorkingDir:     buildDir,
		DockerConfig:   &s2iapi.DockerConfig{Endpoint: s.dockerSocket},
		DockerCfgPath:  os.Getenv(dockercfg.PullAuthType),
		LabelNamespace: api.DefaultDockerLabelNamespace,

		ScriptsURL: s.build.Spec.Strategy.SourceStrategy.Scripts,

		BuilderImage:       s.build.Spec.Strategy.SourceStrategy.From.Name,
		Incremental:        s.build.Spec.Strategy.SourceStrategy.Incremental,
		IncrementalFromTag: pushTag,

		Environment:       buildEnvVars(s.build),
		DockerNetworkMode: getDockerNetworkMode(),

		Source:       sourceURI.String(),
		Tag:          buildTag,
		ContextDir:   s.build.Spec.Source.ContextDir,
		CGroupLimits: s.cgLimits,
		Injections:   injections,
	}

	if s.build.Spec.Strategy.SourceStrategy.ForcePull {
		glog.V(4).Infof("With force pull true, setting policies to %s", s2iapi.PullAlways)
		config.BuilderPullPolicy = s2iapi.PullAlways
	} else {
		glog.V(4).Infof("With force pull false, setting policies to %s", s2iapi.PullIfNotPresent)
		config.BuilderPullPolicy = s2iapi.PullIfNotPresent
	}
	config.PreviousImagePullPolicy = s2iapi.PullAlways

	allowedUIDs := os.Getenv("ALLOWED_UIDS")
	glog.V(2).Infof("The value of ALLOWED_UIDS is [%s]", allowedUIDs)
	if len(allowedUIDs) > 0 {
		err := config.AllowedUIDs.Set(allowedUIDs)
		if err != nil {
			return err
		}
	}

	if errs := s.validator.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	config.PullAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType)
	config.IncrementalAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(pushTag, dockercfg.PushAuthType)

	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := s.builder.Builder(config, s2ibuild.Overrides{Downloader: download})
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	if _, err = builder.Build(config); err != nil {
		return err
	}

	cname := containerName("s2i", s.build.Name, s.build.Namespace, "post-commit")
	if err := execPostCommitHook(s.dockerClient, s.build.Spec.PostCommit, buildTag, cname); err != nil {
		return err
	}

	if push {
		if err := tagImage(s.dockerClient, buildTag, pushTag); err != nil {
			return err
		}
	}

	if err := removeImage(s.dockerClient, buildTag); err != nil {
		glog.Warningf("Failed to remove temporary build tag %v: %v", buildTag, err)
	}

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			pushTag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", pushTag)
		} else {
			glog.Infof("No push secret provided")
		}
		glog.Infof("Pushing %s image ...", pushTag)
		if err := pushImage(s.dockerClient, pushTag, pushAuthConfig); err != nil {
			// write extended error message to assist in problem resolution
			msg := fmt.Sprintf("Failed to push image. Response from registry is: %v", err)
			if authPresent {
				glog.Infof("Registry server Address: %s", pushAuthConfig.ServerAddress)
				glog.Infof("Registry server User Name: %s", pushAuthConfig.Username)
				glog.Infof("Registry server Email: %s", pushAuthConfig.Email)
				passwordPresent := "<<empty>>"
				if len(pushAuthConfig.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.Infof("Registry server Password: %s", passwordPresent)
			}
			return errors.New(msg)
		}
		glog.Infof("Successfully pushed %s", pushTag)
		glog.Flush()
	}
	return nil
}

type downloader struct {
	s       *S2IBuilder
	in      io.Reader
	timeout time.Duration

	dir        string
	contextDir string
	tmpDir     string
}

func (d *downloader) Download(config *s2iapi.Config) (*s2iapi.SourceInfo, error) {
	var targetDir string
	if len(d.contextDir) > 0 {
		targetDir = d.tmpDir
	} else {
		targetDir = d.dir
	}

	// fetch source
	sourceInfo, err := fetchSource(d.s.dockerClient, targetDir, d.s.build, d.timeout, d.in, d.s.gitClient)
	if err != nil {
		return nil, err
	}
	if sourceInfo != nil {
		updateBuildRevision(d.s.client, d.s.build, sourceInfo)
	}
	if sourceInfo != nil {
		sourceInfo.ContextDir = config.ContextDir
	}

	// if a context dir is provided, move the context dir contents into the src location
	if len(d.contextDir) > 0 {
		srcDir := filepath.Join(targetDir, d.contextDir)
		if err := os.Remove(d.dir); err != nil {
			return nil, err
		}
		if err := os.Rename(srcDir, d.dir); err != nil {
			return nil, err
		}
	}
	if sourceInfo != nil {
		return &sourceInfo.SourceInfo, nil
	}
	return nil, nil
}

// buildEnvVars returns a map with build metadata to be inserted into Docker
// images produced by build. It transforms the output from buildInfo into the
// input format expected by s2iapi.Config.Environment.
// Note that using a map has at least two downsides:
// 1. The order of metadata KeyValue pairs is lost;
// 2. In case of repeated Keys, the last Value takes precedence right here,
//    instead of deferring what to do with repeated environment variables to the
//    Docker runtime.
func buildEnvVars(build *api.Build) map[string]string {
	bi := buildInfo(build)
	envVars := make(map[string]string, len(bi))
	for _, item := range bi {
		envVars[item.Key] = item.Value
	}
	return envVars
}
<|endoftext|>


<commit_before>package chClient

import (
	"git.containerum.net/ch/kube-client/pkg/cherry"
	"git.containerum.net/ch/kube-client/pkg/cherry/auth"
	"git.containerum.net/ch/kube-client/pkg/cherry/kube-api"
	"git.containerum.net/ch/kube-client/pkg/cherry/resource-service"
	"git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/model/deployment"
	"github.com/sirupsen/logrus"
)

func (client *Client) GetDeployment(namespace, deplName string) (deployment.Deployment, error) {
	var depl deployment.Deployment
	err := retry(4, func() (bool, error) {
		kubeDeployment, err := client.kubeAPIClient.GetDeployment(namespace, deplName)
		switch {
		case err == nil:
			depl = deployment.DeploymentFromKube(kubeDeployment)
			return false, nil
		case cherry.In(err,
			kubeErrors.ErrResourceNotExist(),
			kubeErrors.ErrAccessError(),
			kubeErrors.ErrUnableGetResource()):
			return false, ErrResourceNotExists
		case cherry.In(err, autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			return true, client.Auth()
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
	return depl, err
}

func (client *Client) GetDeploymentList(namespace string) (deployment.DeploymentList, error) {
	var list deployment.DeploymentList
	err := retry(4, func() (bool, error) {
		kubeList, err := client.kubeAPIClient.GetDeploymentList(namespace)
		switch {
		case err == nil:
			list = deployment.DeploymentListFromKube(kubeList)
			return false, nil
		case cherry.In(err,
			kubeErrors.ErrResourceNotExist(),
			kubeErrors.ErrAccessError(),
			kubeErrors.ErrUnableGetResource()):
			return false, ErrNamespaceNotExists
		case cherry.In(err, autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			return true, client.Auth()
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
	return list, err
}

func (client *Client) DeleteDeployment(namespace, deplName string) error {
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.DeleteDeployment(namespace, deplName)
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			return false, ErrResourceNotExists.Wrap(err)
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists()):
			return false, ErrYouDoNotHaveAccessToResource
		case cherry.In(err, autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			return true, client.Auth()
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}

func (client *Client) CreateDeployment(ns string, depl deployment.Deployment) error {
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.CreateDeployment(ns, depl.ToKube())
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			logrus.WithError(ErrResourceNotExists.Wrap(err)).
				Debugf("error while creating service %q", depl.Name)
			return false, ErrResourceNotExists.CommentF("namespace %q doesn't exist", ns)
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists(),
			rserrors.ErrPermissionDenied()):
			logrus.WithError(ErrYouDoNotHaveAccessToResource.Wrap(err)).
				Debugf("error while creating service %q", depl.Name)
			return false, ErrYouDoNotHaveAccessToResource.
				CommentF("you don't have create access to namespace %q", ns)
		case cherry.In(err,
			autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			err = client.Auth()
			if err != nil {
				logrus.WithError(err).
					Debugf("error while creating service %q", depl.Name)
			}
			return true, err
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}

func (client *Client) SetContainerImage(ns, depl string, image model.UpdateImage) error {
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.SetContainerImage(ns, depl, image)
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			logrus.WithError(ErrResourceNotExists.Wrap(err)).
				Errorf("unable to set image")
			return false, err
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists(),
			rserrors.ErrPermissionDenied()):
			logrus.WithError(ErrYouDoNotHaveAccessToResource.Wrap(err)).
				Errorf("unable to set container image image")
			return false, ErrYouDoNotHaveAccessToResource.
				CommentF("you don't have create access to namespace %q", ns)
		case cherry.In(err,
			autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			err = client.Auth()
			if err != nil {
				logrus.WithError(err).
					Errorf("unable to set container image")
			}
			return true, err
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}
<commit_msg>add method ReplaceDeployment<commit_after>package chClient

import (
	"git.containerum.net/ch/kube-client/pkg/cherry"
	"git.containerum.net/ch/kube-client/pkg/cherry/auth"
	"git.containerum.net/ch/kube-client/pkg/cherry/kube-api"
	"git.containerum.net/ch/kube-client/pkg/cherry/resource-service"
	"git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/model/deployment"
	"github.com/sirupsen/logrus"
)

func (client *Client) GetDeployment(namespace, deplName string) (deployment.Deployment, error) {
	var depl deployment.Deployment
	err := retry(4, func() (bool, error) {
		kubeDeployment, err := client.kubeAPIClient.GetDeployment(namespace, deplName)
		switch {
		case err == nil:
			depl = deployment.DeploymentFromKube(kubeDeployment)
			return false, nil
		case cherry.In(err,
			kubeErrors.ErrResourceNotExist(),
			kubeErrors.ErrAccessError(),
			kubeErrors.ErrUnableGetResource()):
			return false, ErrResourceNotExists
		case cherry.In(err, autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			return true, client.Auth()
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
	return depl, err
}

func (client *Client) GetDeploymentList(namespace string) (deployment.DeploymentList, error) {
	var list deployment.DeploymentList
	err := retry(4, func() (bool, error) {
		kubeList, err := client.kubeAPIClient.GetDeploymentList(namespace)
		switch {
		case err == nil:
			list = deployment.DeploymentListFromKube(kubeList)
			return false, nil
		case cherry.In(err,
			kubeErrors.ErrResourceNotExist(),
			kubeErrors.ErrAccessError(),
			kubeErrors.ErrUnableGetResource()):
			return false, ErrNamespaceNotExists
		case cherry.In(err, autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			return true, client.Auth()
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
	return list, err
}

func (client *Client) DeleteDeployment(namespace, deplName string) error {
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.DeleteDeployment(namespace, deplName)
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			return false, ErrResourceNotExists.Wrap(err)
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists()):
			return false, ErrYouDoNotHaveAccessToResource
		case cherry.In(err, autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			return true, client.Auth()
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}

func (client *Client) CreateDeployment(ns string, depl deployment.Deployment) error {
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.CreateDeployment(ns, depl.ToKube())
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			logrus.WithError(ErrResourceNotExists.Wrap(err)).
				Debugf("error while creating service %q", depl.Name)
			return false, ErrResourceNotExists.CommentF("namespace %q doesn't exist", ns)
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists(),
			rserrors.ErrPermissionDenied()):
			logrus.WithError(ErrYouDoNotHaveAccessToResource.Wrap(err)).
				Debugf("error while creating service %q", depl.Name)
			return false, ErrYouDoNotHaveAccessToResource.
				CommentF("you don't have create access to namespace %q", ns)
		case cherry.In(err,
			autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			err = client.Auth()
			if err != nil {
				logrus.WithError(err).
					Debugf("error while creating deployment %q", depl.Name)
			}
			return true, err
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}

func (client *Client) SetContainerImage(ns, depl string, image model.UpdateImage) error {
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.SetContainerImage(ns, depl, image)
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			logrus.WithError(ErrResourceNotExists.Wrap(err)).
				Errorf("unable to set image")
			return false, err
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists(),
			rserrors.ErrPermissionDenied()):
			logrus.WithError(ErrYouDoNotHaveAccessToResource.Wrap(err)).
				Errorf("unable to set container image image")
			return false, ErrYouDoNotHaveAccessToResource.
				CommentF("you don't have create access to namespace %q", ns)
		case cherry.In(err,
			autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			err = client.Auth()
			if err != nil {
				logrus.WithError(err).
					Errorf("unable to set container image")
			}
			return true, err
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}

func (client *Client) ReplaceDeployment(ns, oldDepl string, newDepl deployment.Deployment) error {
	newDepl.Name = oldDepl
	return retry(4, func() (bool, error) {
		err := client.kubeAPIClient.ReplaceDeployment(ns, newDepl.ToKube())
		switch {
		case err == nil:
			return false, nil
		case cherry.In(err,
			rserrors.ErrResourceNotExists()):
			logrus.WithError(ErrResourceNotExists.Wrap(err)).
				Debugf("error while creating service %q", newDepl.Name)
			return false, ErrResourceNotExists.Wrap(err)
		case cherry.In(err,
			rserrors.ErrResourceNotOwned(),
			rserrors.ErrAccessRecordNotExists(),
			rserrors.ErrPermissionDenied()):
			logrus.WithError(ErrYouDoNotHaveAccessToResource.Wrap(err)).
				Debugf("error while creating deployment %q", newDepl.Name)
			return false, ErrYouDoNotHaveAccessToResource.
				CommentF("you don't have create access to namespace %q", ns)
		case cherry.In(err,
			autherr.ErrInvalidToken(),
			autherr.ErrTokenNotFound()):
			err = client.Auth()
			if err != nil {
				logrus.WithError(err).
					Debugf("error while creating service %q", newDepl.Name)
			}
			return true, err
		default:
			return true, ErrFatalError.Wrap(err)
		}
	})
}
<|endoftext|>


<commit_before>package discovery

import (
	"fmt"
	"strings"

	pb_config "github.com/improbable-eng/kedge/protogen/kedge/config"
	"github.com/pkg/errors"
)

type route struct {
	// Host (HTTP) or Authority (gRPC).
	nameMatcher string
	// If 0 it means just name matcher only.
	portMatcher uint32
}

type serviceKey struct {
	name, namespace string
}

type backendName struct {
	service    serviceKey
	targetPort string
}

func (b backendName) String() string {
	backendName := fmt.Sprintf("%s_%s_%v", b.service.name, b.service.namespace, b.targetPort)
	// BackendName needs to conform regex: "^[a-z_0-9.]{2,64}$"
	return strings.Replace(backendName, "-", "_", -1)
}

type serviceRoutings struct {
	// There could be more than one director routing per backend.
	http map[backendName][]route
	grpc map[backendName][]route
}

type serviceBackends struct {
	httpDomainPorts map[backendName]string
	grpcDomainPorts map[backendName]string

	tlsConfigs map[backendName]struct{}
}

type serviceConf struct {
	routings serviceRoutings
	backends serviceBackends
}

type updater struct {
	baseDirectorConfig    *pb_config.DirectorConfig
	baseBackendConfig     *pb_config.BackendPoolConfig
	externalDomainSuffix  string
	labelAnnotationPrefix string

	lastSeenServices map[serviceKey]serviceConf
}

func newUpdater(
	baseDirector *pb_config.DirectorConfig,
	baseBackendpool *pb_config.BackendPoolConfig,
	externalDomainSuffix string,
	labelAnnotationPrefix string,
) *updater {
	return &updater{
		baseDirectorConfig:    baseDirector,
		baseBackendConfig:     baseBackendpool,
		externalDomainSuffix:  externalDomainSuffix,
		labelAnnotationPrefix: labelAnnotationPrefix,
		lastSeenServices:      make(map[serviceKey]serviceConf),
	}
}

func (u *updater) onEvent(e event) (*pb_config.DirectorConfig, *pb_config.BackendPoolConfig, error) {
	serviceObj := e.Object
	service := serviceKey{serviceObj.Metadata.Name, serviceObj.Metadata.Namespace}

	var err error
	switch e.Type {
	case deleted:
		err = u.onDeletedEvent(service)
	case added, modified:
		err = u.onModifiedOrAddedEvent(serviceObj, service, e.Type)
	default:
		err = errors.Errorf("Got not supported event type %s", e.Type)
	}
	if err != nil {
		return nil, nil, err
	}

	return u.lastSeenServicesToConfigs()
}

// onDeletedEvent creates removes deleted serviceConfig from lastSeenServices map.
func (u *updater) onDeletedEvent(service serviceKey) error {
	_, ok := u.lastSeenServices[service]
	if !ok {
		return errors.Errorf("Got %s event for item %v that we are seeing for the first time", deleted, service)
	}

	delete(u.lastSeenServices, service)
	return nil
}

func (u *updater) hostMatcherAnnotation() string {
	return fmt.Sprintf("%s%s", u.labelAnnotationPrefix, hostMatcherAnnotationSuffix)
}

// onModifiedOrAddedEvent creates new serviceConfig and places it in lastSeenServices map.
func (u *updater) onModifiedOrAddedEvent(serviceObj service, service serviceKey, eType eventType) error {
	_, ok := u.lastSeenServices[service]
	if ok && eType == added {
		return errors.Errorf("Got %s event for item %v that already exists", eType, service)
	}

	if !ok {
		if eType == modified {
			return errors.Errorf("Got %s event for item %v that we are seeing for the first time", eType, service)
		}
	}

	var hostMatcherOverride string
	if serviceObj.Metadata.Annotations != nil {
		hostMatcherOverride = serviceObj.Metadata.Annotations[u.hostMatcherAnnotation()]
	}

	foundRoutes := serviceRoutings{
		http: make(map[backendName][]route),
		grpc: make(map[backendName][]route),
	}
	foundBackends := serviceBackends{
		httpDomainPorts: make(map[backendName]string),
		grpcDomainPorts: make(map[backendName]string),
		tlsConfigs:      make(map[backendName]struct{}),
	}

	for _, port := range serviceObj.Spec.Ports {
		// NOTE: There is no check if this port actually is exposed by serviceObj.
		portToExpose := port.TargetPort
		if portToExpose == nil {
			portToExpose = port.Port
		}

		backendName := backendName{
			service:    service,
			targetPort: fmt.Sprintf("%v", portToExpose),
		}
		domainPort := fmt.Sprintf("%s.%s:%v", service.name, service.namespace, port.Name)
		foundRoute := route{
			nameMatcher: fmt.Sprintf("%s.%s", service.name, u.externalDomainSuffix),
			portMatcher: port.Port,
		}

		scheme := getScheme(port.Name)

		if hostMatcherOverride != "" {
			foundRoute.nameMatcher = hostMatcherOverride
		}

		switch scheme {
		case httpScheme, httptlsScheme:
			foundRoutes.http[backendName] = append(foundRoutes.http[backendName], foundRoute)
			// Since target port is the same we can use whatever domainPort we have here.
			foundBackends.httpDomainPorts[backendName] = domainPort

			if scheme == httptlsScheme {
				// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
				foundBackends.tlsConfigs[backendName] = struct{}{}
			}
		case grpcScheme, grpctlsScheme:
			foundRoutes.grpc[backendName] = append(foundRoutes.grpc[backendName], foundRoute)
			// Since target port is the same we can use whatever domainPort we have here.
			foundBackends.grpcDomainPorts[backendName] = domainPort

			if scheme == grpctlsScheme {
				// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
				foundBackends.tlsConfigs[backendName] = struct{}{}
			}
		}
	}

	u.lastSeenServices[service] = serviceConf{
		routings: foundRoutes,
		backends: foundBackends,
	}
	return nil
}

func isDefaultPort(scheme protocolType, port uint32) bool {
	switch scheme {
	case httpScheme:
		return port == uint32(80)
	case httptlsScheme:
		return port == uint32(443)
	}
	return false
}

type protocolType int

const (
	httpScheme protocolType = iota
	httptlsScheme
	grpcScheme
	grpctlsScheme
	unknownScheme
)

func getScheme(portName string) protocolType {
	if portName == "http" || strings.HasPrefix(portName, "http-") {
		return httpScheme
	}

	if portName == "httptls" || strings.HasPrefix(portName, "httptls-") {
		return httptlsScheme
	}

	if portName == "grpc" || strings.HasPrefix(portName, "grpc-") {
		return grpcScheme
	}

	if portName == "grpctls" || strings.HasPrefix(portName, "grpctls-") {
		return grpctlsScheme
	}

	return unknownScheme
}
<commit_msg>Use service short name<commit_after>package discovery

import (
	"fmt"
	"strings"

	pb_config "github.com/improbable-eng/kedge/protogen/kedge/config"
	"github.com/pkg/errors"
)

type route struct {
	// Host (HTTP) or Authority (gRPC).
	nameMatcher string
	// If 0 it means just name matcher only.
	portMatcher uint32
}

type serviceKey struct {
	name, namespace string
}

type backendName struct {
	service    serviceKey
	targetPort string
}

func (b backendName) String() string {
	backendName := fmt.Sprintf("%s_%s_%v", b.service.name, b.service.namespace, b.targetPort)
	// BackendName needs to conform regex: "^[a-z_0-9.]{2,64}$"
	return strings.Replace(backendName, "-", "_", -1)
}

type serviceRoutings struct {
	// There could be more than one director routing per backend.
	http map[backendName][]route
	grpc map[backendName][]route
}

type serviceBackends struct {
	httpDomainPorts map[backendName]string
	grpcDomainPorts map[backendName]string

	tlsConfigs map[backendName]struct{}
}

type serviceConf struct {
	routings serviceRoutings
	backends serviceBackends
}

type updater struct {
	baseDirectorConfig    *pb_config.DirectorConfig
	baseBackendConfig     *pb_config.BackendPoolConfig
	externalDomainSuffix  string
	labelAnnotationPrefix string

	lastSeenServices map[serviceKey]serviceConf
}

func newUpdater(
	baseDirector *pb_config.DirectorConfig,
	baseBackendpool *pb_config.BackendPoolConfig,
	externalDomainSuffix string,
	labelAnnotationPrefix string,
) *updater {
	return &updater{
		baseDirectorConfig:    baseDirector,
		baseBackendConfig:     baseBackendpool,
		externalDomainSuffix:  externalDomainSuffix,
		labelAnnotationPrefix: labelAnnotationPrefix,
		lastSeenServices:      make(map[serviceKey]serviceConf),
	}
}

func (u *updater) onEvent(e event) (*pb_config.DirectorConfig, *pb_config.BackendPoolConfig, error) {
	serviceObj := e.Object
	service := serviceKey{serviceObj.Metadata.Name, serviceObj.Metadata.Namespace}

	var err error
	switch e.Type {
	case deleted:
		err = u.onDeletedEvent(service)
	case added, modified:
		err = u.onModifiedOrAddedEvent(serviceObj, service, e.Type)
	default:
		err = errors.Errorf("Got not supported event type %s", e.Type)
	}
	if err != nil {
		return nil, nil, err
	}

	return u.lastSeenServicesToConfigs()
}

// onDeletedEvent creates removes deleted serviceConfig from lastSeenServices map.
func (u *updater) onDeletedEvent(service serviceKey) error {
	_, ok := u.lastSeenServices[service]
	if !ok {
		return errors.Errorf("Got %s event for item %v that we are seeing for the first time", deleted, service)
	}

	delete(u.lastSeenServices, service)
	return nil
}

func (u *updater) hostMatcherAnnotation() string {
	return fmt.Sprintf("%s%s", u.labelAnnotationPrefix, hostMatcherAnnotationSuffix)
}

// onModifiedOrAddedEvent creates new serviceConfig and places it in lastSeenServices map.
func (u *updater) onModifiedOrAddedEvent(serviceObj service, service serviceKey, eType eventType) error {
	_, ok := u.lastSeenServices[service]
	if ok && eType == added {
		return errors.Errorf("Got %s event for item %v that already exists", eType, service)
	}

	if !ok {
		if eType == modified {
			return errors.Errorf("Got %s event for item %v that we are seeing for the first time", eType, service)
		}
	}

	var hostMatcherOverride string
	if serviceObj.Metadata.Annotations != nil {
		hostMatcherOverride = serviceObj.Metadata.Annotations[u.hostMatcherAnnotation()]
	}

	foundRoutes := serviceRoutings{
		http: make(map[backendName][]route),
		grpc: make(map[backendName][]route),
	}
	foundBackends := serviceBackends{
		httpDomainPorts: make(map[backendName]string),
		grpcDomainPorts: make(map[backendName]string),
		tlsConfigs:      make(map[backendName]struct{}),
	}

	for _, port := range serviceObj.Spec.Ports {
		// NOTE: There is no check if this port actually is exposed by serviceObj.
		portToExpose := port.TargetPort
		if portToExpose == nil {
			portToExpose = port.Port
		}

		backendName := backendName{
			service:    service,
			targetPort: fmt.Sprintf("%v", portToExpose),
		}
		domainPort := fmt.Sprintf("%s.%s:%v", service.name, service.namespace, port.Name)
		foundRoute := route{
			nameMatcher: fmt.Sprintf("%s.%s.svc.%s", service.name, service.namespace, u.externalDomainSuffix),
			portMatcher: port.Port,
		}

		scheme := getScheme(port.Name)

		if hostMatcherOverride != "" {
			foundRoute.nameMatcher = hostMatcherOverride
		}

		switch scheme {
		case httpScheme, httptlsScheme:
			foundRoutes.http[backendName] = append(foundRoutes.http[backendName], foundRoute)
			// Since target port is the same we can use whatever domainPort we have here.
			foundBackends.httpDomainPorts[backendName] = domainPort

			if scheme == httptlsScheme {
				// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
				foundBackends.tlsConfigs[backendName] = struct{}{}
			}
		case grpcScheme, grpctlsScheme:
			foundRoutes.grpc[backendName] = append(foundRoutes.grpc[backendName], foundRoute)
			// Since target port is the same we can use whatever domainPort we have here.
			foundBackends.grpcDomainPorts[backendName] = domainPort

			if scheme == grpctlsScheme {
				// TODO(bplotka): Add support for customizing the TLS config (or setting it to actually verify!) using service annotations.
				foundBackends.tlsConfigs[backendName] = struct{}{}
			}
		}
	}

	u.lastSeenServices[service] = serviceConf{
		routings: foundRoutes,
		backends: foundBackends,
	}
	return nil
}

func isDefaultPort(scheme protocolType, port uint32) bool {
	switch scheme {
	case httpScheme:
		return port == uint32(80)
	case httptlsScheme:
		return port == uint32(443)
	}
	return false
}

type protocolType int

const (
	httpScheme protocolType = iota
	httptlsScheme
	grpcScheme
	grpctlsScheme
	unknownScheme
)

func getScheme(portName string) protocolType {
	if portName == "http" || strings.HasPrefix(portName, "http-") {
		return httpScheme
	}

	if portName == "httptls" || strings.HasPrefix(portName, "httptls-") {
		return httptlsScheme
	}

	if portName == "grpc" || strings.HasPrefix(portName, "grpc-") {
		return grpcScheme
	}

	if portName == "grpctls" || strings.HasPrefix(portName, "grpctls-") {
		return grpctlsScheme
	}

	return unknownScheme
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The HAProxy Ingress Controller Authors.

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

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

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

package haproxy

import (
	"fmt"
	"reflect"
	"sort"
	"strconv"

	hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/utils"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/types"
)

type dynUpdater struct {
	logger types.Logger
	socket string
	cmd    func(socket string, commands ...string) ([]string, error)
}

type backendPair struct {
	old *hatypes.Backend
	cur *hatypes.Backend
}

type epPair struct {
	old *hatypes.Endpoint
	cur *hatypes.Endpoint
}

func (i *instance) newDynUpdater(socket string) *dynUpdater {
	return &dynUpdater{
		logger: i.logger,
		socket: socket,
		cmd:    utils.HAProxyCommand,
	}
}

func (d *dynUpdater) dynUpdate(old, cur Config) bool {
	if old == nil || cur == nil {
		return false
	}
	oldConfig := old.(*config)
	curConfig := cur.(*config)
	if oldConfig == nil || curConfig == nil {
		return false
	}

	// check equality of everything but backends
	oldConfigCopy := *oldConfig
	oldConfigCopy.backends = curConfig.backends
	if !reflect.DeepEqual(&oldConfigCopy, curConfig) {
		return false
	}

	// map backends of old and new config together
	// return false if len or names doesn't match
	if len(curConfig.backends) != len(curConfig.backends) {
		return false
	}
	backends := make(map[string]*backendPair, len(oldConfig.backends))
	for _, backend := range oldConfig.backends {
		backends[backend.ID] = &backendPair{old: backend}
	}
	for _, backend := range curConfig.backends {
		back, found := backends[backend.ID]
		if !found {
			return false
		}
		back.cur = backend
	}

	// try to dynamically update every single backend
	// true if deep equals or sucessfully updated
	// false if cannot be dynamically updated or update failed
	for _, pair := range backends {
		if !d.updateBackendPair(pair) {
			return false
		}
	}

	return true
}

func (d *dynUpdater) updateBackendPair(pair *backendPair) bool {
	oldBack := pair.old
	curBack := pair.cur

	// check equality of everything but endpoints
	oldBackCopy := *oldBack
	oldBackCopy.Endpoints = curBack.Endpoints
	if !reflect.DeepEqual(&oldBackCopy, curBack) {
		return false
	}

	// can decrease endpoints, cannot increase
	if len(oldBack.Endpoints) < len(curBack.Endpoints) {
		return false
	}

	// map endpoints of old and new config together
	endpoints := make(map[string]*epPair, len(oldBack.Endpoints))
	empty := make([]string, 0, len(oldBack.Endpoints))
	for _, endpoint := range oldBack.Endpoints {
		if endpoint.Enabled {
			endpoints[endpoint.Target] = &epPair{old: endpoint}
		} else {
			empty = append(empty, endpoint.Name)
		}
	}

	// current endpoint names will be overwritten from its
	// old counterpart, this will save some socket calls
	added := make([]*hatypes.Endpoint, 0, len(curBack.Endpoints))
	for _, endpoint := range curBack.Endpoints {
		if pair, found := endpoints[endpoint.Target]; found {
			endpoint.Name = pair.old.Name
			pair.cur = endpoint
		} else {
			added = append(added, endpoint)
		}
	}

	// try to dynamically remove/update/add endpoints
	for _, pair := range endpoints {
		if pair.cur == nil {
			if !d.execDisableEndpoint(curBack.ID, pair.old) {
				return false
			}
			empty = append(empty, pair.old.Name)
		} else if !d.updateEndpointPair(curBack.ID, pair) {
			return false
		}
	}
	sort.Strings(empty)
	for i := range added {
		added[i].Name = empty[i]
		if !d.execEnableEndpoint(curBack.ID, added[i]) {
			return false
		}
	}

	return true
}

func (d *dynUpdater) updateEndpointPair(backname string, pair *epPair) bool {
	if reflect.DeepEqual(pair.old, pair.cur) {
		return true
	}
	return d.execEnableEndpoint(backname, pair.cur)
}

func (d *dynUpdater) execDisableEndpoint(backname string, ep *hatypes.Endpoint) bool {
	server := fmt.Sprintf("set server %s/%s ", backname, ep.Name)
	cmd := []string{
		server + "state maint",
		server + "addr 127.0.0.1 port 1023",
		server + "weight 0",
	}
	msg, err := d.cmd(d.socket, cmd...)
	if err != nil {
		d.logger.Error("error disabling endpoint %s/%s: %v", backname, ep.Name, err)
		return false
	}
	for _, m := range msg {
		d.logger.Info(m)
	}
	d.logger.InfoV(2, "disabled endpoint %s on backend/server %s/%s", ep.Target, backname, ep.Name)
	return true
}

func (d *dynUpdater) execEnableEndpoint(backname string, ep *hatypes.Endpoint) bool {
	stateReady := map[bool]string{true: "ready", false: "drain"}
	server := fmt.Sprintf("set server %s/%s ", backname, ep.Name)
	cmd := []string{
		server + "addr " + ep.IP + " port " + strconv.Itoa(ep.Port),
		server + "state " + stateReady[ep.Weight > 0],
		server + "weight " + strconv.Itoa(ep.Weight),
	}
	msg, err := d.cmd(d.socket, cmd...)
	if err != nil {
		d.logger.Error("error adding endpoint %s/%s: %v", backname, ep.Name, err)
		return false
	}
	for _, m := range msg {
		d.logger.Info(m)
	}
	d.logger.InfoV(2, "added endpoint %s on backend/server %s/%s", ep.Target, backname, ep.Name)
	return true
}
<commit_msg>Reduce memory allocation when comparing backends<commit_after>/*
Copyright 2019 The HAProxy Ingress Controller Authors.

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

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

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

package haproxy

import (
	"fmt"
	"reflect"
	"sort"
	"strconv"

	hatypes "github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/types"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/haproxy/utils"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/types"
)

type dynUpdater struct {
	logger types.Logger
	socket string
	cmd    func(socket string, commands ...string) ([]string, error)
}

type backendPair struct {
	old *hatypes.Backend
	cur *hatypes.Backend
}

type epPair struct {
	old *hatypes.Endpoint
	cur *hatypes.Endpoint
}

func (i *instance) newDynUpdater(socket string) *dynUpdater {
	return &dynUpdater{
		logger: i.logger,
		socket: socket,
		cmd:    utils.HAProxyCommand,
	}
}

func (d *dynUpdater) dynUpdate(old, cur Config) bool {
	if old == nil || cur == nil {
		return false
	}
	oldConfig := old.(*config)
	curConfig := cur.(*config)
	if oldConfig == nil || curConfig == nil {
		return false
	}

	// check equality of everything but backends
	oldConfigCopy := *oldConfig
	oldConfigCopy.backends = curConfig.backends
	if !reflect.DeepEqual(&oldConfigCopy, curConfig) {
		return false
	}

	// map backends of old and new config together
	// return false if len or names doesn't match
	if len(curConfig.backends) != len(curConfig.backends) {
		return false
	}
	backends := make(map[string]*backendPair, len(oldConfig.backends))
	for _, backend := range oldConfig.backends {
		backends[backend.ID] = &backendPair{old: backend}
	}
	for _, backend := range curConfig.backends {
		back, found := backends[backend.ID]
		if !found {
			return false
		}
		back.cur = backend
	}

	// try to dynamically update every single backend
	// true if deep equals or sucessfully updated
	// false if cannot be dynamically updated or update failed
	for _, pair := range backends {
		if !d.updateBackendPair(pair) {
			return false
		}
	}

	return true
}

func (d *dynUpdater) updateBackendPair(pair *backendPair) bool {
	oldBack := pair.old
	curBack := pair.cur

	// check equality of everything but endpoints
	oldBackCopy := *oldBack
	oldBackCopy.Endpoints = curBack.Endpoints
	if !reflect.DeepEqual(&oldBackCopy, curBack) {
		return false
	}

	// can decrease endpoints, cannot increase
	if len(oldBack.Endpoints) < len(curBack.Endpoints) {
		return false
	}

	// map endpoints of old and new config together
	endpoints := make(map[string]*epPair, len(oldBack.Endpoints))
	var empty []string
	for _, endpoint := range oldBack.Endpoints {
		if endpoint.Enabled {
			endpoints[endpoint.Target] = &epPair{old: endpoint}
		} else {
			empty = append(empty, endpoint.Name)
		}
	}

	// current endpoint names will be overwritten from its
	// old counterpart, this will save some socket calls
	var added []*hatypes.Endpoint
	for _, endpoint := range curBack.Endpoints {
		if pair, found := endpoints[endpoint.Target]; found {
			endpoint.Name = pair.old.Name
			pair.cur = endpoint
		} else {
			added = append(added, endpoint)
		}
	}

	// try to dynamically remove/update/add endpoints
	for _, pair := range endpoints {
		if pair.cur == nil {
			if !d.execDisableEndpoint(curBack.ID, pair.old) {
				return false
			}
			empty = append(empty, pair.old.Name)
		} else if !d.updateEndpointPair(curBack.ID, pair) {
			return false
		}
	}
	sort.Strings(empty)
	for i := range added {
		added[i].Name = empty[i]
		if !d.execEnableEndpoint(curBack.ID, added[i]) {
			return false
		}
	}

	return true
}

func (d *dynUpdater) updateEndpointPair(backname string, pair *epPair) bool {
	if reflect.DeepEqual(pair.old, pair.cur) {
		return true
	}
	return d.execEnableEndpoint(backname, pair.cur)
}

func (d *dynUpdater) execDisableEndpoint(backname string, ep *hatypes.Endpoint) bool {
	server := fmt.Sprintf("set server %s/%s ", backname, ep.Name)
	cmd := []string{
		server + "state maint",
		server + "addr 127.0.0.1 port 1023",
		server + "weight 0",
	}
	msg, err := d.cmd(d.socket, cmd...)
	if err != nil {
		d.logger.Error("error disabling endpoint %s/%s: %v", backname, ep.Name, err)
		return false
	}
	for _, m := range msg {
		d.logger.Info(m)
	}
	d.logger.InfoV(2, "disabled endpoint %s on backend/server %s/%s", ep.Target, backname, ep.Name)
	return true
}

func (d *dynUpdater) execEnableEndpoint(backname string, ep *hatypes.Endpoint) bool {
	stateReady := map[bool]string{true: "ready", false: "drain"}
	server := fmt.Sprintf("set server %s/%s ", backname, ep.Name)
	cmd := []string{
		server + "addr " + ep.IP + " port " + strconv.Itoa(ep.Port),
		server + "state " + stateReady[ep.Weight > 0],
		server + "weight " + strconv.Itoa(ep.Weight),
	}
	msg, err := d.cmd(d.socket, cmd...)
	if err != nil {
		d.logger.Error("error adding endpoint %s/%s: %v", backname, ep.Name, err)
		return false
	}
	for _, m := range msg {
		d.logger.Info(m)
	}
	d.logger.InfoV(2, "added endpoint %s on backend/server %s/%s", ep.Target, backname, ep.Name)
	return true
}
<|endoftext|>


<commit_before>package proxy

import (
	"github.com/juju/errors"
	log "github.com/ngaut/logging"
	"github.com/wandoulabs/cm/mysql"
	"github.com/wandoulabs/cm/sqlparser"
	"strings"
)

var nstring = sqlparser.String

func (c *Conn) handleSet(stmt *sqlparser.Set, sql string) error {
	if len(stmt.Exprs) != 1 {
		return errors.Errorf("must set one item once, not %s", nstring(stmt, c.alloc))
	}

	switch stmt.Scope {
	case "global":
		log.Warning("set global")
	case "session":
		log.Warning("set session")
	}

	k := string(stmt.Exprs[0].Name.Name)

	switch strings.ToUpper(k) {
	case `AUTOCOMMIT`:
		return c.handleSetAutoCommit(stmt.Exprs[0].Expr, sql)
	case `NAMES`:
		return c.handleSetNames(stmt.Exprs[0].Expr)
	default:
		//todo:strict condition
		return c.handleShow(nil, sql, nil) //errors.Errorf("set %s is not supported now", k)
	}
}

func (c *Conn) handleSetAutoCommit(val sqlparser.ValExpr, sql string) error {
	value, ok := val.(sqlparser.NumVal)
	if !ok {
		return errors.Errorf("set autocommit error")
	}

	switch value[0] {
	case '1':
		log.Warning("set autocommit 1")
		//c.status |= SERVER_STATUS_AUTOCOMMIT //todo: extra to function
	case '0':
		log.Warning("set autocommit 0")
		c.server.IncCounter("set autocommit 0")
		c.status &= ^mysql.SERVER_STATUS_AUTOCOMMIT
	default:
		return errors.Errorf("invalid autocommit flag %s", value)
	}

	err := c.writeOkFlush(nil)
	return errors.Trace(err)
}

func (c *Conn) handleSetNames(val sqlparser.ValExpr) error {
	value, ok := val.(sqlparser.StrVal)
	if !ok {
		return errors.Errorf("set names charset error")
	}

	charset := strings.ToLower(string(value))
	cid, ok := mysql.CharsetIds[charset]
	if !ok {
		return errors.Errorf("invalid charset %s", charset)
	}

	c.charset = charset
	c.collation = cid

	err := c.writeOkFlush(nil)
	return errors.Trace(err)
}
<commit_msg>remove set statement item count check<commit_after>package proxy

import (
	"github.com/juju/errors"
	log "github.com/ngaut/logging"
	"github.com/wandoulabs/cm/mysql"
	"github.com/wandoulabs/cm/sqlparser"
	"strings"
)

var nstring = sqlparser.String

func (c *Conn) handleSet(stmt *sqlparser.Set, sql string) error {
	switch stmt.Scope {
	case "global":
		log.Warning("set global")
	case "session":
		log.Warning("set session")
	}

	k := string(stmt.Exprs[0].Name.Name)

	switch strings.ToUpper(k) {
	case `AUTOCOMMIT`:
		return c.handleSetAutoCommit(stmt.Exprs[0].Expr, sql)
	case `NAMES`:
		return c.handleSetNames(stmt.Exprs[0].Expr)
	default:
		//todo:strict condition
		return c.handleShow(nil, sql, nil) //errors.Errorf("set %s is not supported now", k)
	}
}

func (c *Conn) handleSetAutoCommit(val sqlparser.ValExpr, sql string) error {
	value, ok := val.(sqlparser.NumVal)
	if !ok {
		return errors.Errorf("set autocommit error")
	}

	switch value[0] {
	case '1':
		log.Warning("set autocommit 1")
		//c.status |= SERVER_STATUS_AUTOCOMMIT //todo: extra to function
	case '0':
		log.Warning("set autocommit 0")
		c.server.IncCounter("set autocommit 0")
		c.status &= ^mysql.SERVER_STATUS_AUTOCOMMIT
	default:
		return errors.Errorf("invalid autocommit flag %s", value)
	}

	err := c.writeOkFlush(nil)
	return errors.Trace(err)
}

func (c *Conn) handleSetNames(val sqlparser.ValExpr) error {
	value, ok := val.(sqlparser.StrVal)
	if !ok {
		return errors.Errorf("set names charset error")
	}

	charset := strings.ToLower(string(value))
	cid, ok := mysql.CharsetIds[charset]
	if !ok {
		return errors.Errorf("invalid charset %s", charset)
	}

	c.charset = charset
	c.collation = cid

	err := c.writeOkFlush(nil)
	return errors.Trace(err)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 Google LLC

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 reconciler

import (
	"k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	appsv1listers "k8s.io/client-go/listers/apps/v1"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/record"
)

const (
	deploymentCreated = "DeploymentCreated"
	deploymentUpdated = "DeploymentUpdated"
	serviceCreated    = "ServiceCreated"
	serviceUpdated    = "ServiceUpdated"
)

type ServiceReconciler struct {
	KubeClient      kubernetes.Interface
	ServiceLister   corev1listers.ServiceLister
	EndpointsLister corev1listers.EndpointsLister
	Recorder        record.EventRecorder
}

type DeploymentReconciler struct {
	KubeClient kubernetes.Interface
	Lister     appsv1listers.DeploymentLister
	Recorder   record.EventRecorder
}

// ReconcileDeployment reconciles the K8s Deployment 'd'.
func (r *DeploymentReconciler) ReconcileDeployment(obj runtime.Object, d *v1.Deployment) (*v1.Deployment, error) {
	current, err := r.Lister.Deployments(d.Namespace).Get(d.Name)
	if apierrs.IsNotFound(err) {
		current, err = r.KubeClient.AppsV1().Deployments(d.Namespace).Create(d)
		if apierrs.IsAlreadyExists(err) {
			return current, nil
		}
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, deploymentCreated, "Created deployment %s/%s", d.Namespace, d.Name)
		}
		return current, err
	}
	if err != nil {
		return nil, err
	}
	if !equality.Semantic.DeepDerivative(d.Spec, current.Spec) {
		// Don't modify the informers copy.
		desired := current.DeepCopy()
		desired.Spec = d.Spec
		d, err := r.KubeClient.AppsV1().Deployments(desired.Namespace).Update(desired)
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, deploymentUpdated, "Updated deployment %s/%s", d.Namespace, d.Name)
		}
		return d, err
	}
	return current, err
}

// ReconcileService reconciles the K8s Service 'svc'.
func (r *ServiceReconciler) ReconcileService(obj runtime.Object, svc *corev1.Service) (*corev1.Endpoints, error) {
	current, err := r.ServiceLister.Services(svc.Namespace).Get(svc.Name)

	if apierrs.IsNotFound(err) {
		current, err = r.KubeClient.CoreV1().Services(svc.Namespace).Create(svc)
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, serviceCreated, "Created service %s/%s", svc.Namespace, svc.Name)
		}
		if err == nil || apierrs.IsAlreadyExists(err) {
			return r.EndpointsLister.Endpoints(svc.Namespace).Get(svc.Name)
		}
		return nil, err
	}
	if err != nil {
		return nil, err
	}
	// spec.clusterIP is immutable and is set on existing services. If we don't set this to the same value, we will
	// encounter an error while updating.
	svc.Spec.ClusterIP = current.Spec.ClusterIP
	if !equality.Semantic.DeepDerivative(svc.Spec, current.Spec) {
		// Don't modify the informers copy.
		desired := current.DeepCopy()
		desired.Spec = svc.Spec
		current, err = r.KubeClient.CoreV1().Services(current.Namespace).Update(desired)
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, serviceUpdated, "Updated service %s/%s", svc.Namespace, svc.Name)
		}
		if err != nil {
			return nil, err
		}
	}

	return r.EndpointsLister.Endpoints(svc.Namespace).Get(svc.Name)
}
<commit_msg>Alias import of k8s.io/api/apps/v1 (#1122)<commit_after>/*
Copyright 2020 Google LLC

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 reconciler

import (
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	appsv1listers "k8s.io/client-go/listers/apps/v1"
	corev1listers "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/record"
)

const (
	deploymentCreated = "DeploymentCreated"
	deploymentUpdated = "DeploymentUpdated"
	serviceCreated    = "ServiceCreated"
	serviceUpdated    = "ServiceUpdated"
)

type ServiceReconciler struct {
	KubeClient      kubernetes.Interface
	ServiceLister   corev1listers.ServiceLister
	EndpointsLister corev1listers.EndpointsLister
	Recorder        record.EventRecorder
}

type DeploymentReconciler struct {
	KubeClient kubernetes.Interface
	Lister     appsv1listers.DeploymentLister
	Recorder   record.EventRecorder
}

// ReconcileDeployment reconciles the K8s Deployment 'd'.
func (r *DeploymentReconciler) ReconcileDeployment(obj runtime.Object, d *appsv1.Deployment) (*appsv1.Deployment, error) {
	current, err := r.Lister.Deployments(d.Namespace).Get(d.Name)
	if apierrs.IsNotFound(err) {
		current, err = r.KubeClient.AppsV1().Deployments(d.Namespace).Create(d)
		if apierrs.IsAlreadyExists(err) {
			return current, nil
		}
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, deploymentCreated, "Created deployment %s/%s", d.Namespace, d.Name)
		}
		return current, err
	}
	if err != nil {
		return nil, err
	}
	if !equality.Semantic.DeepDerivative(d.Spec, current.Spec) {
		// Don't modify the informers copy.
		desired := current.DeepCopy()
		desired.Spec = d.Spec
		d, err := r.KubeClient.AppsV1().Deployments(desired.Namespace).Update(desired)
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, deploymentUpdated, "Updated deployment %s/%s", d.Namespace, d.Name)
		}
		return d, err
	}
	return current, err
}

// ReconcileService reconciles the K8s Service 'svc'.
func (r *ServiceReconciler) ReconcileService(obj runtime.Object, svc *corev1.Service) (*corev1.Endpoints, error) {
	current, err := r.ServiceLister.Services(svc.Namespace).Get(svc.Name)

	if apierrs.IsNotFound(err) {
		current, err = r.KubeClient.CoreV1().Services(svc.Namespace).Create(svc)
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, serviceCreated, "Created service %s/%s", svc.Namespace, svc.Name)
		}
		if err == nil || apierrs.IsAlreadyExists(err) {
			return r.EndpointsLister.Endpoints(svc.Namespace).Get(svc.Name)
		}
		return nil, err
	}
	if err != nil {
		return nil, err
	}
	// spec.clusterIP is immutable and is set on existing services. If we don't set this to the same value, we will
	// encounter an error while updating.
	svc.Spec.ClusterIP = current.Spec.ClusterIP
	if !equality.Semantic.DeepDerivative(svc.Spec, current.Spec) {
		// Don't modify the informers copy.
		desired := current.DeepCopy()
		desired.Spec = svc.Spec
		current, err = r.KubeClient.CoreV1().Services(current.Namespace).Update(desired)
		if err == nil {
			r.Recorder.Eventf(obj, corev1.EventTypeNormal, serviceUpdated, "Updated service %s/%s", svc.Namespace, svc.Name)
		}
		if err != nil {
			return nil, err
		}
	}

	return r.EndpointsLister.Endpoints(svc.Namespace).Get(svc.Name)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package utils

import (
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/intstr"

	"k8s.io/ingress-gce/pkg/annotations"
	backendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1beta1"
)

// ServicePortID contains the Service and Port fields.
type ServicePortID struct {
	Service types.NamespacedName
	Port    intstr.IntOrString
}

// ServicePort maintains configuration for a single backend.
type ServicePort struct {
	// Ingress backend-specified service name and port
	ID ServicePortID

	NodePort int64
	// Numerical port of the Service, retrieved from the Service
	Port          int32
	Protocol      annotations.AppProtocol
	TargetPort    string
	NEGEnabled    bool
	BackendConfig *backendconfigv1beta1.BackendConfig
}

// GetDescription returns a Description for this ServicePort.
func (sp ServicePort) GetDescription() Description {
	return Description{
		ServiceName: sp.ID.Service.String(),
		ServicePort: sp.ID.Port.String(),
	}
}

// IsAlpha returns true if the ServicePort is using ProtocolHTTP2 - which means
// we need to use the Alpha API.
// TODO(rramkumar): Replace all instances of this call with Version()
func (sp ServicePort) IsAlpha() bool {
	return sp.Protocol == annotations.ProtocolHTTP2
}

// BackendName returns the name of the backend which would be used for this ServicePort.
func (sp ServicePort) BackendName(namer *Namer) string {
	if !sp.NEGEnabled {
		return namer.IGBackend(sp.NodePort)
	}

	return namer.NEG(sp.ID.Service.Namespace, sp.ID.Service.Name, sp.Port)
}

// BackendToServicePortID creates a ServicePortID from a given IngressBackend and namespace.
func BackendToServicePortID(be extensions.IngressBackend, namespace string) ServicePortID {
	return ServicePortID{
		Service: types.NamespacedName{
			Name:      be.ServiceName,
			Namespace: namespace,
		},
		Port: be.ServicePort,
	}
}
<commit_msg>add Version for ServicePort<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package utils

import (
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"

	"k8s.io/ingress-gce/pkg/annotations"
	backendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1beta1"
)

// ServicePortID contains the Service and Port fields.
type ServicePortID struct {
	Service types.NamespacedName
	Port    intstr.IntOrString
}

// ServicePort maintains configuration for a single backend.
type ServicePort struct {
	// Ingress backend-specified service name and port
	ID ServicePortID

	NodePort int64
	// Numerical port of the Service, retrieved from the Service
	Port          int32
	Protocol      annotations.AppProtocol
	TargetPort    string
	NEGEnabled    bool
	BackendConfig *backendconfigv1beta1.BackendConfig
}

// GetDescription returns a Description for this ServicePort.
func (sp ServicePort) GetDescription() Description {
	return Description{
		ServiceName: sp.ID.Service.String(),
		ServicePort: sp.ID.Port.String(),
	}
}

// Version returns the support version of the ServicePort configuration
// Alpha includes HTTP2
// Beta includes NEG
// The rest are v1
func (sp ServicePort) Version() meta.Version {
	if sp.Protocol == annotations.ProtocolHTTP2 {
		return meta.VersionAlpha
	}
	if sp.NEGEnabled {
		return meta.VersionBeta
	}
	return meta.VersionGA
}

// BackendName returns the name of the backend which would be used for this ServicePort.
func (sp ServicePort) BackendName(namer *Namer) string {
	if !sp.NEGEnabled {
		return namer.IGBackend(sp.NodePort)
	}

	return namer.NEG(sp.ID.Service.Namespace, sp.ID.Service.Name, sp.Port)
}

// BackendToServicePortID creates a ServicePortID from a given IngressBackend and namespace.
func BackendToServicePortID(be extensions.IngressBackend, namespace string) ServicePortID {
	return ServicePortID{
		Service: types.NamespacedName{
			Name:      be.ServiceName,
			Namespace: namespace,
		},
		Port: be.ServicePort,
	}
}
<|endoftext|>


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

package visitors

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strconv"
	"unsafe"

	"github.com/linuxboot/fiano/pkg/guid"
	"github.com/linuxboot/fiano/pkg/uefi"
)

// CreateFV creates a firmware volume at given offset
type CreateFV struct {
	AbsOffset uint64
	Size      uint64
	Name      guid.GUID

	offset uint64
	found  bool
}

// Run wraps Visit and performs some setup and teardown tasks.
func (v *CreateFV) Run(f uefi.Firmware) error {
	err := f.Apply(v)
	if err != nil {
		return err
	}
	if !v.found {
		return fmt.Errorf("Cannot create FV at %#x (+%#x) no BIOS region found", v.AbsOffset, v.Size)
	}
	return nil
}

// Visit applies the Remove visitor to any Firmware type.
func (v *CreateFV) Visit(f uefi.Firmware) error {
	if v.found {
		return nil
	}
	var offset, end uint64
	switch f := f.(type) {
	case *uefi.BIOSRegion:
		if f.FRegion != nil {
			offset = uint64(f.FRegion.BaseOffset())
			end = uint64(f.FRegion.EndOffset())
		} else {
			end = f.Length
		}
		if v.AbsOffset < offset {
			return fmt.Errorf("Cannot create FV at %#x, BIOS region starts at %#x", v.AbsOffset, offset)
		}
		if v.AbsOffset+v.Size >= end {
			return fmt.Errorf("Cannot create FV ending at %#x (%#x + %#x), BIOS region ends at %#x", v.AbsOffset+v.Size, v.AbsOffset, v.Size, end)
		}

		// Manually visit children, we want the index
		idx := -1
		var bp *uefi.BIOSPadding
	L:
		for i, e := range f.Elements {
			switch f := e.Value.(type) {
			case *uefi.BIOSPadding:
				bp = f
				bpOffset := offset + f.Offset
				bpEnd := bpOffset + uint64(len(f.Buf()))
				if v.AbsOffset >= bpOffset && v.AbsOffset+v.Size <= bpEnd {

					idx = i
					break L
				}
			}
		}

		if idx == -1 {
			return fmt.Errorf("Cannot create FV at %#x (+%#x) no matching BIOS Pad found", v.AbsOffset, v.Size)
		}
		v.found = true
		fv, err := createEmptyFirmwareVolume(v.AbsOffset-offset, v.Size, &v.Name)
		if err != nil {
			return err
		}
		return insertFVinBP(f, v.AbsOffset-offset, bp, idx, fv)
	}

	return f.ApplyChildren(v)
}

func insertFVinBP(br *uefi.BIOSRegion, offset uint64, bp *uefi.BIOSPadding, idx int, fv *uefi.FirmwareVolume) error {
	// Copy the Elements before the modified BIOS Pad
	newElements := make([]*uefi.TypedFirmware, idx, len(br.Elements))
	copy(newElements, br.Elements[:idx])

	bpBuf := bp.Buf()
	// keep part of the BIOS Pad before the new fv if needed
	if bp.Offset < offset {
		hbp, err := uefi.NewBIOSPadding(bpBuf[:offset-bp.Offset], bp.Offset)
		if err != nil {
			return err
		}
		newElements = append(newElements, uefi.MakeTyped(hbp))
	}

	// Add the FV
	newElements = append(newElements, uefi.MakeTyped(fv))

	// keep part of the BIOS Pad after the new fv if needed
	if offset-bp.Offset+fv.Length < uint64(len(bpBuf)) {
		tbp, err := uefi.NewBIOSPadding(bpBuf[offset-bp.Offset+fv.Length:], offset+fv.Length)
		if err != nil {
			return err
		}
		newElements = append(newElements, uefi.MakeTyped(tbp))
	}

	// Keep the remaining Elements in the BIOS Region
	if idx+1 < len(br.Elements) {
		newElements = append(newElements, br.Elements[idx+1:]...)
	}
	br.Elements = newElements
	return nil
}

func createEmptyFirmwareVolume(fvOffset, size uint64, name *guid.GUID) (*uefi.FirmwareVolume, error) {
	// TODO: can this be refactored with the code in repack.go and assemble.go ?
	fv := &uefi.FirmwareVolume{} // new Firmware Volume
	// Set up volume header first.
	fv.FileSystemGUID = *uefi.FFS2
	fv.Signature = binary.LittleEndian.Uint32([]byte("_FVH"))
	// TODO: retrieve all details from (all) other fv in BIOS Region
	fv.Attributes = 0x0004FEFF
	fv.Revision = 2
	// Create Blocks
	fv.Blocks = make([]uefi.Block, 2)
	fv.Blocks[0] = uefi.Block{Size: 4096, Count: uint32(size / 4096)}
	fv.Blocks[1] = uefi.Block{}
	// Calculate the HeaderLen field
	fv.HeaderLen = uint16(uefi.FirmwareVolumeFixedHeaderSize + int(unsafe.Sizeof(uefi.Block{}))*len(fv.Blocks))

	fv.DataOffset = uint64(fv.HeaderLen) // unless we add the extended header
	fv.Length = size

	if name != nil {
		// TODO: should the extended header offset be computed ?
		fv.ExtHeaderOffset = 0x60
		fv.FVName = *name
		fv.ExtHeaderSize = uefi.FirmwareVolumeExtHeaderMinSize
	}
	// Generate binary header.
	header := new(bytes.Buffer)
	err := binary.Write(header, binary.LittleEndian, fv.FirmwareVolumeFixedHeader)
	if err != nil {
		return nil, fmt.Errorf("unable to construct binary header of new firmware volume: got %v", err)
	}
	for _, b := range fv.Blocks {
		err = binary.Write(header, binary.LittleEndian, b)
		if err != nil {
			return nil, fmt.Errorf("unable to construct binary header of new firmware volume: got %v", err)
		}

	}
	buf := header.Bytes()

	// Checksum the header
	sum, err := uefi.Checksum16(buf[:fv.HeaderLen])
	if err != nil {
		return nil, err
	}
	newSum := 0 - sum
	binary.LittleEndian.PutUint16(buf[50:], newSum)

	// Store the header buffer in
	fv.SetBuf(buf)

	if name != nil {
		// Build the ExtHeader
		extHeader := new(bytes.Buffer)
		err = binary.Write(extHeader, binary.LittleEndian, fv.FirmwareVolumeExtHeader)
		if err != nil {
			return nil, fmt.Errorf("unable to construct binary extended header of new firmware volume: got %v", err)
		}

		extHeaderFile, err := uefi.CreatePadFile(uint64(uefi.FileHeaderMinLength + fv.ExtHeaderSize))
		if err != nil {
			return nil, fmt.Errorf("Building ExtHeader %v", err)
		}
		if err := extHeaderFile.ChecksumAndAssemble(extHeader.Bytes()); err != nil {
			return nil, fmt.Errorf("Building ExtHeader %v", err)
		}

		// Add the extended header in a Padfile just after the header.
		extHeaderFileBuf := extHeaderFile.Buf()
		if err = fv.InsertFile(fv.DataOffset, extHeaderFileBuf); err != nil {
			return nil, fmt.Errorf("Adding ExtHeader %v", err)
		}
		fv.DataOffset += uint64(len(extHeaderFileBuf))
	}
	// Add empty space
	extLen := fv.Length - fv.DataOffset
	emptyBuf := make([]byte, extLen)
	uefi.Erase(emptyBuf, uefi.Attributes.ErasePolarity)

	// Store the buffer in
	fv.SetBuf(append(fv.Buf(), emptyBuf...))

	// Make sure DataOffset is 8 byte aligned at least.
	fv.DataOffset = uefi.Align8(fv.DataOffset)

	// Internal fields
	fv.FVOffset = fvOffset
	fv.FreeSpace = fv.Length - fv.DataOffset

	return fv, nil
}

func init() {
	RegisterCLI("create-fv", "creates a FV given an offset, size and volume GUID (can only replace a BIOS Padding)", 3, func(args []string) (uefi.Visitor, error) {
		offset, err := strconv.ParseUint(args[0], 0, 64)
		if err != nil {
			return nil, err
		}
		size, err := strconv.ParseUint(args[1], 0, 64)
		if err != nil {
			return nil, err
		}
		name, err := guid.Parse(args[2])
		if err != nil {
			return nil, err
		}
		return &CreateFV{
			AbsOffset: offset,
			Size:      size,
			Name:      *name,
		}, nil
	})
}
<commit_msg>visitors/createfv: Update after review<commit_after>// Copyright 2018 the LinuxBoot Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package visitors

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strconv"
	"unsafe"

	"github.com/linuxboot/fiano/pkg/guid"
	"github.com/linuxboot/fiano/pkg/uefi"
)

// CreateFV creates a firmware volume at given offset
type CreateFV struct {
	AbsOffset uint64
	Size      uint64
	Name      guid.GUID

	offset uint64
	found  bool
}

// Run wraps Visit and performs some setup and teardown tasks.
func (v *CreateFV) Run(f uefi.Firmware) error {
	err := f.Apply(v)
	if err != nil {
		return err
	}
	if !v.found {
		return fmt.Errorf("Cannot create FV at %#x (+%#x) no BIOS region found", v.AbsOffset, v.Size)
	}
	return nil
}

// Visit applies the CreateFV visitor to any Firmware type.
func (v *CreateFV) Visit(f uefi.Firmware) error {
	if v.found {
		return nil
	}
	var offset, end uint64
	switch f := f.(type) {
	case *uefi.BIOSRegion:
		if f.FRegion != nil {
			offset = uint64(f.FRegion.BaseOffset())
			end = uint64(f.FRegion.EndOffset())
		} else {
			end = f.Length
		}
		if v.AbsOffset < offset {
			return fmt.Errorf("Cannot create FV at %#x, BIOS region starts at %#x", v.AbsOffset, offset)
		}
		if v.AbsOffset+v.Size >= end {
			return fmt.Errorf("Cannot create FV ending at %#x (%#x + %#x), BIOS region ends at %#x", v.AbsOffset+v.Size, v.AbsOffset, v.Size, end)
		}

		// Manually visit children, we want the index
		idx := -1
		var bp *uefi.BIOSPadding
	L:
		for i, e := range f.Elements {
			switch f := e.Value.(type) {
			case *uefi.BIOSPadding:
				bp = f
				bpOffset := offset + f.Offset
				bpEnd := bpOffset + uint64(len(f.Buf()))
				// Warning: This won't work if for whatever reason we have two biospads that are contiguous
				// TODO: join contiguous before !
				if v.AbsOffset >= bpOffset && v.AbsOffset+v.Size <= bpEnd {

					idx = i
					break L
				}
			}
		}

		if idx == -1 {
			return fmt.Errorf("Cannot create FV at %#x (+%#x) no matching BIOS Pad found", v.AbsOffset, v.Size)
		}
		v.found = true
		fv, err := createEmptyFirmwareVolume(v.AbsOffset-offset, v.Size, &v.Name)
		if err != nil {
			return err
		}
		return insertFVinBP(f, v.AbsOffset-offset, bp, idx, fv)
	}

	return f.ApplyChildren(v)
}

func insertFVinBP(br *uefi.BIOSRegion, offset uint64, bp *uefi.BIOSPadding, idx int, fv *uefi.FirmwareVolume) error {
	// Copy the Elements before the modified BIOS Pad
	newElements := make([]*uefi.TypedFirmware, idx, len(br.Elements))
	copy(newElements, br.Elements[:idx])

	bpBuf := bp.Buf()
	// keep part of the BIOS Pad before the new fv if needed
	if bp.Offset < offset {
		hbp, err := uefi.NewBIOSPadding(bpBuf[:offset-bp.Offset], bp.Offset)
		if err != nil {
			return err
		}
		newElements = append(newElements, uefi.MakeTyped(hbp))
	}

	// Add the FV
	newElements = append(newElements, uefi.MakeTyped(fv))

	// keep part of the BIOS Pad after the new fv if needed
	tbpStart := offset - bp.Offset + fv.Length
	if tbpStart < uint64(len(bpBuf)) {
		tbp, err := uefi.NewBIOSPadding(bpBuf[tbpStart:], offset+fv.Length)
		if err != nil {
			return err
		}
		newElements = append(newElements, uefi.MakeTyped(tbp))
	}

	// Keep the remaining Elements in the BIOS Region
	if idx+1 < len(br.Elements) {
		newElements = append(newElements, br.Elements[idx+1:]...)
	}
	br.Elements = newElements
	return nil
}

func createEmptyFirmwareVolume(fvOffset, size uint64, name *guid.GUID) (*uefi.FirmwareVolume, error) {
	// TODO: can this be refactored with the code in repack.go and assemble.go ?
	fv := &uefi.FirmwareVolume{} // new Firmware Volume
	// Set up volume header first.
	fv.FileSystemGUID = *uefi.FFS2
	fv.Signature = binary.LittleEndian.Uint32([]byte("_FVH"))
	// TODO: retrieve all details from (all) other fv in BIOS Region
	fv.Attributes = 0x0004FEFF
	fv.Revision = 2
	// Create Blocks
	fv.Blocks = make([]uefi.Block, 2)
	fv.Blocks[0] = uefi.Block{Size: 4096, Count: uint32(size / 4096)}
	fv.Blocks[1] = uefi.Block{}
	// Calculate the HeaderLen field
	fv.HeaderLen = uint16(uefi.FirmwareVolumeFixedHeaderSize + int(unsafe.Sizeof(uefi.Block{}))*len(fv.Blocks))

	fv.DataOffset = uint64(fv.HeaderLen) // unless we add the extended header
	fv.Length = size

	if name != nil {
		// TODO: compute the extended header offset (especially if more than 2 Blocks, this offset will be wrong)
		fv.ExtHeaderOffset = 0x60
		fv.FVName = *name
		fv.ExtHeaderSize = uefi.FirmwareVolumeExtHeaderMinSize
	}
	// Generate binary header.
	header := new(bytes.Buffer)
	err := binary.Write(header, binary.LittleEndian, fv.FirmwareVolumeFixedHeader)
	if err != nil {
		return nil, fmt.Errorf("unable to construct binary header of new firmware volume: got %v", err)
	}
	for _, b := range fv.Blocks {
		err = binary.Write(header, binary.LittleEndian, b)
		if err != nil {
			return nil, fmt.Errorf("unable to construct binary header of new firmware volume: got %v", err)
		}

	}
	buf := header.Bytes()

	// Checksum the header
	sum, err := uefi.Checksum16(buf[:fv.HeaderLen])
	if err != nil {
		return nil, err
	}
	newSum := 0 - sum
	binary.LittleEndian.PutUint16(buf[50:], newSum)

	// Store the header buffer in
	fv.SetBuf(buf)

	if name != nil {
		// Build the ExtHeader
		extHeader := new(bytes.Buffer)
		err = binary.Write(extHeader, binary.LittleEndian, fv.FirmwareVolumeExtHeader)
		if err != nil {
			return nil, fmt.Errorf("unable to construct binary extended header of new firmware volume: got %v", err)
		}

		// The extended header in encapsulated in a PadFile just after the header.
		// The UEFI PI Specification is not clear on that point, however the implementation in tianocore GenFv tools is clear:
		// At [1] `GenerateFvImage` gives the extended header as an argument to `AddPadFile` implemented at [2].
		// [1]: https://github.com/tianocore/edk2/blob/master/BaseTools/Source/C/GenFv/GenFvInternalLib.c#L2772
		// [2]: https://github.com/tianocore/edk2/blob/master/BaseTools/Source/C/GenFv/GenFvInternalLib.c#L563
		extHeaderFile, err := uefi.CreatePadFile(uint64(uefi.FileHeaderMinLength + fv.ExtHeaderSize))
		if err != nil {
			return nil, fmt.Errorf("Building ExtHeader %v", err)
		}
		if err := extHeaderFile.ChecksumAndAssemble(extHeader.Bytes()); err != nil {
			return nil, fmt.Errorf("Building ExtHeader %v", err)
		}

		// Add the extended header in a Padfile just after the header.
		extHeaderFileBuf := extHeaderFile.Buf()
		if err = fv.InsertFile(fv.DataOffset, extHeaderFileBuf); err != nil {
			return nil, fmt.Errorf("Adding ExtHeader %v", err)
		}
		fv.DataOffset += uint64(len(extHeaderFileBuf))
	}
	// Add empty space
	extLen := fv.Length - fv.DataOffset
	emptyBuf := make([]byte, extLen)
	uefi.Erase(emptyBuf, uefi.Attributes.ErasePolarity)

	// Store the buffer in
	fv.SetBuf(append(fv.Buf(), emptyBuf...))

	// Make sure DataOffset is 8 byte aligned at least.
	fv.DataOffset = uefi.Align8(fv.DataOffset)

	// Internal fields
	fv.FVOffset = fvOffset
	fv.FreeSpace = fv.Length - fv.DataOffset

	return fv, nil
}

func init() {
	RegisterCLI("create-fv", "creates a FV given an offset, size and volume GUID (can only replace a BIOS Padding)", 3, func(args []string) (uefi.Visitor, error) {
		offset, err := strconv.ParseUint(args[0], 0, 64)
		if err != nil {
			return nil, err
		}
		size, err := strconv.ParseUint(args[1], 0, 64)
		if err != nil {
			return nil, err
		}
		name, err := guid.Parse(args[2])
		if err != nil {
			return nil, err
		}
		return &CreateFV{
			AbsOffset: offset,
			Size:      size,
			Name:      *name,
		}, nil
	})
}
<|endoftext|>