<commit_before>package llb

import (
	"context"
	_ "crypto/sha256" // for opencontainers/go-digest
	"encoding/json"
	"os"
	"strconv"
	"strings"

	"github.com/docker/distribution/reference"
	"github.com/moby/buildkit/solver/pb"
	"github.com/moby/buildkit/util/apicaps"
	digest "github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
)

type SourceOp struct {
	MarshalCache
	id          string
	attrs       map[string]string
	output      Output
	constraints Constraints
	err         error
}

func NewSource(id string, attrs map[string]string, c Constraints) *SourceOp {
	s := &SourceOp{
		id:          id,
		attrs:       attrs,
		constraints: c,
	}
	s.output = &output{vertex: s, platform: c.Platform}
	return s
}

func (s *SourceOp) Validate(ctx context.Context) error {
	if s.err != nil {
		return s.err
	}
	if s.id == "" {
		return errors.Errorf("source identifier can't be empty")
	}
	return nil
}

func (s *SourceOp) Marshal(ctx context.Context, constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
	if s.Cached(constraints) {
		return s.Load()
	}
	if err := s.Validate(ctx); err != nil {
		return "", nil, nil, nil, err
	}

	if strings.HasPrefix(s.id, "local://") {
		if _, hasSession := s.attrs[pb.AttrLocalSessionID]; !hasSession {
			uid := s.constraints.LocalUniqueID
			if uid == "" {
				uid = constraints.LocalUniqueID
			}
			s.attrs[pb.AttrLocalUniqueID] = uid
			addCap(&s.constraints, pb.CapSourceLocalUnique)
		}
	}
	proto, md := MarshalConstraints(constraints, &s.constraints)

	proto.Op = &pb.Op_Source{
		Source: &pb.SourceOp{Identifier: s.id, Attrs: s.attrs},
	}

	if !platformSpecificSource(s.id) {
		proto.Platform = nil
	}

	dt, err := proto.Marshal()
	if err != nil {
		return "", nil, nil, nil, err
	}

	s.Store(dt, md, s.constraints.SourceLocations, constraints)
	return s.Load()
}

func (s *SourceOp) Output() Output {
	return s.output
}

func (s *SourceOp) Inputs() []Output {
	return nil
}

func Image(ref string, opts ...ImageOption) State {
	r, err := reference.ParseNormalizedNamed(ref)
	if err == nil {
		r = reference.TagNameOnly(r)
		ref = r.String()
	}
	var info ImageInfo
	for _, opt := range opts {
		opt.SetImageOption(&info)
	}

	addCap(&info.Constraints, pb.CapSourceImage)

	attrs := map[string]string{}
	if info.resolveMode != 0 {
		attrs[pb.AttrImageResolveMode] = info.resolveMode.String()
		if info.resolveMode == ResolveModeForcePull {
			addCap(&info.Constraints, pb.CapSourceImageResolveMode) // only require cap for security enforced mode
		}
	}

	if info.RecordType != "" {
		attrs[pb.AttrImageRecordType] = info.RecordType
	}

	src := NewSource("docker-image://"+ref, attrs, info.Constraints) // controversial
	if err != nil {
		src.err = err
	} else if info.metaResolver != nil {
		if _, ok := r.(reference.Digested); ok || !info.resolveDigest {
			return NewState(src.Output()).Async(func(ctx context.Context, st State) (State, error) {
				_, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{
					Platform:    info.Constraints.Platform,
					ResolveMode: info.resolveMode.String(),
				})
				if err != nil {
					return State{}, err
				}
				return st.WithImageConfig(dt)
			})
		}
		return Scratch().Async(func(ctx context.Context, _ State) (State, error) {
			dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
				Platform:    info.Constraints.Platform,
				ResolveMode: info.resolveMode.String(),
			})
			if err != nil {
				return State{}, err
			}
			if dgst != "" {
				r, err = reference.WithDigest(r, dgst)
				if err != nil {
					return State{}, err
				}
			}
			return NewState(NewSource("docker-image://"+r.String(), attrs, info.Constraints).Output()).WithImageConfig(dt)
		})
	}
	return NewState(src.Output())
}

type ImageOption interface {
	SetImageOption(*ImageInfo)
}

type imageOptionFunc func(*ImageInfo)

func (fn imageOptionFunc) SetImageOption(ii *ImageInfo) {
	fn(ii)
}

var MarkImageInternal = imageOptionFunc(func(ii *ImageInfo) {
	ii.RecordType = "internal"
})

type ResolveMode int

const (
	ResolveModeDefault ResolveMode = iota
	ResolveModeForcePull
	ResolveModePreferLocal
)

func (r ResolveMode) SetImageOption(ii *ImageInfo) {
	ii.resolveMode = r
}

func (r ResolveMode) String() string {
	switch r {
	case ResolveModeDefault:
		return pb.AttrImageResolveModeDefault
	case ResolveModeForcePull:
		return pb.AttrImageResolveModeForcePull
	case ResolveModePreferLocal:
		return pb.AttrImageResolveModePreferLocal
	default:
		return ""
	}
}

type ImageInfo struct {
	constraintsWrapper
	metaResolver  ImageMetaResolver
	resolveDigest bool
	resolveMode   ResolveMode
	RecordType    string
}

func Git(remote, ref string, opts ...GitOption) State {
	url := ""

	for _, prefix := range []string{
		"http://", "https://", "git://", "git@",
	} {
		if strings.HasPrefix(remote, prefix) {
			url = strings.Split(remote, "#")[0]
			remote = strings.TrimPrefix(remote, prefix)
		}
	}

	id := remote

	if ref != "" {
		id += "#" + ref
	}

	gi := &GitInfo{
		AuthHeaderSecret: "GIT_AUTH_HEADER",
		AuthTokenSecret:  "GIT_AUTH_TOKEN",
	}
	for _, o := range opts {
		o.SetGitOption(gi)
	}
	attrs := map[string]string{}
	if gi.KeepGitDir {
		attrs[pb.AttrKeepGitDir] = "true"
		addCap(&gi.Constraints, pb.CapSourceGitKeepDir)
	}
	if url != "" {
		attrs[pb.AttrFullRemoteURL] = url
		addCap(&gi.Constraints, pb.CapSourceGitFullURL)
	}
	if gi.AuthTokenSecret != "" {
		attrs[pb.AttrAuthTokenSecret] = gi.AuthTokenSecret
		if gi.addAuthCap {
			addCap(&gi.Constraints, pb.CapSourceGitHTTPAuth)
		}
	}
	if gi.AuthHeaderSecret != "" {
		attrs[pb.AttrAuthHeaderSecret] = gi.AuthHeaderSecret
		if gi.addAuthCap {
			addCap(&gi.Constraints, pb.CapSourceGitHTTPAuth)
		}
	}
	if gi.KnownSSHHosts != "" {
		attrs[pb.AttrKnownSSHHosts] = gi.KnownSSHHosts
		addCap(&gi.Constraints, pb.CapSourceGitKnownSSHHosts)
	}
	if gi.MountSSHSock != "" {
		attrs[pb.AttrMountSSHSock] = gi.MountSSHSock
		addCap(&gi.Constraints, pb.CapSourceGitMountSSHSock)
	}

	addCap(&gi.Constraints, pb.CapSourceGit)

	source := NewSource("git://"+id, attrs, gi.Constraints)
	return NewState(source.Output())
}

type GitOption interface {
	SetGitOption(*GitInfo)
}
type gitOptionFunc func(*GitInfo)

func (fn gitOptionFunc) SetGitOption(gi *GitInfo) {
	fn(gi)
}

type GitInfo struct {
	constraintsWrapper
	KeepGitDir       bool
	AuthTokenSecret  string
	AuthHeaderSecret string
	addAuthCap       bool
	KnownSSHHosts    string
	MountSSHSock     string
}

func KeepGitDir() GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.KeepGitDir = true
	})
}

func AuthTokenSecret(v string) GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.AuthTokenSecret = v
		gi.addAuthCap = true
	})
}

func AuthHeaderSecret(v string) GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.AuthHeaderSecret = v
		gi.addAuthCap = true
	})
}

func KnownSSHHosts(key string) GitOption {
	key = strings.TrimSuffix(key, "\n")
	return gitOptionFunc(func(gi *GitInfo) {
		gi.KnownSSHHosts = gi.KnownSSHHosts + key + "\n"
	})
}

func MountSSHSock(sshID string) GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.MountSSHSock = sshID
	})
}

func Scratch() State {
	return NewState(nil)
}

func Local(name string, opts ...LocalOption) State {
	gi := &LocalInfo{}

	for _, o := range opts {
		o.SetLocalOption(gi)
	}
	attrs := map[string]string{}
	if gi.SessionID != "" {
		attrs[pb.AttrLocalSessionID] = gi.SessionID
		addCap(&gi.Constraints, pb.CapSourceLocalSessionID)
	}
	if gi.IncludePatterns != "" {
		attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
		addCap(&gi.Constraints, pb.CapSourceLocalIncludePatterns)
	}
	if gi.FollowPaths != "" {
		attrs[pb.AttrFollowPaths] = gi.FollowPaths
		addCap(&gi.Constraints, pb.CapSourceLocalFollowPaths)
	}
	if gi.ExcludePatterns != "" {
		attrs[pb.AttrExcludePatterns] = gi.ExcludePatterns
		addCap(&gi.Constraints, pb.CapSourceLocalExcludePatterns)
	}
	if gi.SharedKeyHint != "" {
		attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
		addCap(&gi.Constraints, pb.CapSourceLocalSharedKeyHint)
	}

	addCap(&gi.Constraints, pb.CapSourceLocal)

	source := NewSource("local://"+name, attrs, gi.Constraints)
	return NewState(source.Output())
}

type LocalOption interface {
	SetLocalOption(*LocalInfo)
}

type localOptionFunc func(*LocalInfo)

func (fn localOptionFunc) SetLocalOption(li *LocalInfo) {
	fn(li)
}

func SessionID(id string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		li.SessionID = id
	})
}

func IncludePatterns(p []string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		if len(p) == 0 {
			li.IncludePatterns = ""
			return
		}
		dt, _ := json.Marshal(p) // empty on error
		li.IncludePatterns = string(dt)
	})
}

func FollowPaths(p []string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		if len(p) == 0 {
			li.FollowPaths = ""
			return
		}
		dt, _ := json.Marshal(p) // empty on error
		li.FollowPaths = string(dt)
	})
}

func ExcludePatterns(p []string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		if len(p) == 0 {
			li.ExcludePatterns = ""
			return
		}
		dt, _ := json.Marshal(p) // empty on error
		li.ExcludePatterns = string(dt)
	})
}

func SharedKeyHint(h string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		li.SharedKeyHint = h
	})
}

type LocalInfo struct {
	constraintsWrapper
	SessionID       string
	IncludePatterns string
	ExcludePatterns string
	FollowPaths     string
	SharedKeyHint   string
}

func HTTP(url string, opts ...HTTPOption) State {
	hi := &HTTPInfo{}
	for _, o := range opts {
		o.SetHTTPOption(hi)
	}
	attrs := map[string]string{}
	if hi.Checksum != "" {
		attrs[pb.AttrHTTPChecksum] = hi.Checksum.String()
		addCap(&hi.Constraints, pb.CapSourceHTTPChecksum)
	}
	if hi.Filename != "" {
		attrs[pb.AttrHTTPFilename] = hi.Filename
	}
	if hi.Perm != 0 {
		attrs[pb.AttrHTTPPerm] = "0" + strconv.FormatInt(int64(hi.Perm), 8)
		addCap(&hi.Constraints, pb.CapSourceHTTPPerm)
	}
	if hi.UID != 0 {
		attrs[pb.AttrHTTPUID] = strconv.Itoa(hi.UID)
		addCap(&hi.Constraints, pb.CapSourceHTTPUIDGID)
	}
	if hi.GID != 0 {
		attrs[pb.AttrHTTPGID] = strconv.Itoa(hi.GID)
		addCap(&hi.Constraints, pb.CapSourceHTTPUIDGID)
	}

	addCap(&hi.Constraints, pb.CapSourceHTTP)
	source := NewSource(url, attrs, hi.Constraints)
	return NewState(source.Output())
}

type HTTPInfo struct {
	constraintsWrapper
	Checksum digest.Digest
	Filename string
	Perm     int
	UID      int
	GID      int
}

type HTTPOption interface {
	SetHTTPOption(*HTTPInfo)
}

type httpOptionFunc func(*HTTPInfo)

func (fn httpOptionFunc) SetHTTPOption(hi *HTTPInfo) {
	fn(hi)
}

func Checksum(dgst digest.Digest) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.Checksum = dgst
	})
}

func Chmod(perm os.FileMode) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.Perm = int(perm) & 0777
	})
}

func Filename(name string) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.Filename = name
	})
}

func Chown(uid, gid int) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.UID = uid
		hi.GID = gid
	})
}

func platformSpecificSource(id string) bool {
	return strings.HasPrefix(id, "docker-image://")
}

func addCap(c *Constraints, id apicaps.CapID) {
	if c.Metadata.Caps == nil {
		c.Metadata.Caps = make(map[apicaps.CapID]bool)
	}
	c.Metadata.Caps[id] = true
}
<commit_msg>Fix parsing ssh-based git sources<commit_after>package llb

import (
	"context"
	_ "crypto/sha256" // for opencontainers/go-digest
	"encoding/json"
	"os"
	"strconv"
	"strings"

	"github.com/docker/distribution/reference"
	"github.com/moby/buildkit/solver/pb"
	"github.com/moby/buildkit/util/apicaps"
	digest "github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
)

type SourceOp struct {
	MarshalCache
	id          string
	attrs       map[string]string
	output      Output
	constraints Constraints
	err         error
}

func NewSource(id string, attrs map[string]string, c Constraints) *SourceOp {
	s := &SourceOp{
		id:          id,
		attrs:       attrs,
		constraints: c,
	}
	s.output = &output{vertex: s, platform: c.Platform}
	return s
}

func (s *SourceOp) Validate(ctx context.Context) error {
	if s.err != nil {
		return s.err
	}
	if s.id == "" {
		return errors.Errorf("source identifier can't be empty")
	}
	return nil
}

func (s *SourceOp) Marshal(ctx context.Context, constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
	if s.Cached(constraints) {
		return s.Load()
	}
	if err := s.Validate(ctx); err != nil {
		return "", nil, nil, nil, err
	}

	if strings.HasPrefix(s.id, "local://") {
		if _, hasSession := s.attrs[pb.AttrLocalSessionID]; !hasSession {
			uid := s.constraints.LocalUniqueID
			if uid == "" {
				uid = constraints.LocalUniqueID
			}
			s.attrs[pb.AttrLocalUniqueID] = uid
			addCap(&s.constraints, pb.CapSourceLocalUnique)
		}
	}
	proto, md := MarshalConstraints(constraints, &s.constraints)

	proto.Op = &pb.Op_Source{
		Source: &pb.SourceOp{Identifier: s.id, Attrs: s.attrs},
	}

	if !platformSpecificSource(s.id) {
		proto.Platform = nil
	}

	dt, err := proto.Marshal()
	if err != nil {
		return "", nil, nil, nil, err
	}

	s.Store(dt, md, s.constraints.SourceLocations, constraints)
	return s.Load()
}

func (s *SourceOp) Output() Output {
	return s.output
}

func (s *SourceOp) Inputs() []Output {
	return nil
}

func Image(ref string, opts ...ImageOption) State {
	r, err := reference.ParseNormalizedNamed(ref)
	if err == nil {
		r = reference.TagNameOnly(r)
		ref = r.String()
	}
	var info ImageInfo
	for _, opt := range opts {
		opt.SetImageOption(&info)
	}

	addCap(&info.Constraints, pb.CapSourceImage)

	attrs := map[string]string{}
	if info.resolveMode != 0 {
		attrs[pb.AttrImageResolveMode] = info.resolveMode.String()
		if info.resolveMode == ResolveModeForcePull {
			addCap(&info.Constraints, pb.CapSourceImageResolveMode) // only require cap for security enforced mode
		}
	}

	if info.RecordType != "" {
		attrs[pb.AttrImageRecordType] = info.RecordType
	}

	src := NewSource("docker-image://"+ref, attrs, info.Constraints) // controversial
	if err != nil {
		src.err = err
	} else if info.metaResolver != nil {
		if _, ok := r.(reference.Digested); ok || !info.resolveDigest {
			return NewState(src.Output()).Async(func(ctx context.Context, st State) (State, error) {
				_, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{
					Platform:    info.Constraints.Platform,
					ResolveMode: info.resolveMode.String(),
				})
				if err != nil {
					return State{}, err
				}
				return st.WithImageConfig(dt)
			})
		}
		return Scratch().Async(func(ctx context.Context, _ State) (State, error) {
			dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
				Platform:    info.Constraints.Platform,
				ResolveMode: info.resolveMode.String(),
			})
			if err != nil {
				return State{}, err
			}
			if dgst != "" {
				r, err = reference.WithDigest(r, dgst)
				if err != nil {
					return State{}, err
				}
			}
			return NewState(NewSource("docker-image://"+r.String(), attrs, info.Constraints).Output()).WithImageConfig(dt)
		})
	}
	return NewState(src.Output())
}

type ImageOption interface {
	SetImageOption(*ImageInfo)
}

type imageOptionFunc func(*ImageInfo)

func (fn imageOptionFunc) SetImageOption(ii *ImageInfo) {
	fn(ii)
}

var MarkImageInternal = imageOptionFunc(func(ii *ImageInfo) {
	ii.RecordType = "internal"
})

type ResolveMode int

const (
	ResolveModeDefault ResolveMode = iota
	ResolveModeForcePull
	ResolveModePreferLocal
)

func (r ResolveMode) SetImageOption(ii *ImageInfo) {
	ii.resolveMode = r
}

func (r ResolveMode) String() string {
	switch r {
	case ResolveModeDefault:
		return pb.AttrImageResolveModeDefault
	case ResolveModeForcePull:
		return pb.AttrImageResolveModeForcePull
	case ResolveModePreferLocal:
		return pb.AttrImageResolveModePreferLocal
	default:
		return ""
	}
}

type ImageInfo struct {
	constraintsWrapper
	metaResolver  ImageMetaResolver
	resolveDigest bool
	resolveMode   ResolveMode
	RecordType    string
}

func Git(remote, ref string, opts ...GitOption) State {
	url := ""
	isSSH := true

	for _, prefix := range []string{
		"http://", "https://",
	} {
		if strings.HasPrefix(remote, prefix) {
			url = strings.Split(remote, "#")[0]
			remote = strings.TrimPrefix(remote, prefix)
			isSSH = false
			break
		}
	}
	if isSSH {
		remote = strings.TrimPrefix(remote, "git://")
		url = remote
		parts := strings.SplitN(remote, "@", 2)
		if len(parts) == 2 {
			//sshUser = parts[0]
			remote = parts[1]
		}
		// keep remote consistent with http(s) version
		remote = strings.Replace(remote, ":", "/", 1)
	}

	id := remote

	if ref != "" {
		id += "#" + ref
	}

	gi := &GitInfo{
		AuthHeaderSecret: "GIT_AUTH_HEADER",
		AuthTokenSecret:  "GIT_AUTH_TOKEN",
	}
	for _, o := range opts {
		o.SetGitOption(gi)
	}
	attrs := map[string]string{}
	if gi.KeepGitDir {
		attrs[pb.AttrKeepGitDir] = "true"
		addCap(&gi.Constraints, pb.CapSourceGitKeepDir)
	}
	if url != "" {
		attrs[pb.AttrFullRemoteURL] = url
		addCap(&gi.Constraints, pb.CapSourceGitFullURL)
	}
	if gi.AuthTokenSecret != "" {
		attrs[pb.AttrAuthTokenSecret] = gi.AuthTokenSecret
		if gi.addAuthCap {
			addCap(&gi.Constraints, pb.CapSourceGitHTTPAuth)
		}
	}
	if gi.AuthHeaderSecret != "" {
		attrs[pb.AttrAuthHeaderSecret] = gi.AuthHeaderSecret
		if gi.addAuthCap {
			addCap(&gi.Constraints, pb.CapSourceGitHTTPAuth)
		}
	}
	if gi.KnownSSHHosts != "" {
		attrs[pb.AttrKnownSSHHosts] = gi.KnownSSHHosts
		addCap(&gi.Constraints, pb.CapSourceGitKnownSSHHosts)
	}
	if gi.MountSSHSock != "" {
		attrs[pb.AttrMountSSHSock] = gi.MountSSHSock
		addCap(&gi.Constraints, pb.CapSourceGitMountSSHSock)
	}

	addCap(&gi.Constraints, pb.CapSourceGit)

	source := NewSource("git://"+id, attrs, gi.Constraints)
	return NewState(source.Output())
}

type GitOption interface {
	SetGitOption(*GitInfo)
}
type gitOptionFunc func(*GitInfo)

func (fn gitOptionFunc) SetGitOption(gi *GitInfo) {
	fn(gi)
}

type GitInfo struct {
	constraintsWrapper
	KeepGitDir       bool
	AuthTokenSecret  string
	AuthHeaderSecret string
	addAuthCap       bool
	KnownSSHHosts    string
	MountSSHSock     string
}

func KeepGitDir() GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.KeepGitDir = true
	})
}

func AuthTokenSecret(v string) GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.AuthTokenSecret = v
		gi.addAuthCap = true
	})
}

func AuthHeaderSecret(v string) GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.AuthHeaderSecret = v
		gi.addAuthCap = true
	})
}

func KnownSSHHosts(key string) GitOption {
	key = strings.TrimSuffix(key, "\n")
	return gitOptionFunc(func(gi *GitInfo) {
		gi.KnownSSHHosts = gi.KnownSSHHosts + key + "\n"
	})
}

func MountSSHSock(sshID string) GitOption {
	return gitOptionFunc(func(gi *GitInfo) {
		gi.MountSSHSock = sshID
	})
}

func Scratch() State {
	return NewState(nil)
}

func Local(name string, opts ...LocalOption) State {
	gi := &LocalInfo{}

	for _, o := range opts {
		o.SetLocalOption(gi)
	}
	attrs := map[string]string{}
	if gi.SessionID != "" {
		attrs[pb.AttrLocalSessionID] = gi.SessionID
		addCap(&gi.Constraints, pb.CapSourceLocalSessionID)
	}
	if gi.IncludePatterns != "" {
		attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
		addCap(&gi.Constraints, pb.CapSourceLocalIncludePatterns)
	}
	if gi.FollowPaths != "" {
		attrs[pb.AttrFollowPaths] = gi.FollowPaths
		addCap(&gi.Constraints, pb.CapSourceLocalFollowPaths)
	}
	if gi.ExcludePatterns != "" {
		attrs[pb.AttrExcludePatterns] = gi.ExcludePatterns
		addCap(&gi.Constraints, pb.CapSourceLocalExcludePatterns)
	}
	if gi.SharedKeyHint != "" {
		attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
		addCap(&gi.Constraints, pb.CapSourceLocalSharedKeyHint)
	}

	addCap(&gi.Constraints, pb.CapSourceLocal)

	source := NewSource("local://"+name, attrs, gi.Constraints)
	return NewState(source.Output())
}

type LocalOption interface {
	SetLocalOption(*LocalInfo)
}

type localOptionFunc func(*LocalInfo)

func (fn localOptionFunc) SetLocalOption(li *LocalInfo) {
	fn(li)
}

func SessionID(id string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		li.SessionID = id
	})
}

func IncludePatterns(p []string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		if len(p) == 0 {
			li.IncludePatterns = ""
			return
		}
		dt, _ := json.Marshal(p) // empty on error
		li.IncludePatterns = string(dt)
	})
}

func FollowPaths(p []string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		if len(p) == 0 {
			li.FollowPaths = ""
			return
		}
		dt, _ := json.Marshal(p) // empty on error
		li.FollowPaths = string(dt)
	})
}

func ExcludePatterns(p []string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		if len(p) == 0 {
			li.ExcludePatterns = ""
			return
		}
		dt, _ := json.Marshal(p) // empty on error
		li.ExcludePatterns = string(dt)
	})
}

func SharedKeyHint(h string) LocalOption {
	return localOptionFunc(func(li *LocalInfo) {
		li.SharedKeyHint = h
	})
}

type LocalInfo struct {
	constraintsWrapper
	SessionID       string
	IncludePatterns string
	ExcludePatterns string
	FollowPaths     string
	SharedKeyHint   string
}

func HTTP(url string, opts ...HTTPOption) State {
	hi := &HTTPInfo{}
	for _, o := range opts {
		o.SetHTTPOption(hi)
	}
	attrs := map[string]string{}
	if hi.Checksum != "" {
		attrs[pb.AttrHTTPChecksum] = hi.Checksum.String()
		addCap(&hi.Constraints, pb.CapSourceHTTPChecksum)
	}
	if hi.Filename != "" {
		attrs[pb.AttrHTTPFilename] = hi.Filename
	}
	if hi.Perm != 0 {
		attrs[pb.AttrHTTPPerm] = "0" + strconv.FormatInt(int64(hi.Perm), 8)
		addCap(&hi.Constraints, pb.CapSourceHTTPPerm)
	}
	if hi.UID != 0 {
		attrs[pb.AttrHTTPUID] = strconv.Itoa(hi.UID)
		addCap(&hi.Constraints, pb.CapSourceHTTPUIDGID)
	}
	if hi.GID != 0 {
		attrs[pb.AttrHTTPGID] = strconv.Itoa(hi.GID)
		addCap(&hi.Constraints, pb.CapSourceHTTPUIDGID)
	}

	addCap(&hi.Constraints, pb.CapSourceHTTP)
	source := NewSource(url, attrs, hi.Constraints)
	return NewState(source.Output())
}

type HTTPInfo struct {
	constraintsWrapper
	Checksum digest.Digest
	Filename string
	Perm     int
	UID      int
	GID      int
}

type HTTPOption interface {
	SetHTTPOption(*HTTPInfo)
}

type httpOptionFunc func(*HTTPInfo)

func (fn httpOptionFunc) SetHTTPOption(hi *HTTPInfo) {
	fn(hi)
}

func Checksum(dgst digest.Digest) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.Checksum = dgst
	})
}

func Chmod(perm os.FileMode) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.Perm = int(perm) & 0777
	})
}

func Filename(name string) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.Filename = name
	})
}

func Chown(uid, gid int) HTTPOption {
	return httpOptionFunc(func(hi *HTTPInfo) {
		hi.UID = uid
		hi.GID = gid
	})
}

func platformSpecificSource(id string) bool {
	return strings.HasPrefix(id, "docker-image://")
}

func addCap(c *Constraints, id apicaps.CapID) {
	if c.Metadata.Caps == nil {
		c.Metadata.Caps = make(map[apicaps.CapID]bool)
	}
	c.Metadata.Caps[id] = true
}
<|endoftext|>


<commit_before>package client
<commit_msg>client: remove empty file<commit_after><|endoftext|>


<commit_before>/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

    * Neither the name of "The Computer Language Benchmarks Game" nor the
    name of "The Computer Language Shootout Benchmarks" nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

/* The Computer Language Benchmarks Game
 * http://shootout.alioth.debian.org/
 *
 * contributed by The Go Authors.
 * Based on mandelbrot.c contributed by Greg Buchholz
 */

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
)

var n = flag.Int("n", 200, "size")

func main() {
	flag.Parse()
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	w := *n
	h := *n
	bit_num := 0
	byte_acc := byte(0)
	const Iter = 50
	const Zero float64 = 0
	const Limit = 2.0

	fmt.Fprintf(out, "P4\n%d %d\n", w, h)

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			Zr, Zi, Tr, Ti := Zero, Zero, Zero, Zero
			Cr := (2*float64(x)/float64(w) - 1.5)
			Ci := (2*float64(y)/float64(h) - 1.0)

			for i := 0; i < Iter && (Tr+Ti <= Limit*Limit); i++ {
				Zi = 2*Zr*Zi + Ci
				Zr = Tr - Ti + Cr
				Tr = Zr * Zr
				Ti = Zi * Zi
			}

			byte_acc <<= 1
			if Tr+Ti <= Limit*Limit {
				byte_acc |= 0x01
			}

			bit_num++

			if bit_num == 8 {
				out.WriteByte(byte_acc)
				byte_acc = 0
				bit_num = 0
			} else if x == w-1 {
				byte_acc <<= uint(8 - w%8)
				out.WriteByte(byte_acc)
				byte_acc = 0
				bit_num = 0
			}
		}
	}
}
<commit_msg>shootout: make mandelbrot.go more like mandelbrot.c<commit_after>/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.

    * Neither the name of "The Computer Language Benchmarks Game" nor the
    name of "The Computer Language Shootout Benchmarks" nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

/* The Computer Language Benchmarks Game
 * http://shootout.alioth.debian.org/
 *
 * contributed by The Go Authors.
 * Based on mandelbrot.c contributed by Greg Buchholz
 */

package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
)

var n = flag.Int("n", 200, "size")

func main() {
	flag.Parse()
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()

	w := float64(*n)
	h := float64(*n)
	bit_num := 0
	byte_acc := byte(0)
	const Iter = 50
	const Zero float64 = 0
	const Limit = 2.0

	fmt.Fprintf(out, "P4\n%d %d\n", *n, *n)

	for y := 0.0; y < h; y++ {
		for x := 0.0; x < w; x++ {
			Zr, Zi, Tr, Ti := Zero, Zero, Zero, Zero
			Cr := (2*x/w - 1.5)
			Ci := (2*y/h - 1.0)

			for i := 0; i < Iter && (Tr+Ti <= Limit*Limit); i++ {
				Zi = 2*Zr*Zi + Ci
				Zr = Tr - Ti + Cr
				Tr = Zr * Zr
				Ti = Zi * Zi
			}

			byte_acc <<= 1
			if Tr+Ti <= Limit*Limit {
				byte_acc |= 0x01
			}

			bit_num++

			if bit_num == 8 {
				out.WriteByte(byte_acc)
				byte_acc = 0
				bit_num = 0
			} else if x == w-1 {
				byte_acc <<= uint(8 - uint(*n)%8)
				out.WriteByte(byte_acc)
				byte_acc = 0
				bit_num = 0
			}
		}
	}
}
<|endoftext|>


<commit_before>package oauth

import (
	"context"
	"fmt"
	"net/http"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	"golang.org/x/oauth2"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/rest"

	oauthv1 "github.com/openshift/api/oauth/v1"
	userv1client "github.com/openshift/client-go/user/clientset/versioned/typed/user/v1"

	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/oauthserver"
)

var _ = g.Describe("[sig-auth][Feature:OAuthServer] [Token Expiration]", func() {
	var oc = exutil.NewCLI("oauth-expiration")
	var newRequestTokenOptions oauthserver.NewRequestTokenOptionsFunc
	var oauthServerCleanup func()

	g.BeforeEach(func() {
		var err error
		newRequestTokenOptions, oauthServerCleanup, err = deployOAuthServer(oc)
		o.Expect(err).ToNot(o.HaveOccurred())
	})

	g.AfterEach(func() {
		oauthServerCleanup()
	})

	g.Context("Using a OAuth client with a non-default token max age", func() {
		var oAuthClientResource *oauthv1.OAuthClient
		var accessTokenMaxAgeSeconds int32

		g.JustBeforeEach(func() {
			var err error
			oAuthClientResource, err = oc.AdminOAuthClient().OauthV1().OAuthClients().Create(context.Background(), &oauthv1.OAuthClient{
				ObjectMeta:               metav1.ObjectMeta{Name: fmt.Sprintf("%s-%05d", oc.Namespace(), accessTokenMaxAgeSeconds)},
				RespondWithChallenges:    true,
				RedirectURIs:             []string{"http://localhost"},
				AccessTokenMaxAgeSeconds: &accessTokenMaxAgeSeconds,
				GrantMethod:              oauthv1.GrantHandlerAuto,
			}, metav1.CreateOptions{})
			o.Expect(err).ToNot(o.HaveOccurred())
		})

		g.AfterEach(func() {
			oc.AdminOAuthClient().OauthV1().OAuthClients().Delete(context.Background(), oAuthClientResource.Name, metav1.DeleteOptions{})
		})

		g.Context("to generate tokens that do not expire", func() {

			g.BeforeEach(func() {
				accessTokenMaxAgeSeconds = 0
			})

			g.It("works as expected when using a token authorization flow", func() {
				testTokenFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})

			g.It("works as expected when using a code authorization flow", func() {
				testCodeFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})

		})
		g.Context("to generate tokens that expire shortly", func() {

			g.BeforeEach(func() {
				accessTokenMaxAgeSeconds = 10
			})

			g.It("works as expected when using a token authorization flow", func() {
				testTokenFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})
			g.It("works as expected when using a code authorization flow", func() {
				testCodeFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})
		})
	})
})

func testTokenFlow(oc *exutil.CLI, newRequestTokenOptions oauthserver.NewRequestTokenOptionsFunc, client *oauthv1.OAuthClient, expectedExpiresIn int32) {
	// new request token command
	requestTokenOptions := newRequestTokenOptions("testuser", "password")
	// setup for token flow
	requestTokenOptions.TokenFlow = true
	requestTokenOptions.OsinConfig.CodeChallenge = ""
	requestTokenOptions.OsinConfig.CodeChallengeMethod = ""
	requestTokenOptions.OsinConfig.CodeVerifier = ""
	// setup for non-default oauth client
	requestTokenOptions.OsinConfig.ClientId = client.Name
	requestTokenOptions.OsinConfig.RedirectUrl = client.RedirectURIs[0]
	// request token
	token, err := requestTokenOptions.RequestToken()
	o.Expect(err).ToNot(o.HaveOccurred())
	// Make sure we can use the token, and it represents who we expect
	userConfig := *rest.AnonymousClientConfig(oc.AdminConfig())
	userConfig.BearerToken = token
	userClient, err := userv1client.NewForConfig(&userConfig)
	o.Expect(err).ToNot(o.HaveOccurred())
	user, err := userClient.Users().Get(context.Background(), "~", metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(user.Name).To(o.Equal("testuser"))
	// Make sure the token exists with the overridden time
	tokenObj, err := oc.AdminOauthClient().OauthV1().OAuthAccessTokens().Get(context.Background(), token, metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(tokenObj.ExpiresIn).To(o.BeNumerically("==", expectedExpiresIn))
}

func testCodeFlow(oc *exutil.CLI, newRequestTokenOptions oauthserver.NewRequestTokenOptionsFunc, client *oauthv1.OAuthClient, expectedExpiresIn int32) {
	anonymousClientConfig := rest.AnonymousClientConfig(oc.AdminConfig())
	rt, err := rest.TransportFor(anonymousClientConfig)
	o.Expect(err).ToNot(o.HaveOccurred())

	// need to extract the oauth server root url
	oauthServerURL := newRequestTokenOptions("", "").Issuer

	conf := &oauth2.Config{
		ClientID:     client.Name,
		ClientSecret: client.Secret,
		RedirectURL:  client.RedirectURIs[0],
		Endpoint: oauth2.Endpoint{
			AuthURL:  oauthServerURL + "/oauth/authorize",
			TokenURL: oauthServerURL + "/oauth/token",
		},
	}

	// get code
	req, err := http.NewRequest("GET", conf.AuthCodeURL(""), nil)
	o.Expect(err).ToNot(o.HaveOccurred())

	req.SetBasicAuth("testuser", "password")
	resp, err := rt.RoundTrip(req)
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(resp.StatusCode).To(o.Equal(http.StatusFound))

	location, err := resp.Location()
	o.Expect(err).ToNot(o.HaveOccurred())

	code := location.Query().Get("code")
	o.Expect(code).ToNot(o.BeEmpty())

	// Make sure the code exists with the default time
	oauthClientSet := oc.AdminOauthClient()
	codeObj, err := oauthClientSet.OauthV1().OAuthAuthorizeTokens().Get(context.Background(), code, metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(codeObj.ExpiresIn).To(o.BeNumerically("==", 5*60))

	// Use the custom HTTP client when requesting a token.
	httpClient := &http.Client{Transport: rt}
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
	oauthToken, err := conf.Exchange(ctx, code)
	o.Expect(err).ToNot(o.HaveOccurred())
	token := oauthToken.AccessToken

	// Make sure we can use the token, and it represents who we expect
	userConfig := *anonymousClientConfig
	userConfig.BearerToken = token
	userClient, err := userv1client.NewForConfig(&userConfig)
	o.Expect(err).ToNot(o.HaveOccurred())

	user, err := userClient.Users().Get(context.Background(), "~", metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(user.Name).To(o.Equal("testuser"))

	// Make sure the token exists with the overridden time
	tokenObj, err := oauthClientSet.OauthV1().OAuthAccessTokens().Get(context.Background(), token, metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(tokenObj.ExpiresIn).To(o.BeNumerically("==", expectedExpiresIn))
}
<commit_msg>e2e/oauth/expiration: support sha256 tokens<commit_after>package oauth

import (
	"context"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"net/http"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	"golang.org/x/oauth2"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/rest"

	oauthv1 "github.com/openshift/api/oauth/v1"
	userv1client "github.com/openshift/client-go/user/clientset/versioned/typed/user/v1"

	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/oauthserver"
)

var _ = g.Describe("[sig-auth][Feature:OAuthServer] [Token Expiration]", func() {
	var oc = exutil.NewCLI("oauth-expiration")
	var newRequestTokenOptions oauthserver.NewRequestTokenOptionsFunc
	var oauthServerCleanup func()

	g.BeforeEach(func() {
		var err error
		newRequestTokenOptions, oauthServerCleanup, err = deployOAuthServer(oc)
		o.Expect(err).ToNot(o.HaveOccurred())
	})

	g.AfterEach(func() {
		oauthServerCleanup()
	})

	g.Context("Using a OAuth client with a non-default token max age", func() {
		var oAuthClientResource *oauthv1.OAuthClient
		var accessTokenMaxAgeSeconds int32

		g.JustBeforeEach(func() {
			var err error
			oAuthClientResource, err = oc.AdminOAuthClient().OauthV1().OAuthClients().Create(context.Background(), &oauthv1.OAuthClient{
				ObjectMeta:               metav1.ObjectMeta{Name: fmt.Sprintf("%s-%05d", oc.Namespace(), accessTokenMaxAgeSeconds)},
				RespondWithChallenges:    true,
				RedirectURIs:             []string{"http://localhost"},
				AccessTokenMaxAgeSeconds: &accessTokenMaxAgeSeconds,
				GrantMethod:              oauthv1.GrantHandlerAuto,
			}, metav1.CreateOptions{})
			o.Expect(err).ToNot(o.HaveOccurred())
		})

		g.AfterEach(func() {
			oc.AdminOAuthClient().OauthV1().OAuthClients().Delete(context.Background(), oAuthClientResource.Name, metav1.DeleteOptions{})
		})

		g.Context("to generate tokens that do not expire", func() {

			g.BeforeEach(func() {
				accessTokenMaxAgeSeconds = 0
			})

			g.It("works as expected when using a token authorization flow", func() {
				testTokenFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})

			g.It("works as expected when using a code authorization flow", func() {
				testCodeFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})

		})
		g.Context("to generate tokens that expire shortly", func() {

			g.BeforeEach(func() {
				accessTokenMaxAgeSeconds = 10
			})

			g.It("works as expected when using a token authorization flow", func() {
				testTokenFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})
			g.It("works as expected when using a code authorization flow", func() {
				testCodeFlow(oc, newRequestTokenOptions, oAuthClientResource, accessTokenMaxAgeSeconds)
			})
		})
	})
})

func testTokenFlow(oc *exutil.CLI, newRequestTokenOptions oauthserver.NewRequestTokenOptionsFunc, client *oauthv1.OAuthClient, expectedExpiresIn int32) {
	// new request token command
	requestTokenOptions := newRequestTokenOptions("testuser", "password")
	// setup for token flow
	requestTokenOptions.TokenFlow = true
	requestTokenOptions.OsinConfig.CodeChallenge = ""
	requestTokenOptions.OsinConfig.CodeChallengeMethod = ""
	requestTokenOptions.OsinConfig.CodeVerifier = ""
	// setup for non-default oauth client
	requestTokenOptions.OsinConfig.ClientId = client.Name
	requestTokenOptions.OsinConfig.RedirectUrl = client.RedirectURIs[0]
	// request token
	token, err := requestTokenOptions.RequestToken()
	o.Expect(err).ToNot(o.HaveOccurred())
	// Make sure we can use the token, and it represents who we expect
	userConfig := *rest.AnonymousClientConfig(oc.AdminConfig())
	userConfig.BearerToken = token
	userClient, err := userv1client.NewForConfig(&userConfig)
	o.Expect(err).ToNot(o.HaveOccurred())
	user, err := userClient.Users().Get(context.Background(), "~", metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(user.Name).To(o.Equal("testuser"))
	// Make sure the token exists with the overridden time
	tokenObj, err := oc.AdminOauthClient().OauthV1().OAuthAccessTokens().Get(context.Background(), toAccessTokenName(token), metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(tokenObj.ExpiresIn).To(o.BeNumerically("==", expectedExpiresIn))
}

func testCodeFlow(oc *exutil.CLI, newRequestTokenOptions oauthserver.NewRequestTokenOptionsFunc, client *oauthv1.OAuthClient, expectedExpiresIn int32) {
	anonymousClientConfig := rest.AnonymousClientConfig(oc.AdminConfig())
	rt, err := rest.TransportFor(anonymousClientConfig)
	o.Expect(err).ToNot(o.HaveOccurred())

	// need to extract the oauth server root url
	oauthServerURL := newRequestTokenOptions("", "").Issuer

	conf := &oauth2.Config{
		ClientID:     client.Name,
		ClientSecret: client.Secret,
		RedirectURL:  client.RedirectURIs[0],
		Endpoint: oauth2.Endpoint{
			AuthURL:  oauthServerURL + "/oauth/authorize",
			TokenURL: oauthServerURL + "/oauth/token",
		},
	}

	// get code
	req, err := http.NewRequest("GET", conf.AuthCodeURL(""), nil)
	o.Expect(err).ToNot(o.HaveOccurred())

	req.SetBasicAuth("testuser", "password")
	resp, err := rt.RoundTrip(req)
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(resp.StatusCode).To(o.Equal(http.StatusFound))

	location, err := resp.Location()
	o.Expect(err).ToNot(o.HaveOccurred())

	code := location.Query().Get("code")
	o.Expect(code).ToNot(o.BeEmpty())

	// Make sure the code exists with the default time
	oauthClientSet := oc.AdminOauthClient()
	codeObj, err := oauthClientSet.OauthV1().OAuthAuthorizeTokens().Get(context.Background(), code, metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(codeObj.ExpiresIn).To(o.BeNumerically("==", 5*60))

	// Use the custom HTTP client when requesting a token.
	httpClient := &http.Client{Transport: rt}
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
	oauthToken, err := conf.Exchange(ctx, code)
	o.Expect(err).ToNot(o.HaveOccurred())
	token := oauthToken.AccessToken

	// Make sure we can use the token, and it represents who we expect
	userConfig := *anonymousClientConfig
	userConfig.BearerToken = token
	userClient, err := userv1client.NewForConfig(&userConfig)
	o.Expect(err).ToNot(o.HaveOccurred())

	user, err := userClient.Users().Get(context.Background(), "~", metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(user.Name).To(o.Equal("testuser"))

	// Make sure the token exists with the overridden time
	tokenObj, err := oauthClientSet.OauthV1().OAuthAccessTokens().Get(context.Background(), toAccessTokenName(token), metav1.GetOptions{})
	o.Expect(err).ToNot(o.HaveOccurred())
	o.Expect(tokenObj.ExpiresIn).To(o.BeNumerically("==", expectedExpiresIn))
}

func toAccessTokenName(token string) string {
	if strings.HasPrefix(token, "sha256:") {
		withoutPrefix := strings.TrimPrefix(token, "sha256:")
		h := sha256.Sum256([]byte(withoutPrefix))
		return "sha256:" + base64.RawURLEncoding.EncodeToString(h[0:])
	}
	return token
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/xyproto/onthefly"
)

const (
	angular_version = "1.3.0-rc.2"
)

// Generate a new onthefly Page (HTML5, Angular and CSS combined)
func indexPage() *onthefly.Page {

	// Create a new HTML5 page, with CSS included
	page := onthefly.NewAngularPage("Demonstration", angular_version)

	// Rely on the body tag being present
	body, _ := page.GetTag("body")

	// Add a title paragraph
	title := body.AddNewTag("p")
	// Use id attributes to style similar elements separately
	title.AddAttrib("id", "title")
	title.AddContent(fmt.Sprintf("onthefly %.1f & angular %s", onthefly.Version, angular_version))
	title.AddStyle("font-size", "2em")
	title.AddStyle("font-family", "sans-serif")
	title.AddStyle("font-style", "italic")

	// Add a paragraph for the angular related tags
	angularp := body.AddNewTag("p")
	angularp.AddAttrib("id", "angular")
	angularp.AddStyle("margin-top", "2em")

	// Label for the input box
	label := angularp.AddNewTag("label")
	inputID := "input1"
	label.AddAttrib("for", inputID)
	label.AddContent("Input text:")
	label.AddStyle("margin-right", "3em")

	// Angular input
	input := angularp.AddNewTag("input")
	input.AddAttrib("id", inputID)
	input.AddAttrib("type", "text")
	dataBindingName := "sometext"
	input.AddAttrib("ng-model", dataBindingName)

	// Angular output
	h1 := angularp.AddNewTag("h1")
	h1.AddAttrib("ng-show", dataBindingName)
	h1.AddContent("HELLO {{ " + dataBindingName + " | uppercase }}")
	h1.AddStyle("color", "red")
	h1.AddStyle("margin", "2em")
	h1.AddStyle("font-size", "4em")
	h1.AddStyle("font-family", "courier")

	// Set the margin (em is default)
	page.SetMargin(5)

	// Set the font family
	page.SetFontFamily("serif")

	// Set the color scheme (fg, bg)
	page.SetColor("black", "#e0e0e0")

	return page
}

// Set up the paths and handlers then start serving.
func main() {
	fmt.Println("onthefly ", onthefly.Version)

	// Create a Negroni instance and a ServeMux instance
	n := negroni.Classic()
	mux := http.NewServeMux()

	// Publish the generated Page in a way that connects the HTML and CSS. Cached.
	indexPage().Publish(mux, "/", "/style.css", false)

	// Handler goes last
	n.UseHandler(mux)

	// Listen for requests at port 8080
	n.Run(":8080")
}
<commit_msg>Port 9000<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/codegangsta/negroni"
	"github.com/xyproto/onthefly"
)

const (
	angular_version = "1.3.0-rc.2"
)

// Generate a new onthefly Page (HTML5, Angular and CSS combined)
func indexPage() *onthefly.Page {

	// Create a new HTML5 page, with CSS included
	page := onthefly.NewAngularPage("Demonstration", angular_version)

	// Rely on the body tag being present
	body, _ := page.GetTag("body")

	// Add a title paragraph
	title := body.AddNewTag("p")
	// Use id attributes to style similar tags separately
	title.AddAttrib("id", "title")
	title.AddContent(fmt.Sprintf("onthefly %.1f & angular %s", onthefly.Version, angular_version))
	title.AddStyle("font-size", "2em")
	title.AddStyle("font-family", "sans-serif")
	title.AddStyle("font-style", "italic")

	// Add a paragraph for the angular related tags
	angularp := body.AddNewTag("p")
	angularp.AddAttrib("id", "angular")
	angularp.AddStyle("margin-top", "2em")

	// Label for the input box
	label := angularp.AddNewTag("label")
	inputID := "input1"
	label.AddAttrib("for", inputID)
	label.AddContent("Input text:")
	label.AddStyle("margin-right", "3em")

	// Angular input
	input := angularp.AddNewTag("input")
	input.AddAttrib("id", inputID)
	input.AddAttrib("type", "text")
	dataBindingName := "sometext"
	input.AddAttrib("ng-model", dataBindingName)

	// Angular output
	h1 := angularp.AddNewTag("h1")
	h1.AddAttrib("ng-show", dataBindingName)
	h1.AddContent("HELLO {{ " + dataBindingName + " | uppercase }}")
	h1.AddStyle("color", "red")
	h1.AddStyle("margin", "2em")
	h1.AddStyle("font-size", "4em")
	h1.AddStyle("font-family", "courier")

	// Set the margin (em is default)
	page.SetMargin(5)

	// Set the font family
	page.SetFontFamily("serif")

	// Set the color scheme (fg, bg)
	page.SetColor("black", "#e0e0e0")

	return page
}

// Set up the paths and handlers then start serving.
func main() {
	fmt.Println("onthefly ", onthefly.Version)

	// Create a Negroni instance and a ServeMux instance
	n := negroni.Classic()
	mux := http.NewServeMux()

	// Publish the generated Page in a way that connects the HTML and CSS. Cached.
	indexPage().Publish(mux, "/", "/style.css", false)

	// Handler goes last
	n.UseHandler(mux)

	// Listen for requests at port 9000
	n.Run(":9000")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"time"

	"summercat.com/irc"
)

// Client holds state about a single client connection.
// All clients are in this state until they register as either a user client
// or as a server.
type Client struct {
	// Conn holds the TCP connection to the client.
	Conn Conn

	// WriteChan is the channel to send to to write to the client.
	WriteChan chan irc.Message

	// A unique id. Internal to this server only.
	ID uint64

	ConnectionStartTime time.Time

	// Server references the main server the client is connected to (local
	// client).
	// It's helpful to have to avoid passing server all over the place.
	Server *Server

	// Track if we overflow our send queue. If we do, we'll kill the client.
	SendQueueExceeded bool

	// Info client may send us before we complete its registration and promote it
	// to UserClient or ServerClient.

	// User info

	// NICK
	PreRegDisplayNick string

	// USER
	PreRegUser     string
	PreRegRealName string

	// Server info

	// PASS
	PreRegPass   string
	PreRegTS6SID string

	// CAPAB
	PreRegCapabs map[string]struct{}

	// SERVER
	PreRegServerName string
	PreRegServerDesc string

	// Boolean flags involved in the several step server link process.
	// Use them to keep track of where we are in the process.

	GotPASS   bool
	GotCAPAB  bool
	GotSERVER bool

	SentPASS   bool
	SentCAPAB  bool
	SentSERVER bool
	SentSVINFO bool
}

// NewClient creates a Client
func NewClient(s *Server, id uint64, conn net.Conn) *Client {
	return &Client{
		Conn: NewConn(conn, s.Config.DeadTime),

		// Buffered channel. We don't want to block sending to the client from the
		// server. The client may be stuck. Make the buffer large enough that it
		// should only max out in case of connection issues.
		WriteChan: make(chan irc.Message, 32768),

		ID:                  id,
		ConnectionStartTime: time.Now(),
		Server:              s,

		PreRegCapabs: make(map[string]struct{}),
	}
}

func (c *Client) String() string {
	return fmt.Sprintf("%d %s", c.ID, c.Conn.RemoteAddr())
}

// readLoop endlessly reads from the client's TCP connection. It parses each
// IRC protocol message and passes it to the server through the server's
// channel.
func (c *Client) readLoop() {
	defer c.Server.WG.Done()

	for {
		if c.Server.isShuttingDown() {
			break
		}

		// This means if a client sends us an invalid message that we cut them off.
		message, err := c.Conn.ReadMessage()
		if err != nil {
			log.Printf("Client %s: %s", c, err)
			c.Server.newEvent(Event{Type: DeadClientEvent, Client: c})
			break
		}

		c.Server.newEvent(Event{
			Type:    MessageFromClientEvent,
			Client:  c,
			Message: message,
		})
	}

	log.Printf("Client %s: Reader shutting down.", c)
}

// writeLoop endlessly reads from the client's channel, encodes each message,
// and writes it to the client's TCP connection.
//
// When the channel is closed, or if we have a write error, close the TCP
// connection. I have this here so that we try to deliver messages to the
// client before closing its socket and giving up.
func (c *Client) writeLoop() {
	defer c.Server.WG.Done()

	// Receive on the client's write channel.
	//
	// Ensure we also stop if the server is shutting down (indicated by the
	// ShutdownChan being closed). If we don't, then there is potential for us to
	// leak this goroutine. Consider the case where we have a new client, and
	// tell the server about it, but the server is shutting down, and so does not
	// see the new client event. In this case the server does not know that it
	// must close the write channel so that the client will end (if we were for
	// example using 'for message := range c.WriteChan', as it would block
	// forever).
	//
	// A problem with this is we are not guaranteed to process any remaining
	// messages on the write channel (and so inform the client about shutdown)
	// when we are shutting down. But it is an improvement on leaking the
	// goroutine.
Loop:
	for {
		select {
		case message := <-c.WriteChan:
			err := c.Conn.WriteMessage(message)
			if err != nil {
				log.Printf("Client %s: %s", c, err)
				c.Server.newEvent(Event{Type: DeadClientEvent, Client: c})
				break Loop
			}
		case <-c.Server.ShutdownChan:
			break Loop
		}
	}

	err := c.Conn.Close()
	if err != nil {
		log.Printf("Client %s: Problem closing connection: %s", c, err)
	}

	log.Printf("Client %s: Writer shutting down.", c)
}

// quit means the client is quitting. Tell it why and clean up.
func (c *Client) quit(msg string) {
	c.messageFromServer("ERROR", []string{msg})
	close(c.WriteChan)

	if len(c.PreRegDisplayNick) > 0 {
		delete(c.Server.Nicks, canonicalizeNick(c.PreRegDisplayNick))
	}

	delete(c.Server.UnregisteredClients, c.ID)
}

func (c *Client) handleMessage(m irc.Message) {
	// Clients SHOULD NOT (section 2.3) send a prefix.
	if m.Prefix != "" {
		c.quit("No prefix permitted")
		return
	}

	// Non-RFC command that appears to be widely supported. Just ignore it for
	// now.
	if m.Command == "CAP" {
		return
	}

	// We may receive NOTICE when initiating connection to a server. Ignore it.
	if m.Command == "NOTICE" {
		return
	}

	// To register as a user client:
	// NICK
	// USER

	if m.Command == "NICK" {
		c.nickCommand(m)
		return
	}

	if m.Command == "USER" {
		c.userCommand(m)
		return
	}

	// To register as a server (using TS6):

	// If incoming client is initiator, they send this:

	// > PASS
	// > CAPAB
	// > SERVER

	// We check this info. If valid, reply:

	// < PASS
	// < CAPAB
	// < SERVER

	// They check our info. If valid, reply:

	// > SVINFO

	// We reply again:

	// < SVINFO
	// < Burst
	// < PING

	// They finish:

	// > Burst
	// > PING

	// Everyone ACKs the PINGs:

	// < PONG

	// > PONG

	// PINGs are used to know end of burst. Then we're linked.

	// If we initiate the link, then we send PASS/CAPAB/SERVER and expect it
	// in return. Beyond that, the process is the same.

	if m.Command == "PASS" {
		c.passCommand(m)
		return
	}

	if m.Command == "CAPAB" {
		c.capabCommand(m)
		return
	}

	if m.Command == "SERVER" {
		c.serverCommand(m)
		return
	}

	if m.Command == "SVINFO" {
		c.svinfoCommand(m)
		return
	}

	if m.Command == "ERROR" {
		c.errorCommand(m)
		return
	}

	// Let's say *all* other commands require you to be registered.
	// 451 ERR_NOTREGISTERED
	c.messageFromServer("451", []string{fmt.Sprintf("You have not registered.")})
}

func (c *Client) completeRegistration() {
	// RFC 2813 specifies messages to send upon registration.

	uc := NewUserClient(c)

	delete(c.Server.UnregisteredClients, uc.ID)
	c.Server.UserClients[c.ID] = uc

	// 001 RPL_WELCOME
	uc.messageFromServer("001", []string{
		fmt.Sprintf("Welcome to the Internet Relay Network %s",
			uc.nickUhost()),
	})

	// 002 RPL_YOURHOST
	uc.messageFromServer("002", []string{
		fmt.Sprintf("Your host is %s, running version %s",
			uc.Server.Config.ServerName,
			uc.Server.Config.Version),
	})

	// 003 RPL_CREATED
	uc.messageFromServer("003", []string{
		fmt.Sprintf("This server was created %s", uc.Server.Config.CreatedDate),
	})

	// 004 RPL_MYINFO
	// <servername> <version> <available user modes> <available channel modes>
	uc.messageFromServer("004", []string{
		// It seems ambiguous if these are to be separate parameters.
		uc.Server.Config.ServerName,
		uc.Server.Config.Version,
		"o",
		"n",
	})

	uc.lusersCommand()
	uc.motdCommand()
}

// Send an IRC message to a client. Appears to be from the server.
// This works by writing to a client's channel.
//
// Note: Only the server goroutine should call this (due to channel use).
func (c *Client) messageFromServer(command string, params []string) {
	// For numeric messages, we need to prepend the nick.
	// Use * for the nick in cases where the client doesn't have one yet.
	// This is what ircd-ratbox does. Maybe not RFC...
	if isNumericCommand(command) {
		nick := "*"
		if len(c.PreRegDisplayNick) > 0 {
			nick = c.PreRegDisplayNick
		}
		newParams := []string{nick}
		newParams = append(newParams, params...)
		params = newParams
	}

	c.maybeQueueMessage(irc.Message{
		Prefix:  c.Server.Config.ServerName,
		Command: command,
		Params:  params,
	})
}

// Send a message to the client. We send it to its write channel, which in turn
// leads to writing it to its TCP socket.
//
// This function won't block. If the client's queue is full, we flag it as
// having a full send queue.
//
// Not blocking is important because the server sends the client messages this
// way, and if we block on a problem client, everything would grind to a halt.
func (c *Client) maybeQueueMessage(m irc.Message) {
	if c.SendQueueExceeded {
		return
	}

	select {
	case c.WriteChan <- m:
	default:
		c.SendQueueExceeded = true
	}
}

func (c *Client) sendPASS(pass string) {
	// PASS <password>, TS, <ts version>, <SID>
	c.maybeQueueMessage(irc.Message{
		Command: "PASS",
		Params:  []string{pass, "TS", "6", c.Server.Config.TS6SID},
	})

	c.SentPASS = true
}

func (c *Client) sendCAPAB() {
	// CAPAB <space separated list>
	c.maybeQueueMessage(irc.Message{
		Command: "CAPAB",
		Params:  []string{"QS ENCAP"},
	})

	c.SentCAPAB = true
}

func (c *Client) sendSERVER() {
	// SERVER <name> <hopcount> <description>
	c.maybeQueueMessage(irc.Message{
		Command: "SERVER",
		Params: []string{
			c.Server.Config.ServerName,
			"1",
			c.Server.Config.ServerInfo,
		},
	})

	c.SentSERVER = true
}

func (c *Client) sendSVINFO() {
	// SVINFO <TS version> <min TS version> 0 <current time>
	epoch := time.Now().Unix()
	c.maybeQueueMessage(irc.Message{
		Command: "SVINFO",
		Params: []string{
			"6", "6", "0", fmt.Sprintf("%d", epoch),
		},
	})

	c.SentSVINFO = true
}

func (c *Client) registerServer() {
	// Possible it took a NICK... Doesn't make sense for it to do so, but since
	// it's been unregistered until now, a malicious server could have taken a
	// nick.
	if len(c.PreRegDisplayNick) > 0 {
		delete(c.Server.Nicks, canonicalizeNick(c.PreRegDisplayNick))
	}

	s := NewServerClient(c)

	delete(c.Server.UnregisteredClients, s.ID)
	c.Server.ServerClients[s.ID] = s
	c.Server.Servers[s.Name] = s.ID

	s.Server.noticeOpers(fmt.Sprintf("Established link to %s.", s.Name))

	s.sendBurst()
	s.sendPING()
}

func (c *Client) isSendQueueExceeded() bool {
	return c.SendQueueExceeded
}
<commit_msg>ircd: If client write channel is closed, end writer<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"time"

	"summercat.com/irc"
)

// Client holds state about a single client connection.
// All clients are in this state until they register as either a user client
// or as a server.
type Client struct {
	// Conn holds the TCP connection to the client.
	Conn Conn

	// WriteChan is the channel to send to to write to the client.
	WriteChan chan irc.Message

	// A unique id. Internal to this server only.
	ID uint64

	ConnectionStartTime time.Time

	// Server references the main server the client is connected to (local
	// client).
	// It's helpful to have to avoid passing server all over the place.
	Server *Server

	// Track if we overflow our send queue. If we do, we'll kill the client.
	SendQueueExceeded bool

	// Info client may send us before we complete its registration and promote it
	// to UserClient or ServerClient.

	// User info

	// NICK
	PreRegDisplayNick string

	// USER
	PreRegUser     string
	PreRegRealName string

	// Server info

	// PASS
	PreRegPass   string
	PreRegTS6SID string

	// CAPAB
	PreRegCapabs map[string]struct{}

	// SERVER
	PreRegServerName string
	PreRegServerDesc string

	// Boolean flags involved in the several step server link process.
	// Use them to keep track of where we are in the process.

	GotPASS   bool
	GotCAPAB  bool
	GotSERVER bool

	SentPASS   bool
	SentCAPAB  bool
	SentSERVER bool
	SentSVINFO bool
}

// NewClient creates a Client
func NewClient(s *Server, id uint64, conn net.Conn) *Client {
	return &Client{
		Conn: NewConn(conn, s.Config.DeadTime),

		// Buffered channel. We don't want to block sending to the client from the
		// server. The client may be stuck. Make the buffer large enough that it
		// should only max out in case of connection issues.
		WriteChan: make(chan irc.Message, 32768),

		ID:                  id,
		ConnectionStartTime: time.Now(),
		Server:              s,

		PreRegCapabs: make(map[string]struct{}),
	}
}

func (c *Client) String() string {
	return fmt.Sprintf("%d %s", c.ID, c.Conn.RemoteAddr())
}

// readLoop endlessly reads from the client's TCP connection. It parses each
// IRC protocol message and passes it to the server through the server's
// channel.
func (c *Client) readLoop() {
	defer c.Server.WG.Done()

	for {
		if c.Server.isShuttingDown() {
			break
		}

		// This means if a client sends us an invalid message that we cut them off.
		message, err := c.Conn.ReadMessage()
		if err != nil {
			log.Printf("Client %s: %s", c, err)
			c.Server.newEvent(Event{Type: DeadClientEvent, Client: c})
			break
		}

		c.Server.newEvent(Event{
			Type:    MessageFromClientEvent,
			Client:  c,
			Message: message,
		})
	}

	log.Printf("Client %s: Reader shutting down.", c)
}

// writeLoop endlessly reads from the client's channel, encodes each message,
// and writes it to the client's TCP connection.
//
// When the channel is closed, or if we have a write error, close the TCP
// connection. I have this here so that we try to deliver messages to the
// client before closing its socket and giving up.
func (c *Client) writeLoop() {
	defer c.Server.WG.Done()

	// Receive on the client's write channel.
	//
	// Ensure we also stop if the server is shutting down (indicated by the
	// ShutdownChan being closed). If we don't, then there is potential for us to
	// leak this goroutine. Consider the case where we have a new client, and
	// tell the server about it, but the server is shutting down, and so does not
	// see the new client event. In this case the server does not know that it
	// must close the write channel so that the client will end (if we were for
	// example using 'for message := range c.WriteChan', as it would block
	// forever).
	//
	// A problem with this is we are not guaranteed to process any remaining
	// messages on the write channel (and so inform the client about shutdown)
	// when we are shutting down. But it is an improvement on leaking the
	// goroutine.
Loop:
	for {
		select {
		case message, ok := <-c.WriteChan:
			if !ok {
				break Loop
			}

			err := c.Conn.WriteMessage(message)
			if err != nil {
				log.Printf("Client %s: %s", c, err)
				c.Server.newEvent(Event{Type: DeadClientEvent, Client: c})
				break Loop
			}
		case <-c.Server.ShutdownChan:
			break Loop
		}
	}

	err := c.Conn.Close()
	if err != nil {
		log.Printf("Client %s: Problem closing connection: %s", c, err)
	}

	log.Printf("Client %s: Writer shutting down.", c)
}

// quit means the client is quitting. Tell it why and clean up.
func (c *Client) quit(msg string) {
	c.messageFromServer("ERROR", []string{msg})
	close(c.WriteChan)

	if len(c.PreRegDisplayNick) > 0 {
		delete(c.Server.Nicks, canonicalizeNick(c.PreRegDisplayNick))
	}

	delete(c.Server.UnregisteredClients, c.ID)
}

func (c *Client) handleMessage(m irc.Message) {
	// Clients SHOULD NOT (section 2.3) send a prefix.
	if m.Prefix != "" {
		c.quit("No prefix permitted")
		return
	}

	// Non-RFC command that appears to be widely supported. Just ignore it for
	// now.
	if m.Command == "CAP" {
		return
	}

	// We may receive NOTICE when initiating connection to a server. Ignore it.
	if m.Command == "NOTICE" {
		return
	}

	// To register as a user client:
	// NICK
	// USER

	if m.Command == "NICK" {
		c.nickCommand(m)
		return
	}

	if m.Command == "USER" {
		c.userCommand(m)
		return
	}

	// To register as a server (using TS6):

	// If incoming client is initiator, they send this:

	// > PASS
	// > CAPAB
	// > SERVER

	// We check this info. If valid, reply:

	// < PASS
	// < CAPAB
	// < SERVER

	// They check our info. If valid, reply:

	// > SVINFO

	// We reply again:

	// < SVINFO
	// < Burst
	// < PING

	// They finish:

	// > Burst
	// > PING

	// Everyone ACKs the PINGs:

	// < PONG

	// > PONG

	// PINGs are used to know end of burst. Then we're linked.

	// If we initiate the link, then we send PASS/CAPAB/SERVER and expect it
	// in return. Beyond that, the process is the same.

	if m.Command == "PASS" {
		c.passCommand(m)
		return
	}

	if m.Command == "CAPAB" {
		c.capabCommand(m)
		return
	}

	if m.Command == "SERVER" {
		c.serverCommand(m)
		return
	}

	if m.Command == "SVINFO" {
		c.svinfoCommand(m)
		return
	}

	if m.Command == "ERROR" {
		c.errorCommand(m)
		return
	}

	// Let's say *all* other commands require you to be registered.
	// 451 ERR_NOTREGISTERED
	c.messageFromServer("451", []string{fmt.Sprintf("You have not registered.")})
}

func (c *Client) completeRegistration() {
	// RFC 2813 specifies messages to send upon registration.

	uc := NewUserClient(c)

	delete(c.Server.UnregisteredClients, uc.ID)
	c.Server.UserClients[c.ID] = uc

	// 001 RPL_WELCOME
	uc.messageFromServer("001", []string{
		fmt.Sprintf("Welcome to the Internet Relay Network %s",
			uc.nickUhost()),
	})

	// 002 RPL_YOURHOST
	uc.messageFromServer("002", []string{
		fmt.Sprintf("Your host is %s, running version %s",
			uc.Server.Config.ServerName,
			uc.Server.Config.Version),
	})

	// 003 RPL_CREATED
	uc.messageFromServer("003", []string{
		fmt.Sprintf("This server was created %s", uc.Server.Config.CreatedDate),
	})

	// 004 RPL_MYINFO
	// <servername> <version> <available user modes> <available channel modes>
	uc.messageFromServer("004", []string{
		// It seems ambiguous if these are to be separate parameters.
		uc.Server.Config.ServerName,
		uc.Server.Config.Version,
		"o",
		"n",
	})

	uc.lusersCommand()
	uc.motdCommand()
}

// Send an IRC message to a client. Appears to be from the server.
// This works by writing to a client's channel.
//
// Note: Only the server goroutine should call this (due to channel use).
func (c *Client) messageFromServer(command string, params []string) {
	// For numeric messages, we need to prepend the nick.
	// Use * for the nick in cases where the client doesn't have one yet.
	// This is what ircd-ratbox does. Maybe not RFC...
	if isNumericCommand(command) {
		nick := "*"
		if len(c.PreRegDisplayNick) > 0 {
			nick = c.PreRegDisplayNick
		}
		newParams := []string{nick}
		newParams = append(newParams, params...)
		params = newParams
	}

	c.maybeQueueMessage(irc.Message{
		Prefix:  c.Server.Config.ServerName,
		Command: command,
		Params:  params,
	})
}

// Send a message to the client. We send it to its write channel, which in turn
// leads to writing it to its TCP socket.
//
// This function won't block. If the client's queue is full, we flag it as
// having a full send queue.
//
// Not blocking is important because the server sends the client messages this
// way, and if we block on a problem client, everything would grind to a halt.
func (c *Client) maybeQueueMessage(m irc.Message) {
	if c.SendQueueExceeded {
		return
	}

	select {
	case c.WriteChan <- m:
	default:
		c.SendQueueExceeded = true
	}
}

func (c *Client) sendPASS(pass string) {
	// PASS <password>, TS, <ts version>, <SID>
	c.maybeQueueMessage(irc.Message{
		Command: "PASS",
		Params:  []string{pass, "TS", "6", c.Server.Config.TS6SID},
	})

	c.SentPASS = true
}

func (c *Client) sendCAPAB() {
	// CAPAB <space separated list>
	c.maybeQueueMessage(irc.Message{
		Command: "CAPAB",
		Params:  []string{"QS ENCAP"},
	})

	c.SentCAPAB = true
}

func (c *Client) sendSERVER() {
	// SERVER <name> <hopcount> <description>
	c.maybeQueueMessage(irc.Message{
		Command: "SERVER",
		Params: []string{
			c.Server.Config.ServerName,
			"1",
			c.Server.Config.ServerInfo,
		},
	})

	c.SentSERVER = true
}

func (c *Client) sendSVINFO() {
	// SVINFO <TS version> <min TS version> 0 <current time>
	epoch := time.Now().Unix()
	c.maybeQueueMessage(irc.Message{
		Command: "SVINFO",
		Params: []string{
			"6", "6", "0", fmt.Sprintf("%d", epoch),
		},
	})

	c.SentSVINFO = true
}

func (c *Client) registerServer() {
	// Possible it took a NICK... Doesn't make sense for it to do so, but since
	// it's been unregistered until now, a malicious server could have taken a
	// nick.
	if len(c.PreRegDisplayNick) > 0 {
		delete(c.Server.Nicks, canonicalizeNick(c.PreRegDisplayNick))
	}

	s := NewServerClient(c)

	delete(c.Server.UnregisteredClients, s.ID)
	c.Server.ServerClients[s.ID] = s
	c.Server.Servers[s.Name] = s.ID

	s.Server.noticeOpers(fmt.Sprintf("Established link to %s.", s.Name))

	s.sendBurst()
	s.sendPING()
}

func (c *Client) isSendQueueExceeded() bool {
	return c.SendQueueExceeded
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI 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 main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/api/config"
	apicontext "github.com/docker/compose-cli/api/context"
	"github.com/docker/compose-cli/api/context/store"
	"github.com/docker/compose-cli/api/errdefs"
	"github.com/docker/compose-cli/cli/cmd"
	"github.com/docker/compose-cli/cli/cmd/compose"
	contextcmd "github.com/docker/compose-cli/cli/cmd/context"
	"github.com/docker/compose-cli/cli/cmd/login"
	"github.com/docker/compose-cli/cli/cmd/logout"
	"github.com/docker/compose-cli/cli/cmd/run"
	"github.com/docker/compose-cli/cli/cmd/volume"
	"github.com/docker/compose-cli/cli/metrics"
	"github.com/docker/compose-cli/cli/mobycli"
	cliopts "github.com/docker/compose-cli/cli/options"

	cliflags "github.com/docker/cli/cli/flags"

	// Backend registrations
	_ "github.com/docker/compose-cli/aci"
	_ "github.com/docker/compose-cli/ecs"
	_ "github.com/docker/compose-cli/ecs/local"
	_ "github.com/docker/compose-cli/local"
)

var (
	contextAgnosticCommands = map[string]struct{}{
		"compose":          {},
		"context":          {},
		"login":            {},
		"logout":           {},
		"serve":            {},
		"version":          {},
		"backend-metadata": {},
	}
	unknownCommandRegexp = regexp.MustCompile(`unknown command "([^"]*)"`)
)

func init() {
	// initial hack to get the path of the project's bin dir
	// into the env of this cli for development
	path, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fatal(errors.Wrap(err, "unable to get absolute bin path"))
	}

	if err := os.Setenv("PATH", appendPaths(os.Getenv("PATH"), path)); err != nil {
		panic(err)
	}
	// Seed random
	rand.Seed(time.Now().UnixNano())
}

func appendPaths(envPath string, path string) string {
	if envPath == "" {
		return path
	}
	return strings.Join([]string{envPath, path}, string(os.PathListSeparator))
}

func isContextAgnosticCommand(cmd *cobra.Command) bool {
	if cmd == nil {
		return false
	}
	if _, ok := contextAgnosticCommands[cmd.Name()]; ok {
		return true
	}
	return isContextAgnosticCommand(cmd.Parent())
}

func main() {
	var opts cliopts.GlobalOpts
	root := &cobra.Command{
		Use:              "docker",
		SilenceErrors:    true,
		SilenceUsage:     true,
		TraverseChildren: true,
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if !isContextAgnosticCommand(cmd) {
				mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return cmd.Help()
			}
			return fmt.Errorf("unknown command %q", args[0])
		},
	}

	root.AddCommand(
		contextcmd.Command(),
		cmd.PsCommand(),
		cmd.ServeCommand(),
		cmd.ExecCommand(),
		cmd.LogsCommand(),
		cmd.RmCommand(),
		cmd.StartCommand(),
		cmd.InspectCommand(),
		login.Command(),
		logout.Command(),
		cmd.VersionCommand(),
		cmd.StopCommand(),
		cmd.KillCommand(),
		cmd.SecretCommand(),
		cmd.PruneCommand(),
		cmd.MetadataCommand(),

		// Place holders
		cmd.EcsCommand(),
	)

	helpFunc := root.HelpFunc()
	root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		if !isContextAgnosticCommand(cmd) {
			mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
		}
		helpFunc(cmd, args)
	})

	flags := root.Flags()
	opts.InstallFlags(flags)
	opts.AddConfigFlags(flags)
	flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")

	flags.SetInterspersed(false)

	walk(root, func(c *cobra.Command) {
		c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	})

	// populate the opts with the global flags
	flags.Parse(os.Args[1:]) //nolint: errcheck

	level, err := logrus.ParseLevel(opts.LogLevel)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", opts.LogLevel)
		os.Exit(1)
	}
	logrus.SetFormatter(&logrus.TextFormatter{
		DisableTimestamp:       true,
		DisableLevelTruncation: true,
	})
	logrus.SetLevel(level)
	if opts.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	ctx, cancel := newSigContext()
	defer cancel()

	// --version should immediately be forwarded to the original cli
	if opts.Version {
		mobycli.Exec(root)
	}

	if opts.Config == "" {
		fatal(errors.New("config path cannot be empty"))
	}
	configDir := opts.Config
	ctx = config.WithDir(ctx, configDir)

	currentContext := determineCurrentContext(opts.Context, configDir)

	s, err := store.New(configDir)
	if err != nil {
		mobycli.Exec(root)
	}

	ctype := store.DefaultContextType
	cc, _ := s.Get(currentContext)
	if cc != nil {
		ctype = cc.Type()
	}

	root.AddCommand(
		run.Command(ctype),
		compose.Command(ctype),
		volume.Command(ctype),
	)
	if ctype == store.DefaultContextType || ctype == store.LocalContextType {
		if len(opts.Hosts) > 0 {
			opts.Context = ""
			currentContext = "default"
		}

		cnxOptions := cliflags.CommonOptions{
			Context:   opts.Context,
			Debug:     opts.Debug,
			Hosts:     opts.Hosts,
			LogLevel:  opts.LogLevel,
			TLS:       opts.TLS,
			TLSVerify: opts.TLSVerify,
		}

		if opts.TLSVerify {
			cnxOptions.TLSOptions = opts.TLSOptions
		}
		ctx = apicontext.WithCliOptions(ctx, cnxOptions)
	}
	ctx = apicontext.WithCurrentContext(ctx, currentContext)
	ctx = store.WithContextStore(ctx, s)

	if err = root.ExecuteContext(ctx); err != nil {
		handleError(ctx, err, ctype, currentContext, cc, root)
	}
	metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
}

func handleError(ctx context.Context, err error, ctype string, currentContext string, cc *store.DockerContext, root *cobra.Command) {
	// if user canceled request, simply exit without any error message
	if errdefs.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
		metrics.Track(ctype, os.Args[1:], metrics.CanceledStatus)
		os.Exit(130)
	}
	if ctype == store.AwsContextType {
		exit(currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
$ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
	}

	// Context should always be handled by new CLI
	requiredCmd, _, _ := root.Find(os.Args[1:])
	if requiredCmd != nil && isContextAgnosticCommand(requiredCmd) {
		exit(currentContext, err, ctype)
	}
	mobycli.ExecIfDefaultCtxType(ctx, root)

	checkIfUnknownCommandExistInDefaultContext(err, currentContext, ctype)

	exit(currentContext, err, ctype)
}

func exit(ctx string, err error, ctype string) {
	if exit, ok := err.(cmd.ExitCodeError); ok {
		metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
		os.Exit(exit.ExitCode)
	}

	metrics.Track(ctype, os.Args[1:], metrics.FailureStatus)

	if errors.Is(err, errdefs.ErrLoginRequired) {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(errdefs.ExitCodeLoginRequired)
	}

	if compose.Warning != "" {
		fmt.Fprintln(os.Stderr, compose.Warning)
	}

	if errors.Is(err, errdefs.ErrNotImplemented) {
		name := metrics.GetCommand(os.Args[1:])
		fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)

		os.Exit(1)
	}

	fatal(err)
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string, contextType string) {
	submatch := unknownCommandRegexp.FindSubmatch([]byte(err.Error()))
	if len(submatch) == 2 {
		dockerCommand := string(submatch[1])

		if mobycli.IsDefaultContextCommand(dockerCommand) {
			fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
			metrics.Track(contextType, os.Args[1:], metrics.FailureStatus)
			os.Exit(1)
		}
	}
}

func newSigContext() (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-s
		cancel()
	}()
	return ctx, cancel
}

func determineCurrentContext(flag string, configDir string) string {
	res := flag
	if res == "" {
		config, err := config.LoadFile(configDir)
		if err != nil {
			fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
			return "default"
		}
		res = config.CurrentContext
	}
	if res == "" {
		res = "default"
	}
	return res
}

func walk(c *cobra.Command, f func(*cobra.Command)) {
	f(c)
	for _, c := range c.Commands() {
		walk(c, f)
	}
}
<commit_msg>Check -H flags and DOCKER_HOST/DOCKER_CONTEXT vars when determining current context<commit_after>/*
   Copyright 2020 Docker Compose CLI 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 main

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/api/config"
	apicontext "github.com/docker/compose-cli/api/context"
	"github.com/docker/compose-cli/api/context/store"
	"github.com/docker/compose-cli/api/errdefs"
	"github.com/docker/compose-cli/cli/cmd"
	"github.com/docker/compose-cli/cli/cmd/compose"
	contextcmd "github.com/docker/compose-cli/cli/cmd/context"
	"github.com/docker/compose-cli/cli/cmd/login"
	"github.com/docker/compose-cli/cli/cmd/logout"
	"github.com/docker/compose-cli/cli/cmd/run"
	"github.com/docker/compose-cli/cli/cmd/volume"
	"github.com/docker/compose-cli/cli/metrics"
	"github.com/docker/compose-cli/cli/mobycli"
	cliopts "github.com/docker/compose-cli/cli/options"

	cliflags "github.com/docker/cli/cli/flags"

	// Backend registrations
	_ "github.com/docker/compose-cli/aci"
	_ "github.com/docker/compose-cli/ecs"
	_ "github.com/docker/compose-cli/ecs/local"
	_ "github.com/docker/compose-cli/local"
)

var (
	contextAgnosticCommands = map[string]struct{}{
		"compose":          {},
		"context":          {},
		"login":            {},
		"logout":           {},
		"serve":            {},
		"version":          {},
		"backend-metadata": {},
	}
	unknownCommandRegexp = regexp.MustCompile(`unknown command "([^"]*)"`)
)

func init() {
	// initial hack to get the path of the project's bin dir
	// into the env of this cli for development
	path, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fatal(errors.Wrap(err, "unable to get absolute bin path"))
	}

	if err := os.Setenv("PATH", appendPaths(os.Getenv("PATH"), path)); err != nil {
		panic(err)
	}
	// Seed random
	rand.Seed(time.Now().UnixNano())
}

func appendPaths(envPath string, path string) string {
	if envPath == "" {
		return path
	}
	return strings.Join([]string{envPath, path}, string(os.PathListSeparator))
}

func isContextAgnosticCommand(cmd *cobra.Command) bool {
	if cmd == nil {
		return false
	}
	if _, ok := contextAgnosticCommands[cmd.Name()]; ok {
		return true
	}
	return isContextAgnosticCommand(cmd.Parent())
}

func main() {
	var opts cliopts.GlobalOpts
	root := &cobra.Command{
		Use:              "docker",
		SilenceErrors:    true,
		SilenceUsage:     true,
		TraverseChildren: true,
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			if !isContextAgnosticCommand(cmd) {
				mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
			}
			return nil
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) == 0 {
				return cmd.Help()
			}
			return fmt.Errorf("unknown command %q", args[0])
		},
	}

	root.AddCommand(
		contextcmd.Command(),
		cmd.PsCommand(),
		cmd.ServeCommand(),
		cmd.ExecCommand(),
		cmd.LogsCommand(),
		cmd.RmCommand(),
		cmd.StartCommand(),
		cmd.InspectCommand(),
		login.Command(),
		logout.Command(),
		cmd.VersionCommand(),
		cmd.StopCommand(),
		cmd.KillCommand(),
		cmd.SecretCommand(),
		cmd.PruneCommand(),
		cmd.MetadataCommand(),

		// Place holders
		cmd.EcsCommand(),
	)

	helpFunc := root.HelpFunc()
	root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
		if !isContextAgnosticCommand(cmd) {
			mobycli.ExecIfDefaultCtxType(cmd.Context(), cmd.Root())
		}
		helpFunc(cmd, args)
	})

	flags := root.Flags()
	opts.InstallFlags(flags)
	opts.AddConfigFlags(flags)
	flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")

	flags.SetInterspersed(false)

	walk(root, func(c *cobra.Command) {
		c.Flags().BoolP("help", "h", false, "Help for "+c.Name())
	})

	// populate the opts with the global flags
	flags.Parse(os.Args[1:]) //nolint: errcheck

	level, err := logrus.ParseLevel(opts.LogLevel)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", opts.LogLevel)
		os.Exit(1)
	}
	logrus.SetFormatter(&logrus.TextFormatter{
		DisableTimestamp:       true,
		DisableLevelTruncation: true,
	})
	logrus.SetLevel(level)
	if opts.Debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	ctx, cancel := newSigContext()
	defer cancel()

	// --version should immediately be forwarded to the original cli
	if opts.Version {
		mobycli.Exec(root)
	}

	if opts.Config == "" {
		fatal(errors.New("config path cannot be empty"))
	}

	configDir := opts.Config
	ctx = config.WithDir(ctx, configDir)

	currentContext := determineCurrentContext(opts.Context, configDir, opts.Hosts)

	s, err := store.New(configDir)
	if err != nil {
		mobycli.Exec(root)
	}

	ctype := store.DefaultContextType
	cc, _ := s.Get(currentContext)
	if cc != nil {
		ctype = cc.Type()
	}

	root.AddCommand(
		run.Command(ctype),
		compose.Command(ctype),
		volume.Command(ctype),
	)
	if ctype == store.DefaultContextType || ctype == store.LocalContextType {
		cnxOptions := cliflags.CommonOptions{
			Context:   opts.Context,
			Debug:     opts.Debug,
			Hosts:     opts.Hosts,
			LogLevel:  opts.LogLevel,
			TLS:       opts.TLS,
			TLSVerify: opts.TLSVerify,
		}

		if opts.TLSVerify {
			cnxOptions.TLSOptions = opts.TLSOptions
		}
		ctx = apicontext.WithCliOptions(ctx, cnxOptions)
	}
	ctx = apicontext.WithCurrentContext(ctx, currentContext)
	ctx = store.WithContextStore(ctx, s)

	if err = root.ExecuteContext(ctx); err != nil {
		handleError(ctx, err, ctype, currentContext, cc, root)
	}
	metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
}

func handleError(ctx context.Context, err error, ctype string, currentContext string, cc *store.DockerContext, root *cobra.Command) {
	// if user canceled request, simply exit without any error message
	if errdefs.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
		metrics.Track(ctype, os.Args[1:], metrics.CanceledStatus)
		os.Exit(130)
	}
	if ctype == store.AwsContextType {
		exit(currentContext, errors.Errorf(`%q context type has been renamed. Recreate the context by running:
$ docker context create %s <name>`, cc.Type(), store.EcsContextType), ctype)
	}

	// Context should always be handled by new CLI
	requiredCmd, _, _ := root.Find(os.Args[1:])
	if requiredCmd != nil && isContextAgnosticCommand(requiredCmd) {
		exit(currentContext, err, ctype)
	}
	mobycli.ExecIfDefaultCtxType(ctx, root)

	checkIfUnknownCommandExistInDefaultContext(err, currentContext, ctype)

	exit(currentContext, err, ctype)
}

func exit(ctx string, err error, ctype string) {
	if exit, ok := err.(cmd.ExitCodeError); ok {
		metrics.Track(ctype, os.Args[1:], metrics.SuccessStatus)
		os.Exit(exit.ExitCode)
	}

	metrics.Track(ctype, os.Args[1:], metrics.FailureStatus)

	if errors.Is(err, errdefs.ErrLoginRequired) {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(errdefs.ExitCodeLoginRequired)
	}

	if compose.Warning != "" {
		fmt.Fprintln(os.Stderr, compose.Warning)
	}

	if errors.Is(err, errdefs.ErrNotImplemented) {
		name := metrics.GetCommand(os.Args[1:])
		fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s)\n", name, ctx)

		os.Exit(1)
	}

	fatal(err)
}

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	os.Exit(1)
}

func checkIfUnknownCommandExistInDefaultContext(err error, currentContext string, contextType string) {
	submatch := unknownCommandRegexp.FindSubmatch([]byte(err.Error()))
	if len(submatch) == 2 {
		dockerCommand := string(submatch[1])

		if mobycli.IsDefaultContextCommand(dockerCommand) {
			fmt.Fprintf(os.Stderr, "Command %q not available in current context (%s), you can use the \"default\" context to run this command\n", dockerCommand, currentContext)
			metrics.Track(contextType, os.Args[1:], metrics.FailureStatus)
			os.Exit(1)
		}
	}
}

func newSigContext() (context.Context, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	s := make(chan os.Signal, 1)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-s
		cancel()
	}()
	return ctx, cancel
}

func determineCurrentContext(flag string, configDir string, hosts []string) string {
	// host and context flags cannot be both set at the same time -- the local backend enforces this when resolving hostname
	// -H flag disables context --> set default as current
	if len(hosts) > 0 {
		return "default"
	}
	// DOCKER_HOST disables context --> set default as current
	if _, present := os.LookupEnv("DOCKER_HOST"); present {
		return "default"
	}
	res := flag
	if res == "" {
		// check if DOCKER_CONTEXT env variable was set
		if _, present := os.LookupEnv("DOCKER_CONTEXT"); present {
			res = os.Getenv("DOCKER_CONTEXT")
		}

		if res == "" {
			config, err := config.LoadFile(configDir)
			if err != nil {
				fmt.Fprintln(os.Stderr, errors.Wrap(err, "WARNING"))
				return "default"
			}
			res = config.CurrentContext
		}
	}
	if res == "" {
		res = "default"
	}
	return res
}

func walk(c *cobra.Command, f func(*cobra.Command)) {
	f(c)
	for _, c := range c.Commands() {
		walk(c, f)
	}
}
<|endoftext|>