diff --git a/kubernetes/check.go b/kubernetes/check.go index 47152b01e3..8d347280fd 100644 --- a/kubernetes/check.go +++ b/kubernetes/check.go @@ -1,55 +1,20 @@ package kubernetes -import ( - apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1" - apiv1beta2 "github.com/docker/cli/kubernetes/compose/v1beta2" - "github.com/pkg/errors" - apimachinerymetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) +import api "github.com/docker/compose-on-kubernetes/api" // StackVersion represents the detected Compose Component on Kubernetes side. -type StackVersion string +// Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackVersion instead +type StackVersion = api.StackVersion const ( // StackAPIV1Beta1 is returned if it's the most recent version available. - StackAPIV1Beta1 = StackVersion("v1beta1") + // Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackAPIV1Beta1 instead + StackAPIV1Beta1 = api.StackAPIV1Beta1 // StackAPIV1Beta2 is returned if it's the most recent version available. - StackAPIV1Beta2 = StackVersion("v1beta2") + // Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackAPIV1Beta2 instead + StackAPIV1Beta2 = api.StackAPIV1Beta2 ) // GetStackAPIVersion returns the most recent stack API installed. -func GetStackAPIVersion(clientSet *kubernetes.Clientset) (StackVersion, error) { - groups, err := clientSet.Discovery().ServerGroups() - if err != nil { - return "", err - } - - return getAPIVersion(groups) -} - -func getAPIVersion(groups *metav1.APIGroupList) (StackVersion, error) { - switch { - case findVersion(apiv1beta2.SchemeGroupVersion, groups.Groups): - return StackAPIV1Beta2, nil - case findVersion(apiv1beta1.SchemeGroupVersion, groups.Groups): - return StackAPIV1Beta1, nil - default: - return "", errors.Errorf("failed to find a Stack API version") - } -} - -func findVersion(stackAPI schema.GroupVersion, groups []apimachinerymetav1.APIGroup) bool { - for _, group := range groups { - if group.Name == stackAPI.Group { - for _, version := range group.Versions { - if version.Version == stackAPI.Version { - return true - } - } - } - } - return false -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api.GetStackAPIVersion instead +var GetStackAPIVersion = api.GetStackAPIVersion diff --git a/kubernetes/check_test.go b/kubernetes/check_test.go deleted file mode 100644 index e9b9aa7f9a..0000000000 --- a/kubernetes/check_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package kubernetes - -import ( - "testing" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGetStackAPIVersion(t *testing.T) { - var tests = []struct { - description string - groups *metav1.APIGroupList - err bool - expectedStack StackVersion - }{ - {"no stack api", makeGroups(), true, ""}, - {"v1beta1", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1"}}), false, StackAPIV1Beta1}, - {"v1beta2", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta2"}}), false, StackAPIV1Beta2}, - {"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2"}}), false, StackAPIV1Beta2}, - } - - for _, test := range tests { - version, err := getAPIVersion(test.groups) - if test.err { - assert.ErrorContains(t, err, "") - } else { - assert.NilError(t, err) - } - assert.Check(t, is.Equal(test.expectedStack, version)) - } -} - -type groupVersion struct { - name string - versions []string -} - -func makeGroups(versions ...groupVersion) *metav1.APIGroupList { - groups := make([]metav1.APIGroup, len(versions)) - for i := range versions { - groups[i].Name = versions[i].name - for _, v := range versions[i].versions { - groups[i].Versions = append(groups[i].Versions, metav1.GroupVersionForDiscovery{Version: v}) - } - } - return &metav1.APIGroupList{ - Groups: groups, - } -} diff --git a/kubernetes/client/clientset/clientset.go b/kubernetes/client/clientset/clientset.go index b9cea6bb14..2915752b6d 100644 --- a/kubernetes/client/clientset/clientset.go +++ b/kubernetes/client/clientset/clientset.go @@ -1,96 +1,25 @@ package clientset -import ( - composev1beta1 "github.com/docker/cli/kubernetes/client/clientset/typed/compose/v1beta1" - composev1beta2 "github.com/docker/cli/kubernetes/client/clientset/typed/compose/v1beta2" - "github.com/golang/glog" - "k8s.io/client-go/discovery" - "k8s.io/client-go/rest" - "k8s.io/client-go/util/flowcontrol" -) +import api "github.com/docker/compose-on-kubernetes/api/client/clientset" // Interface defines the methods a compose kube client should have -// FIXME(vdemeester) is it required ? -type Interface interface { - Discovery() discovery.DiscoveryInterface - ComposeV1beta2() composev1beta2.ComposeV1beta2Interface - ComposeV1beta1() composev1beta1.ComposeV1beta1Interface -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.Interface instead +type Interface = api.Interface // Clientset contains the clients for groups. Each group has exactly one // version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - *composev1beta2.ComposeV1beta2Client - *composev1beta1.ComposeV1beta1Client -} - -// ComposeV1beta2 retrieves the ComposeV1beta2Client -func (c *Clientset) ComposeV1beta2() composev1beta2.ComposeV1beta2Interface { - if c == nil { - return nil - } - return c.ComposeV1beta2Client -} - -// ComposeV1beta1 retrieves the ComposeV1beta1Client -func (c *Clientset) ComposeV1beta1() composev1beta1.ComposeV1beta1Interface { - if c == nil { - return nil - } - return c.ComposeV1beta1Client -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.Clientset instead +type Clientset = api.Clientset // NewForConfig creates a new Clientset for the given config. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.ComposeV1beta2Client, err = composev1beta2.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - cs.ComposeV1beta1Client, err = composev1beta1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) - if err != nil { - glog.Errorf("failed to create the DiscoveryClient: %v", err) - return nil, err - } - return &cs, nil -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.NewForConfig instead +var NewForConfig = api.NewForConfig // NewForConfigOrDie creates a new Clientset for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.ComposeV1beta2Client = composev1beta2.NewForConfigOrDie(c) - cs.ComposeV1beta1Client = composev1beta1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.NewForConfigOrDie instead +var NewForConfigOrDie = api.NewForConfigOrDie // New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.ComposeV1beta2Client = composev1beta2.New(c) - cs.ComposeV1beta1Client = composev1beta1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.New instead +var New = api.New diff --git a/kubernetes/client/clientset/scheme/register.go b/kubernetes/client/clientset/scheme/register.go index ffd6e4f009..9a42571b17 100644 --- a/kubernetes/client/clientset/scheme/register.go +++ b/kubernetes/client/clientset/scheme/register.go @@ -1,26 +1,17 @@ package scheme -import ( - composev1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1" - composev1beta2 "github.com/docker/cli/kubernetes/compose/v1beta2" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" -) +import api "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" // Variables required for registration var ( - Scheme = runtime.NewScheme() - Codecs = serializer.NewCodecFactory(Scheme) - ParameterCodec = runtime.NewParameterCodec(Scheme) + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/scheme.Scheme instead + Scheme = api.Scheme + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/scheme.Codecs instead + Codecs = api.Codecs + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/scheme.ParameterCodec instead + ParameterCodec = api.ParameterCodec ) -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - AddToScheme(Scheme) -} - // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: // @@ -35,7 +26,5 @@ func init() { // // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. -func AddToScheme(scheme *runtime.Scheme) { - composev1beta2.AddToScheme(scheme) - composev1beta1.AddToScheme(scheme) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/scheme.AddToScheme instead +var AddToScheme = api.AddToScheme diff --git a/kubernetes/client/clientset/typed/compose/v1beta1/compose_client.go b/kubernetes/client/clientset/typed/compose/v1beta1/compose_client.go index 4039d4de13..03d5b101e7 100644 --- a/kubernetes/client/clientset/typed/compose/v1beta1/compose_client.go +++ b/kubernetes/client/clientset/typed/compose/v1beta1/compose_client.go @@ -1,74 +1,24 @@ package v1beta1 -import ( - "github.com/docker/cli/kubernetes/client/clientset/scheme" - "github.com/docker/cli/kubernetes/compose/v1beta1" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/rest" -) +import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" // ComposeV1beta1Interface defines the methods a compose v1beta1 client has -type ComposeV1beta1Interface interface { - RESTClient() rest.Interface - StacksGetter -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.ComposeV1beta1Interface instead +type ComposeV1beta1Interface = api.ComposeV1beta1Interface // ComposeV1beta1Client is used to interact with features provided by the compose.docker.com group. -type ComposeV1beta1Client struct { - restClient rest.Interface -} - -// Stacks returns a stack client -func (c *ComposeV1beta1Client) Stacks(namespace string) StackInterface { - return newStacks(c, namespace) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.ComposeV1beta1Client instead +type ComposeV1beta1Client = api.ComposeV1beta1Client // NewForConfig creates a new ComposeV1beta1Client for the given config. -func NewForConfig(c *rest.Config) (*ComposeV1beta1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &ComposeV1beta1Client{client}, nil -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.NewForConfig instead +var NewForConfig = api.NewForConfig // NewForConfigOrDie creates a new ComposeV1beta1Client for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *ComposeV1beta1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.NewForConfigOrDie instead +var NewForConfigOrDie = api.NewForConfigOrDie // New creates a new ComposeV1beta1Client for the given RESTClient. -func New(c rest.Interface) *ComposeV1beta1Client { - return &ComposeV1beta1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1beta1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *ComposeV1beta1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.New instead +var New = api.New diff --git a/kubernetes/client/clientset/typed/compose/v1beta1/stack.go b/kubernetes/client/clientset/typed/compose/v1beta1/stack.go index 1fbe53f813..d6207ddac6 100644 --- a/kubernetes/client/clientset/typed/compose/v1beta1/stack.go +++ b/kubernetes/client/clientset/typed/compose/v1beta1/stack.go @@ -1,157 +1,12 @@ package v1beta1 -import ( - "github.com/docker/cli/kubernetes/client/clientset/scheme" - "github.com/docker/cli/kubernetes/compose/v1beta1" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/rest" -) +import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" // StacksGetter has a method to return a StackInterface. // A group's client should implement this interface. -type StacksGetter interface { - Stacks(namespace string) StackInterface -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.StacksGetter instead +type StacksGetter = api.StacksGetter // StackInterface has methods to work with Stack resources. -type StackInterface interface { - Create(*v1beta1.Stack) (*v1beta1.Stack, error) - Update(*v1beta1.Stack) (*v1beta1.Stack, error) - UpdateStatus(*v1beta1.Stack) (*v1beta1.Stack, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1beta1.Stack, error) - List(opts v1.ListOptions) (*v1beta1.StackList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta1.Stack, error) -} - -var _ StackInterface = &stacks{} - -// stacks implements StackInterface -type stacks struct { - client rest.Interface - ns string -} - -// newStacks returns a Stacks -func newStacks(c *ComposeV1beta1Client, namespace string) *stacks { - return &stacks{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Create takes the representation of a stack and creates it. Returns the server's representation of the stack, and an error, if there is any. -func (c *stacks) Create(stack *v1beta1.Stack) (*v1beta1.Stack, error) { - result := &v1beta1.Stack{} - err := c.client.Post(). - Namespace(c.ns). - Resource("stacks"). - Body(stack). - Do(). - Into(result) - return result, err -} - -// Update takes the representation of a stack and updates it. Returns the server's representation of the stack, and an error, if there is any. -func (c *stacks) Update(stack *v1beta1.Stack) (*v1beta1.Stack, error) { - result := &v1beta1.Stack{} - err := c.client.Put(). - Namespace(c.ns). - Resource("stacks"). - Name(stack.Name). - Body(stack). - Do(). - Into(result) - return result, err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclientstatus=false comment above the type to avoid generating UpdateStatus(). - -func (c *stacks) UpdateStatus(stack *v1beta1.Stack) (*v1beta1.Stack, error) { - result := &v1beta1.Stack{} - err := c.client.Put(). - Namespace(c.ns). - Resource("stacks"). - Name(stack.Name). - SubResource("status"). - Body(stack). - Do(). - Into(result) - return result, err -} - -// Delete takes name of the stack and deletes it. Returns an error if one occurs. -func (c *stacks) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("stacks"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *stacks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("stacks"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Get takes name of the stack, and returns the corresponding stack object, and an error if there is any. -func (c *stacks) Get(name string, options v1.GetOptions) (*v1beta1.Stack, error) { - result := &v1beta1.Stack{} - err := c.client.Get(). - Namespace(c.ns). - Resource("stacks"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return result, err -} - -// List takes label and field selectors, and returns the list of Stacks that match those selectors. -func (c *stacks) List(opts v1.ListOptions) (*v1beta1.StackList, error) { - result := &v1beta1.StackList{} - err := c.client.Get(). - Namespace(c.ns). - Resource("stacks"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return result, err -} - -// Watch returns a watch.Interface that watches the requested stacks. -func (c *stacks) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("stacks"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Patch applies the patch and returns the patched stack. -func (c *stacks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta1.Stack, error) { - result := &v1beta1.Stack{} - err := c.client.Patch(pt). - Namespace(c.ns). - Resource("stacks"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return result, err -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.StackInterface instead +type StackInterface = api.StackInterface diff --git a/kubernetes/client/clientset/typed/compose/v1beta2/compose_client.go b/kubernetes/client/clientset/typed/compose/v1beta2/compose_client.go index 34bee389c5..7a09f255ee 100644 --- a/kubernetes/client/clientset/typed/compose/v1beta2/compose_client.go +++ b/kubernetes/client/clientset/typed/compose/v1beta2/compose_client.go @@ -1,74 +1,24 @@ package v1beta2 -import ( - "github.com/docker/cli/kubernetes/client/clientset/scheme" - "github.com/docker/cli/kubernetes/compose/v1beta2" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/rest" -) +import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" // ComposeV1beta2Interface defines the methods a compose v1beta2 client has -type ComposeV1beta2Interface interface { - RESTClient() rest.Interface - StacksGetter -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.StackInterface instead +type ComposeV1beta2Interface = api.ComposeV1beta2Interface // ComposeV1beta2Client is used to interact with features provided by the compose.docker.com group. -type ComposeV1beta2Client struct { - restClient rest.Interface -} - -// Stacks returns a stack client -func (c *ComposeV1beta2Client) Stacks(namespace string) StackInterface { - return newStacks(c, namespace) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.ComposeV1beta2Client instead +type ComposeV1beta2Client = api.ComposeV1beta2Client // NewForConfig creates a new ComposeV1beta2Client for the given config. -func NewForConfig(c *rest.Config) (*ComposeV1beta2Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &ComposeV1beta2Client{client}, nil -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.NewForConfig instead +var NewForConfig = api.NewForConfig // NewForConfigOrDie creates a new ComposeV1beta2Client for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *ComposeV1beta2Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.NewForConfigOrDie instead +var NewForConfigOrDie = api.NewForConfigOrDie // New creates a new ComposeV1beta2Client for the given RESTClient. -func New(c rest.Interface) *ComposeV1beta2Client { - return &ComposeV1beta2Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1beta2.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *ComposeV1beta2Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.New instead +var New = api.New diff --git a/kubernetes/client/clientset/typed/compose/v1beta2/stack.go b/kubernetes/client/clientset/typed/compose/v1beta2/stack.go index 6937b4bfba..ef7b2ba27d 100644 --- a/kubernetes/client/clientset/typed/compose/v1beta2/stack.go +++ b/kubernetes/client/clientset/typed/compose/v1beta2/stack.go @@ -1,155 +1,12 @@ package v1beta2 -import ( - "github.com/docker/cli/kubernetes/client/clientset/scheme" - "github.com/docker/cli/kubernetes/compose/v1beta2" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/rest" -) +import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" // StacksGetter has a method to return a StackInterface. // A group's client should implement this interface. -type StacksGetter interface { - Stacks(namespace string) StackInterface -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.StacksGetter instead +type StacksGetter = api.StacksGetter // StackInterface has methods to work with Stack resources. -type StackInterface interface { - Create(*v1beta2.Stack) (*v1beta2.Stack, error) - Update(*v1beta2.Stack) (*v1beta2.Stack, error) - UpdateStatus(*v1beta2.Stack) (*v1beta2.Stack, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1beta2.Stack, error) - List(opts v1.ListOptions) (*v1beta2.StackList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta2.Stack, error) -} - -// stacks implements StackInterface -type stacks struct { - client rest.Interface - ns string -} - -// newStacks returns a Stacks -func newStacks(c *ComposeV1beta2Client, namespace string) *stacks { - return &stacks{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Create takes the representation of a stack and creates it. Returns the server's representation of the stack, and an error, if there is any. -func (c *stacks) Create(stack *v1beta2.Stack) (*v1beta2.Stack, error) { - result := &v1beta2.Stack{} - err := c.client.Post(). - Namespace(c.ns). - Resource("stacks"). - Body(stack). - Do(). - Into(result) - return result, err -} - -// Update takes the representation of a stack and updates it. Returns the server's representation of the stack, and an error, if there is any. -func (c *stacks) Update(stack *v1beta2.Stack) (*v1beta2.Stack, error) { - result := &v1beta2.Stack{} - err := c.client.Put(). - Namespace(c.ns). - Resource("stacks"). - Name(stack.Name). - Body(stack). - Do(). - Into(result) - return result, err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclientstatus=false comment above the type to avoid generating UpdateStatus(). - -func (c *stacks) UpdateStatus(stack *v1beta2.Stack) (*v1beta2.Stack, error) { - result := &v1beta2.Stack{} - err := c.client.Put(). - Namespace(c.ns). - Resource("stacks"). - Name(stack.Name). - SubResource("status"). - Body(stack). - Do(). - Into(result) - return result, err -} - -// Delete takes name of the stack and deletes it. Returns an error if one occurs. -func (c *stacks) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("stacks"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *stacks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("stacks"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Get takes name of the stack, and returns the corresponding stack object, and an error if there is any. -func (c *stacks) Get(name string, options v1.GetOptions) (*v1beta2.Stack, error) { - result := &v1beta2.Stack{} - err := c.client.Get(). - Namespace(c.ns). - Resource("stacks"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return result, err -} - -// List takes label and field selectors, and returns the list of Stacks that match those selectors. -func (c *stacks) List(opts v1.ListOptions) (*v1beta2.StackList, error) { - result := &v1beta2.StackList{} - err := c.client.Get(). - Namespace(c.ns). - Resource("stacks"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return result, err -} - -// Watch returns a watch.Interface that watches the requested stacks. -func (c *stacks) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("stacks"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Patch applies the patch and returns the patched stack. -func (c *stacks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta2.Stack, error) { - result := &v1beta2.Stack{} - err := c.client.Patch(pt). - Namespace(c.ns). - Resource("stacks"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return result, err -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.StackInterface instead +type StackInterface = api.StackInterface diff --git a/kubernetes/client/informers/compose/interface.go b/kubernetes/client/informers/compose/interface.go index 73928705c5..fe8496784e 100644 --- a/kubernetes/client/informers/compose/interface.go +++ b/kubernetes/client/informers/compose/interface.go @@ -1,25 +1,11 @@ package compose -import ( - "github.com/docker/cli/kubernetes/client/informers/compose/v1beta2" - "github.com/docker/cli/kubernetes/client/informers/internalinterfaces" -) +import api "github.com/docker/compose-on-kubernetes/api/client/informers/compose" // Interface provides access to each of this group's versions. -type Interface interface { - V1beta2() v1beta2.Interface -} - -type group struct { - internalinterfaces.SharedInformerFactory -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose.Interface instead +type Interface = api.Interface // New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory) Interface { - return &group{f} -} - -// V1beta2 returns a new v1beta2.Interface. -func (g *group) V1beta2() v1beta2.Interface { - return v1beta2.New(g.SharedInformerFactory) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose.New instead +var New = api.New diff --git a/kubernetes/client/informers/compose/v1beta2/interface.go b/kubernetes/client/informers/compose/v1beta2/interface.go index 06ea1ac0d8..819474d9dd 100644 --- a/kubernetes/client/informers/compose/v1beta2/interface.go +++ b/kubernetes/client/informers/compose/v1beta2/interface.go @@ -1,25 +1,11 @@ package v1beta2 -import ( - "github.com/docker/cli/kubernetes/client/informers/internalinterfaces" -) +import api "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2" // Interface provides access to all the informers in this group version. -type Interface interface { - // Stacks returns a StackInformer. - Stacks() StackInformer -} - -type version struct { - internalinterfaces.SharedInformerFactory -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2.Interface instead +type Interface = api.Interface // New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory) Interface { - return &version{f} -} - -// Stacks returns a StackInformer. -func (v *version) Stacks() StackInformer { - return &stackInformer{factory: v.SharedInformerFactory} -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2.New instead +var New = api.New diff --git a/kubernetes/client/informers/compose/v1beta2/stack.go b/kubernetes/client/informers/compose/v1beta2/stack.go index 487aeb97d2..a2ff65a643 100644 --- a/kubernetes/client/informers/compose/v1beta2/stack.go +++ b/kubernetes/client/informers/compose/v1beta2/stack.go @@ -1,51 +1,8 @@ package v1beta2 -import ( - "time" - - "github.com/docker/cli/kubernetes/client/clientset" - "github.com/docker/cli/kubernetes/client/informers/internalinterfaces" - "github.com/docker/cli/kubernetes/client/listers/compose/v1beta2" - compose_v1beta2 "github.com/docker/cli/kubernetes/compose/v1beta2" - "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/cache" -) +import api "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2" // StackInformer provides access to a shared informer and lister for // Stacks. -type StackInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1beta2.StackLister -} - -type stackInformer struct { - factory internalinterfaces.SharedInformerFactory -} - -func newStackInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - sharedIndexInformer := cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - return client.ComposeV1beta2().Stacks(v1.NamespaceAll).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - return client.ComposeV1beta2().Stacks(v1.NamespaceAll).Watch(options) - }, - }, - &compose_v1beta2.Stack{}, - resyncPeriod, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - ) - - return sharedIndexInformer -} - -func (f *stackInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&compose_v1beta2.Stack{}, newStackInformer) -} - -func (f *stackInformer) Lister() v1beta2.StackLister { - return v1beta2.NewStackLister(f.Informer().GetIndexer()) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2.StackInformer instead +type StackInformer = api.StackInformer diff --git a/kubernetes/client/informers/factory.go b/kubernetes/client/informers/factory.go index 9cba7013f3..84465da883 100644 --- a/kubernetes/client/informers/factory.go +++ b/kubernetes/client/informers/factory.go @@ -1,101 +1,12 @@ package informers -import ( - "reflect" - "sync" - "time" - - "github.com/docker/cli/kubernetes/client/clientset" - "github.com/docker/cli/kubernetes/client/informers/compose" - "github.com/docker/cli/kubernetes/client/informers/internalinterfaces" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/tools/cache" -) - -type sharedInformerFactory struct { - client clientset.Interface - lock sync.Mutex - defaultResync time.Duration - - informers map[reflect.Type]cache.SharedIndexInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[reflect.Type]bool -} +import api "github.com/docker/compose-on-kubernetes/api/client/informers" // NewSharedInformerFactory constructs a new instance of sharedInformerFactory -func NewSharedInformerFactory(client clientset.Interface, defaultResync time.Duration) SharedInformerFactory { - return &sharedInformerFactory{ - client: client, - defaultResync: defaultResync, - informers: make(map[reflect.Type]cache.SharedIndexInformer), - startedInformers: make(map[reflect.Type]bool), - } -} - -// Start initializes all requested informers. -func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - go informer.Run(stopCh) - f.startedInformers[informerType] = true - } - } -} - -// WaitForCacheSync waits for all started informers' cache were synced. -func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { - informers := func() map[reflect.Type]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[reflect.Type]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer - } - } - return informers - }() - - res := map[reflect.Type]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// InternalInformerFor returns the SharedIndexInformer for obj using an internal -// client. -func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informerType := reflect.TypeOf(obj) - informer, exists := f.informers[informerType] - if exists { - return informer - } - informer = newFunc(f.client, f.defaultResync) - f.informers[informerType] = informer - - return informer -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers.NewSharedInformerFactory instead +var NewSharedInformerFactory = api.NewSharedInformerFactory // SharedInformerFactory provides shared informers for resources in all known // API group versions. -type SharedInformerFactory interface { - internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - - Compose() compose.Interface -} - -func (f *sharedInformerFactory) Compose() compose.Interface { - return compose.New(f) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers.SharedInformerFactory instead +type SharedInformerFactory = api.SharedInformerFactory diff --git a/kubernetes/client/informers/generic.go b/kubernetes/client/informers/generic.go index f3baa85414..df18ed0fa4 100644 --- a/kubernetes/client/informers/generic.go +++ b/kubernetes/client/informers/generic.go @@ -1,44 +1,8 @@ package informers -import ( - "fmt" - - "github.com/docker/cli/kubernetes/compose/v1beta2" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/tools/cache" -) +import api "github.com/docker/compose-on-kubernetes/api/client/informers" // GenericInformer is type of SharedIndexInformer which will locate and delegate to other // sharedInformers based on type -type GenericInformer interface { - Informer() cache.SharedIndexInformer - Lister() cache.GenericLister -} - -type genericInformer struct { - informer cache.SharedIndexInformer - resource schema.GroupResource -} - -// Informer returns the SharedIndexInformer. -func (f *genericInformer) Informer() cache.SharedIndexInformer { - return f.informer -} - -// Lister returns the GenericLister. -func (f *genericInformer) Lister() cache.GenericLister { - return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) -} - -// ForResource gives generic access to a shared informer of the matching type -// TODO extend this to unknown resources with a client pool -func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { - switch resource { - // Group=Compose, Version=V1beta1 - case v1beta2.SchemeGroupVersion.WithResource("stacks"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Compose().V1beta2().Stacks().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers.GenericInformer instead +type GenericInformer = api.GenericInformer diff --git a/kubernetes/client/informers/internalinterfaces/factory_interfaces.go b/kubernetes/client/informers/internalinterfaces/factory_interfaces.go index 079b818621..5666e9525f 100644 --- a/kubernetes/client/informers/internalinterfaces/factory_interfaces.go +++ b/kubernetes/client/informers/internalinterfaces/factory_interfaces.go @@ -1,18 +1,11 @@ package internalinterfaces -import ( - "time" - - "github.com/docker/cli/kubernetes/client/clientset" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/tools/cache" -) +import api "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" // NewInformerFunc defines a Informer constructor (from a clientset and a duration) -type NewInformerFunc func(clientset.Interface, time.Duration) cache.SharedIndexInformer +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces.NewInformerFunc instead +type NewInformerFunc = api.NewInformerFunc // SharedInformerFactory a small interface to allow for adding an informer without an import cycle -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces.SharedInformerFactory instead +type SharedInformerFactory = api.SharedInformerFactory diff --git a/kubernetes/client/listers/compose/v1beta2/expansion_generated.go b/kubernetes/client/listers/compose/v1beta2/expansion_generated.go index 1d0f5529c1..ecd6196eab 100644 --- a/kubernetes/client/listers/compose/v1beta2/expansion_generated.go +++ b/kubernetes/client/listers/compose/v1beta2/expansion_generated.go @@ -1,9 +1,13 @@ package v1beta2 +import api "github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2" + // StackListerExpansion allows custom methods to be added to // StackLister. -type StackListerExpansion interface{} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackListerExpansion instead +type StackListerExpansion = api.StackListerExpansion // StackNamespaceListerExpansion allows custom methods to be added to // StackNamespaceLister. -type StackNamespaceListerExpansion interface{} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackNamespaceListerExpansion instead +type StackNamespaceListerExpansion = api.StackNamespaceListerExpansion diff --git a/kubernetes/client/listers/compose/v1beta2/stack.go b/kubernetes/client/listers/compose/v1beta2/stack.go index c4bb298991..39b3793a16 100644 --- a/kubernetes/client/listers/compose/v1beta2/stack.go +++ b/kubernetes/client/listers/compose/v1beta2/stack.go @@ -1,78 +1,15 @@ package v1beta2 -import ( - "github.com/docker/cli/kubernetes/compose/v1beta2" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) +import api "github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2" // StackLister helps list Stacks. -type StackLister interface { - // List lists all Stacks in the indexer. - List(selector labels.Selector) ([]*v1beta2.Stack, error) - // Stacks returns an object that can list and get Stacks. - Stacks(namespace string) StackNamespaceLister - StackListerExpansion -} - -// stackLister implements the StackLister interface. -type stackLister struct { - indexer cache.Indexer -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackLister instead +type StackLister = api.StackLister // NewStackLister returns a new StackLister. -func NewStackLister(indexer cache.Indexer) StackLister { - return &stackLister{indexer: indexer} -} - -// List lists all Stacks in the indexer. -func (s *stackLister) List(selector labels.Selector) ([]*v1beta2.Stack, error) { - stacks := []*v1beta2.Stack{} - err := cache.ListAll(s.indexer, selector, func(m interface{}) { - stacks = append(stacks, m.(*v1beta2.Stack)) - }) - return stacks, err -} - -// Stacks returns an object that can list and get Stacks. -func (s *stackLister) Stacks(namespace string) StackNamespaceLister { - return stackNamespaceLister{indexer: s.indexer, namespace: namespace} -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.NewStackLister instead +var NewStackLister = api.NewStackLister // StackNamespaceLister helps list and get Stacks. -type StackNamespaceLister interface { - // List lists all Stacks in the indexer for a given namespace. - List(selector labels.Selector) ([]*v1beta2.Stack, error) - // Get retrieves the Stack from the indexer for a given namespace and name. - Get(name string) (*v1beta2.Stack, error) - StackNamespaceListerExpansion -} - -// stackNamespaceLister implements the StackNamespaceLister -// interface. -type stackNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Stacks in the indexer for a given namespace. -func (s stackNamespaceLister) List(selector labels.Selector) ([]*v1beta2.Stack, error) { - stacks := []*v1beta2.Stack{} - err := cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - stacks = append(stacks, m.(*v1beta2.Stack)) - }) - return stacks, err -} - -// Get retrieves the Stack from the indexer for a given namespace and name. -func (s stackNamespaceLister) Get(name string) (*v1beta2.Stack, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1beta2.GroupResource("stack"), name) - } - return obj.(*v1beta2.Stack), nil -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackNamespaceLister instead +type StackNamespaceLister = api.StackNamespaceLister diff --git a/kubernetes/compose/clone/maps.go b/kubernetes/compose/clone/maps.go index fc892ddc0a..3c4dc139f2 100644 --- a/kubernetes/compose/clone/maps.go +++ b/kubernetes/compose/clone/maps.go @@ -1,25 +1,11 @@ package clone +import api "github.com/docker/compose-on-kubernetes/api/compose/clone" + // MapOfStringToSliceOfString deep copy a map[string][]string -func MapOfStringToSliceOfString(source map[string][]string) map[string][]string { - if source == nil { - return nil - } - res := make(map[string][]string, len(source)) - for k, v := range source { - res[k] = SliceOfString(v) - } - return res -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/clone.MapOfStringToSliceOfString instead +var MapOfStringToSliceOfString = api.MapOfStringToSliceOfString // MapOfStringToInt deep copy a map[string]int -func MapOfStringToInt(source map[string]int) map[string]int { - if source == nil { - return nil - } - res := make(map[string]int, len(source)) - for k, v := range source { - res[k] = v - } - return res -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/clone.MapOfStringToInt instead +var MapOfStringToInt = api.MapOfStringToInt diff --git a/kubernetes/compose/clone/slices.go b/kubernetes/compose/clone/slices.go index cdfa8ba16b..57eac2026f 100644 --- a/kubernetes/compose/clone/slices.go +++ b/kubernetes/compose/clone/slices.go @@ -1,11 +1,7 @@ package clone +import api "github.com/docker/compose-on-kubernetes/api/compose/clone" + // SliceOfString deep copy a slice of strings -func SliceOfString(source []string) []string { - if source == nil { - return nil - } - res := make([]string, len(source)) - copy(res, source) - return res -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/clone.SliceOfString instead +var SliceOfString = api.SliceOfString diff --git a/kubernetes/compose/impersonation/impersonationconfig.go b/kubernetes/compose/impersonation/impersonationconfig.go index 06514b60be..c4e71edd83 100644 --- a/kubernetes/compose/impersonation/impersonationconfig.go +++ b/kubernetes/compose/impersonation/impersonationconfig.go @@ -1,26 +1,7 @@ package impersonation -import "github.com/docker/cli/kubernetes/compose/clone" +import api "github.com/docker/compose-on-kubernetes/api/compose/impersonation" // Config contains the data required to impersonate a user. -type Config struct { - // UserName is the username to impersonate on each request. - UserName string - // Groups are the groups to impersonate on each request. - Groups []string - // Extra is a free-form field which can be used to link some authentication information - // to authorization information. This field allows you to impersonate it. - Extra map[string][]string -} - -// Clone clones the impersonation config -func (ic *Config) Clone() *Config { - if ic == nil { - return nil - } - result := new(Config) - result.UserName = ic.UserName - result.Groups = clone.SliceOfString(ic.Groups) - result.Extra = clone.MapOfStringToSliceOfString(ic.Extra) - return result -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/impersonation.Config instead +type Config = api.Config diff --git a/kubernetes/compose/v1beta1/owner.go b/kubernetes/compose/v1beta1/owner.go index 1e5982336b..469eaa1475 100644 --- a/kubernetes/compose/v1beta1/owner.go +++ b/kubernetes/compose/v1beta1/owner.go @@ -1,31 +1,8 @@ package v1beta1 -import ( - "github.com/docker/cli/kubernetes/compose/impersonation" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" // Owner defines the owner of a stack. It is used to impersonate the controller calls // to kubernetes api. -type Owner struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Owner impersonation.Config `json:"owner,omitempty"` -} - -func (o *Owner) clone() *Owner { - if o == nil { - return nil - } - result := new(Owner) - result.TypeMeta = o.TypeMeta - result.ObjectMeta = o.ObjectMeta - result.Owner = *result.Owner.Clone() - return result -} - -// DeepCopyObject clones the owner -func (o *Owner) DeepCopyObject() runtime.Object { - return o.clone() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.Owner instead +type Owner = api.Owner diff --git a/kubernetes/compose/v1beta1/parsing.go b/kubernetes/compose/v1beta1/parsing.go index 634a8428a7..4370f32612 100644 --- a/kubernetes/compose/v1beta1/parsing.go +++ b/kubernetes/compose/v1beta1/parsing.go @@ -1,4 +1,7 @@ package v1beta1 +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + // MaxComposeVersion is the most recent version of compose file Schema supported in v1beta1 -const MaxComposeVersion = "3.5" +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.MaxComposeVersion instead +const MaxComposeVersion = api.MaxComposeVersion diff --git a/kubernetes/compose/v1beta1/register.go b/kubernetes/compose/v1beta1/register.go index 3b418230e4..65a82c03de 100644 --- a/kubernetes/compose/v1beta1/register.go +++ b/kubernetes/compose/v1beta1/register.go @@ -1,39 +1,22 @@ package v1beta1 -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" // GroupName is the group name used to register these objects -const GroupName = "compose.docker.com" +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.GroupName instead +const GroupName = api.GroupName // Alias variables for the registration var ( // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} - SchemeBuilder runtime.SchemeBuilder - localSchemeBuilder = &SchemeBuilder - AddToScheme = localSchemeBuilder.AddToScheme + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.SchemeGroupVersion instead + SchemeGroupVersion = api.SchemeGroupVersion + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.SchemeBuilder instead + SchemeBuilder = api.SchemeBuilder + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.AddToScheme instead + AddToScheme = api.AddToScheme ) -func init() { - localSchemeBuilder.Register(addKnownTypes) -} - -// Adds the list of known types to api.Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Stack{}, - &StackList{}, - &Owner{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} - // Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.Resource instead +var Resource = api.Resource diff --git a/kubernetes/compose/v1beta1/stack.go b/kubernetes/compose/v1beta1/stack.go index 83ad9f0810..5e1a69e4ae 100644 --- a/kubernetes/compose/v1beta1/stack.go +++ b/kubernetes/compose/v1beta1/stack.go @@ -1,87 +1,37 @@ package v1beta1 -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" // StackList defines a list of stacks -type StackList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - Items []Stack `json:"items" protobuf:"bytes,2,rep,name=items"` -} - -// DeepCopyObject clones the stack list -func (s *StackList) DeepCopyObject() runtime.Object { - if s == nil { - return nil - } - result := new(StackList) - result.TypeMeta = s.TypeMeta - result.ListMeta = s.ListMeta - if s.Items == nil { - return result - } - result.Items = make([]Stack, len(s.Items)) - for ix, s := range s.Items { - result.Items[ix] = *s.clone() - } - return result -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackList instead +type StackList = api.StackList // Stack defines a stack object to be register in the kubernetes API -type Stack struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec StackSpec `json:"spec,omitempty"` - Status StackStatus `json:"status,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.Stack instead +type Stack = api.Stack // StackSpec defines the desired state of Stack -type StackSpec struct { - ComposeFile string `json:"composeFile,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackSpec instead +type StackSpec = api.StackSpec // StackPhase defines the status phase in which the stack is. -type StackPhase string +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackPhase instead +type StackPhase = api.StackPhase // These are valid conditions of a stack. const ( // StackAvailable means the stack is available. - StackAvailable StackPhase = "Available" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackAvailable instead + StackAvailable StackPhase = api.StackAvailable // StackProgressing means the deployment is progressing. - StackProgressing StackPhase = "Progressing" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackProgressing instead + StackProgressing StackPhase = api.StackProgressing // StackFailure is added in a stack when one of its members fails to be created // or deleted. - StackFailure StackPhase = "Failure" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackFailure instead + StackFailure StackPhase = api.StackFailure ) // StackStatus defines the observed state of Stack -type StackStatus struct { - // Current condition of the stack. - Phase StackPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=StackPhase"` - // A human readable message indicating details about the stack. - Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` -} - -func (s *Stack) clone() *Stack { - if s == nil { - return nil - } - // in v1beta1, Stack has no pointer, slice or map. Plain old struct copy is ok - result := *s - return &result -} - -// Clone implements the Cloner interface for kubernetes -func (s *Stack) Clone() *Stack { - return s.clone() -} - -// DeepCopyObject clones the stack -func (s *Stack) DeepCopyObject() runtime.Object { - return s.clone() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackStatus instead +type StackStatus = api.StackStatus diff --git a/kubernetes/compose/v1beta2/composefile_stack_types.go b/kubernetes/compose/v1beta2/composefile_stack_types.go index 1a1c2cb2a1..bd94d09649 100644 --- a/kubernetes/compose/v1beta2/composefile_stack_types.go +++ b/kubernetes/compose/v1beta2/composefile_stack_types.go @@ -1,26 +1,7 @@ package v1beta2 -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" // ComposeFile is the content of a stack's compose file if any -type ComposeFile struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - ComposeFile string `json:"composeFile,omitempty"` -} - -func (c *ComposeFile) clone() *ComposeFile { - if c == nil { - return nil - } - res := *c - return &res -} - -// DeepCopyObject clones the ComposeFile -func (c *ComposeFile) DeepCopyObject() runtime.Object { - return c.clone() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ComposeFile instead +type ComposeFile = api.ComposeFile diff --git a/kubernetes/compose/v1beta2/owner.go b/kubernetes/compose/v1beta2/owner.go index 5dfc86528a..689a03e6f1 100644 --- a/kubernetes/compose/v1beta2/owner.go +++ b/kubernetes/compose/v1beta2/owner.go @@ -1,30 +1,7 @@ package v1beta2 -import ( - "github.com/docker/cli/kubernetes/compose/impersonation" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" // Owner describes the user who created the stack -type Owner struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Owner impersonation.Config `json:"owner,omitempty"` -} - -func (o *Owner) clone() *Owner { - if o == nil { - return nil - } - result := new(Owner) - result.TypeMeta = o.TypeMeta - result.ObjectMeta = o.ObjectMeta - result.Owner = *result.Owner.Clone() - return result -} - -// DeepCopyObject clones the owner -func (o *Owner) DeepCopyObject() runtime.Object { - return o.clone() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Owner instead +type Owner = api.Owner diff --git a/kubernetes/compose/v1beta2/register.go b/kubernetes/compose/v1beta2/register.go index 23555093d5..0cee5e2836 100644 --- a/kubernetes/compose/v1beta2/register.go +++ b/kubernetes/compose/v1beta2/register.go @@ -1,42 +1,23 @@ package v1beta2 -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" // GroupName is the name of the compose group -const GroupName = "compose.docker.com" +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Owner instead +const GroupName = api.GroupName var ( // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta2"} + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.SchemeGroupVersion instead + SchemeGroupVersion = api.SchemeGroupVersion // SchemeBuilder is the scheme builder - SchemeBuilder runtime.SchemeBuilder - localSchemeBuilder = &SchemeBuilder + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.SchemeBuilder instead + SchemeBuilder = api.SchemeBuilder // AddToScheme adds to scheme - AddToScheme = localSchemeBuilder.AddToScheme + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.AddToScheme instead + AddToScheme = api.AddToScheme ) -func init() { - localSchemeBuilder.Register(addKnownTypes) -} - -// Adds the list of known types to api.Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Stack{}, - &StackList{}, - &Owner{}, - &ComposeFile{}, - &Scale{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} - // GroupResource takes an unqualified resource and returns a Group qualified GroupResource -func GroupResource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.GroupResource instead +var GroupResource = api.GroupResource diff --git a/kubernetes/compose/v1beta2/scale.go b/kubernetes/compose/v1beta2/scale.go index 13ec54b88d..db12001233 100644 --- a/kubernetes/compose/v1beta2/scale.go +++ b/kubernetes/compose/v1beta2/scale.go @@ -1,29 +1,7 @@ package v1beta2 -import ( - "github.com/docker/cli/kubernetes/compose/clone" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" // Scale contains the current/desired replica count for services in a stack. -type Scale struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - Spec map[string]int `json:"spec,omitempty"` - Status map[string]int `json:"status,omitempty"` -} - -func (s *Scale) clone() *Scale { - return &Scale{ - TypeMeta: s.TypeMeta, - ObjectMeta: s.ObjectMeta, - Spec: clone.MapOfStringToInt(s.Spec), - Status: clone.MapOfStringToInt(s.Status), - } -} - -// DeepCopyObject clones the scale -func (s *Scale) DeepCopyObject() runtime.Object { - return s.clone() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Scale instead +type Scale = api.Scale diff --git a/kubernetes/compose/v1beta2/stack.go b/kubernetes/compose/v1beta2/stack.go index 6c90cdf123..8ca82d6859 100644 --- a/kubernetes/compose/v1beta2/stack.go +++ b/kubernetes/compose/v1beta2/stack.go @@ -1,256 +1,115 @@ package v1beta2 -import ( - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) +import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" // StackList is a list of stacks -type StackList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - Items []Stack `json:"items" protobuf:"bytes,2,rep,name=items"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackList instead +type StackList = api.StackList // Stack is v1beta2's representation of a Stack -type Stack struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec *StackSpec `json:"spec,omitempty"` - Status *StackStatus `json:"status,omitempty"` -} - -// DeepCopyObject clones the stack -func (s *Stack) DeepCopyObject() runtime.Object { - return s.clone() -} - -// DeepCopyObject clones the stack list -func (s *StackList) DeepCopyObject() runtime.Object { - if s == nil { - return nil - } - result := new(StackList) - result.TypeMeta = s.TypeMeta - result.ListMeta = s.ListMeta - if s.Items == nil { - return result - } - result.Items = make([]Stack, len(s.Items)) - for ix, s := range s.Items { - result.Items[ix] = *s.clone() - } - return result -} - -func (s *Stack) clone() *Stack { - if s == nil { - return nil - } - result := new(Stack) - result.TypeMeta = s.TypeMeta - result.ObjectMeta = s.ObjectMeta - result.Spec = s.Spec.clone() - result.Status = s.Status.clone() - return result -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Stack instead +type Stack = api.Stack // StackSpec defines the desired state of Stack -type StackSpec struct { - Services []ServiceConfig `json:"services,omitempty"` - Secrets map[string]SecretConfig `json:"secrets,omitempty"` - Configs map[string]ConfigObjConfig `json:"configs,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackSpec instead +type StackSpec = api.StackSpec // ServiceConfig is the configuration of one service -type ServiceConfig struct { - Name string `json:"name,omitempty"` - - CapAdd []string `json:"cap_add,omitempty"` - CapDrop []string `json:"cap_drop,omitempty"` - Command []string `json:"command,omitempty"` - Configs []ServiceConfigObjConfig `json:"configs,omitempty"` - Deploy DeployConfig `json:"deploy,omitempty"` - Entrypoint []string `json:"entrypoint,omitempty"` - Environment map[string]*string `json:"environment,omitempty"` - ExtraHosts []string `json:"extra_hosts,omitempty"` - Hostname string `json:"hostname,omitempty"` - HealthCheck *HealthCheckConfig `json:"health_check,omitempty"` - Image string `json:"image,omitempty"` - Ipc string `json:"ipc,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Pid string `json:"pid,omitempty"` - Ports []ServicePortConfig `json:"ports,omitempty"` - Privileged bool `json:"privileged,omitempty"` - ReadOnly bool `json:"read_only,omitempty"` - Secrets []ServiceSecretConfig `json:"secrets,omitempty"` - StdinOpen bool `json:"stdin_open,omitempty"` - StopGracePeriod *time.Duration `json:"stop_grace_period,omitempty"` - Tmpfs []string `json:"tmpfs,omitempty"` - Tty bool `json:"tty,omitempty"` - User *int64 `json:"user,omitempty"` - Volumes []ServiceVolumeConfig `json:"volumes,omitempty"` - WorkingDir string `json:"working_dir,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServiceConfig instead +type ServiceConfig = api.ServiceConfig // ServicePortConfig is the port configuration for a service -type ServicePortConfig struct { - Mode string `json:"mode,omitempty"` - Target uint32 `json:"target,omitempty"` - Published uint32 `json:"published,omitempty"` - Protocol string `json:"protocol,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServicePortConfig instead +type ServicePortConfig = api.ServicePortConfig // FileObjectConfig is a config type for a file used by a service -type FileObjectConfig struct { - Name string `json:"name,omitempty"` - File string `json:"file,omitempty"` - External External `json:"external,omitempty"` - Labels map[string]string `json:"labels,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.FileObjectConfig instead +type FileObjectConfig = api.FileObjectConfig // SecretConfig for a secret -type SecretConfig FileObjectConfig +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.SecretConfig instead +type SecretConfig = api.SecretConfig // ConfigObjConfig is the config for the swarm "Config" object -type ConfigObjConfig FileObjectConfig +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ConfigObjConfig instead +type ConfigObjConfig = api.ConfigObjConfig // External identifies a Volume or Network as a reference to a resource that is // not managed, and should already exist. // External.name is deprecated and replaced by Volume.name -type External struct { - Name string `json:"name,omitempty"` - External bool `json:"external,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.External instead +type External = api.External // FileReferenceConfig for a reference to a swarm file object -type FileReferenceConfig struct { - Source string `json:"source,omitempty"` - Target string `json:"target,omitempty"` - UID string `json:"uid,omitempty"` - GID string `json:"gid,omitempty"` - Mode *uint32 `json:"mode,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.FileReferenceConfig instead +type FileReferenceConfig = api.FileReferenceConfig // ServiceConfigObjConfig is the config obj configuration for a service -type ServiceConfigObjConfig FileReferenceConfig +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServiceConfigObjConfig instead +type ServiceConfigObjConfig = api.ServiceConfigObjConfig // ServiceSecretConfig is the secret configuration for a service -type ServiceSecretConfig FileReferenceConfig +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServiceSecretConfig instead +type ServiceSecretConfig = api.ServiceSecretConfig // DeployConfig is the deployment configuration for a service -type DeployConfig struct { - Mode string `json:"mode,omitempty"` - Replicas *uint64 `json:"replicas,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - UpdateConfig *UpdateConfig `json:"update_config,omitempty"` - Resources Resources `json:"resources,omitempty"` - RestartPolicy *RestartPolicy `json:"restart_policy,omitempty"` - Placement Placement `json:"placement,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.DeployConfig instead +type DeployConfig = api.DeployConfig // UpdateConfig is the service update configuration -type UpdateConfig struct { - Parallelism *uint64 `json:"paralellism,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.UpdateConfig instead +type UpdateConfig = api.UpdateConfig // Resources the resource limits and reservations -type Resources struct { - Limits *Resource `json:"limits,omitempty"` - Reservations *Resource `json:"reservations,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Resources instead +type Resources = api.Resources // Resource is a resource to be limited or reserved -type Resource struct { - NanoCPUs string `json:"cpus,omitempty"` - MemoryBytes int64 `json:"memory,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Resource instead +type Resource = api.Resource // RestartPolicy is the service restart policy -type RestartPolicy struct { - Condition string `json:"condition,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.RestartPolicy instead +type RestartPolicy = api.RestartPolicy // Placement constraints for the service -type Placement struct { - Constraints *Constraints `json:"constraints,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Placement instead +type Placement = api.Placement // Constraints lists constraints that can be set on the service -type Constraints struct { - OperatingSystem *Constraint - Architecture *Constraint - Hostname *Constraint - MatchLabels map[string]Constraint -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Constraints instead +type Constraints = api.Constraints // Constraint defines a constraint and it's operator (== or !=) -type Constraint struct { - Value string - Operator string -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Constraint instead +type Constraint = api.Constraint // HealthCheckConfig the healthcheck configuration for a service -type HealthCheckConfig struct { - Test []string `json:"test,omitempty"` - Timeout *time.Duration `json:"timeout,omitempty"` - Interval *time.Duration `json:"interval,omitempty"` - Retries *uint64 `json:"retries,omitempty"` -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.HealthCheckConfig instead +type HealthCheckConfig = api.HealthCheckConfig // ServiceVolumeConfig are references to a volume used by a service -type ServiceVolumeConfig struct { - Type string `json:"type,omitempty"` - Source string `json:"source,omitempty"` - Target string `json:"target,omitempty"` - ReadOnly bool `json:"read_only,omitempty"` -} - -func (s *StackSpec) clone() *StackSpec { - if s == nil { - return nil - } - result := *s - return &result -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServiceVolumeConfig instead +type ServiceVolumeConfig = api.ServiceVolumeConfig // StackPhase is the deployment phase of a stack -type StackPhase string +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackPhase instead +type StackPhase = api.StackPhase // These are valid conditions of a stack. const ( // StackAvailable means the stack is available. - StackAvailable StackPhase = "Available" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackAvailable instead + StackAvailable StackPhase = api.StackAvailable // StackProgressing means the deployment is progressing. - StackProgressing StackPhase = "Progressing" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackProgressing instead + StackProgressing StackPhase = api.StackProgressing // StackFailure is added in a stack when one of its members fails to be created // or deleted. - StackFailure StackPhase = "Failure" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackFailure instead + StackFailure StackPhase = api.StackFailure ) // StackStatus defines the observed state of Stack -type StackStatus struct { - // Current condition of the stack. - // +optional - Phase StackPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=StackPhase"` - // A human readable message indicating details about the stack. - // +optional - Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` -} - -func (s *StackStatus) clone() *StackStatus { - if s == nil { - return nil - } - result := *s - return &result -} - -// Clone clones a Stack -func (s *Stack) Clone() *Stack { - return s.clone() -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackStatus instead +type StackStatus = api.StackStatus diff --git a/kubernetes/config.go b/kubernetes/config.go index 287a6a6bf0..8ec85c2195 100644 --- a/kubernetes/config.go +++ b/kubernetes/config.go @@ -1,26 +1,8 @@ package kubernetes -import ( - "os" - "path/filepath" - - "github.com/docker/docker/pkg/homedir" - "k8s.io/client-go/tools/clientcmd" -) +import api "github.com/docker/compose-on-kubernetes/api" // NewKubernetesConfig resolves the path to the desired Kubernetes configuration file based on // the KUBECONFIG environment variable and command line flags. -func NewKubernetesConfig(configPath string) clientcmd.ClientConfig { - kubeConfig := configPath - if kubeConfig == "" { - if config := os.Getenv("KUBECONFIG"); config != "" { - kubeConfig = config - } else { - kubeConfig = filepath.Join(homedir.Get(), ".kube/config") - } - } - - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfig}, - &clientcmd.ConfigOverrides{}) -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api.NewKubernetesConfig instead +var NewKubernetesConfig = api.NewKubernetesConfig diff --git a/kubernetes/labels/labels.go b/kubernetes/labels/labels.go index 5709af0361..ef66faa136 100644 --- a/kubernetes/labels/labels.go +++ b/kubernetes/labels/labels.go @@ -1,45 +1,24 @@ package labels -import ( - "fmt" - "strings" -) +import labels "github.com/docker/compose-on-kubernetes/api/labels" const ( // ForServiceName is the label for the service name. - ForServiceName = "com.docker.service.name" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.ForServiceName instead + ForServiceName = labels.ForServiceName // ForStackName is the label for the stack name. - ForStackName = "com.docker.stack.namespace" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.ForStackName instead + ForStackName = labels.ForStackName // ForServiceID is the label for the service id. - ForServiceID = "com.docker.service.id" + // Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.ForServiceID instead + ForServiceID = labels.ForServiceID ) // ForService gives the labels to select a given service in a stack. -func ForService(stackName, serviceName string) map[string]string { - labels := map[string]string{} - - if serviceName != "" { - labels[ForServiceName] = serviceName - } - if stackName != "" { - labels[ForStackName] = stackName - } - if serviceName != "" && stackName != "" { - labels[ForServiceID] = stackName + "-" + serviceName - } - - return labels -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.ForService instead +var ForService = labels.ForService // SelectorForStack gives the labelSelector to use for a given stack. // Specific service names can be passed to narrow down the selection. -func SelectorForStack(stackName string, serviceNames ...string) string { - switch len(serviceNames) { - case 0: - return fmt.Sprintf("%s=%s", ForStackName, stackName) - case 1: - return fmt.Sprintf("%s=%s,%s=%s", ForStackName, stackName, ForServiceName, serviceNames[0]) - default: - return fmt.Sprintf("%s=%s,%s in (%s)", ForStackName, stackName, ForServiceName, strings.Join(serviceNames, ",")) - } -} +// Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.SelectorForStack instead +var SelectorForStack = labels.SelectorForStack diff --git a/kubernetes/labels/labels_test.go b/kubernetes/labels/labels_test.go deleted file mode 100644 index 7815b73d34..0000000000 --- a/kubernetes/labels/labels_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package labels - -import ( - "testing" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestForService(t *testing.T) { - labels := ForService("stack", "service") - - assert.Check(t, is.Len(labels, 3)) - assert.Check(t, is.Equal("stack", labels["com.docker.stack.namespace"])) - assert.Check(t, is.Equal("service", labels["com.docker.service.name"])) - assert.Check(t, is.Equal("stack-service", labels["com.docker.service.id"])) -} - -func TestSelectorForStack(t *testing.T) { - assert.Check(t, is.Equal("com.docker.stack.namespace=demostack", SelectorForStack("demostack"))) - assert.Check(t, is.Equal("com.docker.stack.namespace=stack,com.docker.service.name=service", SelectorForStack("stack", "service"))) - assert.Check(t, is.Equal("com.docker.stack.namespace=stack,com.docker.service.name in (service1,service2)", SelectorForStack("stack", "service1", "service2"))) -} diff --git a/vendor.conf b/vendor.conf index 4cf59ebe2d..97826ddf4f 100755 --- a/vendor.conf +++ b/vendor.conf @@ -1,3 +1,4 @@ +cloud.google.com/go 0ebda48a7f143b1cce9eb37a8c1106ac762a3430 # v0.34.0 github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403 github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 @@ -13,6 +14,7 @@ github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 github.com/docker/docker 6e3113f700dea1bf2785d94731b4b5a1e602d9ab +github.com/docker/compose-on-kubernetes a6086e2369e39c2058a003a7eb42e567ecfd1f03 # v0.4.17 github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 # the docker/go package contains a customized version of canonical/json # and is used by Notary. The package is periodically rebased on current Go versions. @@ -83,6 +85,7 @@ github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d golang.org/x/crypto 0709b304e793a5edb4a2c0145f281ecdc20838a4 golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1 +golang.org/x/oauth2 ef147856a6ddbb60760db74283d2424e98c87bff golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 golang.org/x/text f21a4dfb5e38f5895301dc265a8def02365cc3d0 # v0.3.0 diff --git a/vendor/cloud.google.com/go/LICENSE b/vendor/cloud.google.com/go/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/cloud.google.com/go/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/cloud.google.com/go/README.md b/vendor/cloud.google.com/go/README.md new file mode 100644 index 0000000000..1c8c16046a --- /dev/null +++ b/vendor/cloud.google.com/go/README.md @@ -0,0 +1,505 @@ +# Google Cloud Client Libraries for Go + +[![GoDoc](https://godoc.org/cloud.google.com/go?status.svg)](https://godoc.org/cloud.google.com/go) + +Go packages for [Google Cloud Platform](https://cloud.google.com) services. + +``` go +import "cloud.google.com/go" +``` + +To install the packages on your system, *do not clone the repo*. Instead use + +``` +$ go get -u cloud.google.com/go/... +``` + +**NOTE:** Some of these packages are under development, and may occasionally +make backwards-incompatible changes. + +**NOTE:** Github repo is a mirror of [https://code.googlesource.com/gocloud](https://code.googlesource.com/gocloud). + + * [News](#news) + * [Supported APIs](#supported-apis) + * [Go Versions Supported](#go-versions-supported) + * [Authorization](#authorization) + * [Cloud Datastore](#cloud-datastore-) + * [Cloud Storage](#cloud-storage-) + * [Cloud Pub/Sub](#cloud-pub-sub-) + * [BigQuery](#cloud-bigquery-) + * [Stackdriver Logging](#stackdriver-logging-) + * [Cloud Spanner](#cloud-spanner-) + + +## News + +_7 August 2018_ + +As of November 1, the code in the repo will no longer support Go versions 1.8 +and earlier. No one other than AppEngine users should be on those old versions, +and AppEngine +[Standard](https://groups.google.com/forum/#!topic/google-appengine-go/e7oPNomd7ak) +and +[Flex](https://groups.google.com/forum/#!topic/google-appengine-go/wHsYtxvEbXI) +will stop supporting new deployments with those versions on that date. + + +Changes have been moved to [CHANGES](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CHANGES.md). + + +## Supported APIs + +Google API | Status | Package +---------------------------------------------|--------------|----------------------------------------------------------- +[Asset][cloud-asset] | alpha | [`godoc.org/cloud.google.com/go/asset/v1beta`][cloud-asset-ref] +[BigQuery][cloud-bigquery] | stable | [`godoc.org/cloud.google.com/go/bigquery`][cloud-bigquery-ref] +[Bigtable][cloud-bigtable] | stable | [`godoc.org/cloud.google.com/go/bigtable`][cloud-bigtable-ref] +[Cloudtasks][cloud-tasks] | beta | [`godoc.org/cloud.google.com/go/cloudtasks/apiv2beta3`][cloud-tasks-ref] +[Container][cloud-container] | stable | [`godoc.org/cloud.google.com/go/container/apiv1`][cloud-container-ref] +[ContainerAnalysis][cloud-containeranalysis] | beta | [`godoc.org/cloud.google.com/go/containeranalysis/apiv1beta1`][cloud-containeranalysis-ref] +[Dataproc][cloud-dataproc] | stable | [`godoc.org/cloud.google.com/go/dataproc/apiv1`][cloud-dataproc-ref] +[Datastore][cloud-datastore] | stable | [`godoc.org/cloud.google.com/go/datastore`][cloud-datastore-ref] +[Debugger][cloud-debugger] | alpha | [`godoc.org/cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref] +[Dialogflow][cloud-dialogflow] | alpha | [`godoc.org/cloud.google.com/go/dialogflow/apiv2`][cloud-dialogflow-ref] +[Data Loss Prevention][cloud-dlp] | alpha | [`godoc.org/cloud.google.com/go/dlp/apiv2`][cloud-dlp-ref] +[ErrorReporting][cloud-errors] | alpha | [`godoc.org/cloud.google.com/go/errorreporting`][cloud-errors-ref] +[Firestore][cloud-firestore] | beta | [`godoc.org/cloud.google.com/go/firestore`][cloud-firestore-ref] +[IAM][cloud-iam] | stable | [`godoc.org/cloud.google.com/go/iam`][cloud-iam-ref] +[KMS][cloud-kms] | stable | [`godoc.org/cloud.google.com/go/kms`][cloud-kms-ref] +[Natural Language][cloud-natural-language] | stable | [`godoc.org/cloud.google.com/go/language/apiv1`][cloud-natural-language-ref] +[Logging][cloud-logging] | stable | [`godoc.org/cloud.google.com/go/logging`][cloud-logging-ref] +[Monitoring][cloud-monitoring] | alpha | [`godoc.org/cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref] +[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/compute/docs/oslogin/rest`][cloud-oslogin-ref] +[Pub/Sub][cloud-pubsub] | stable | [`godoc.org/cloud.google.com/go/pubsub`][cloud-pubsub-ref] +[Memorystore][cloud-memorystore] | stable | [`godoc.org/cloud.google.com/go/redis/apiv1beta1`][cloud-memorystore-ref] +[Spanner][cloud-spanner] | stable | [`godoc.org/cloud.google.com/go/spanner`][cloud-spanner-ref] +[Speech][cloud-speech] | stable | [`godoc.org/cloud.google.com/go/speech/apiv1`][cloud-speech-ref] +[Storage][cloud-storage] | stable | [`godoc.org/cloud.google.com/go/storage`][cloud-storage-ref] +[Text To Speech][cloud-texttospeech] | alpha | [`godoc.org/cloud.google.com/go/texttospeech/apiv1`][cloud-storage-ref] +[Trace][cloud-trace] | alpha | [`godoc.org/cloud.google.com/go/trace/apiv2`][cloud-translation-ref] +[Translation][cloud-translation] | stable | [`godoc.org/cloud.google.com/go/translate`][cloud-translation-ref] +[Video Intelligence][cloud-video] | alpha | [`godoc.org/cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref] +[Vision][cloud-vision] | stable | [`godoc.org/cloud.google.com/go/vision/apiv1`][cloud-vision-ref] + +> **Alpha status**: the API is still being actively developed. As a +> result, it might change in backward-incompatible ways and is not recommended +> for production use. +> +> **Beta status**: the API is largely complete, but still has outstanding +> features and bugs to be addressed. There may be minor backwards-incompatible +> changes where necessary. +> +> **Stable status**: the API is mature and ready for production use. We will +> continue addressing bugs and feature requests. + +Documentation and examples are available at +https://godoc.org/cloud.google.com/go + +Visit or join the +[google-api-go-announce group](https://groups.google.com/forum/#!forum/google-api-go-announce) +for updates on these packages. + +## Go Versions Supported + +We support the two most recent major versions of Go. If Google App Engine uses +an older version, we support that as well. + +## Authorization + +By default, each API will use [Google Application Default Credentials][default-creds] +for authorization credentials used in calling the API endpoints. This will allow your +application to run in many environments without requiring explicit configuration. + +[snip]:# (auth) +```go +client, err := storage.NewClient(ctx) +``` + +To authorize using a +[JSON key file](https://cloud.google.com/iam/docs/managing-service-account-keys), +pass +[`option.WithCredentialsFile`](https://godoc.org/google.golang.org/api/option#WithCredentialsFile) +to the `NewClient` function of the desired package. For example: + +[snip]:# (auth-JSON) +```go +client, err := storage.NewClient(ctx, option.WithCredentialsFile("path/to/keyfile.json")) +``` + +You can exert more control over authorization by using the +[`golang.org/x/oauth2`](https://godoc.org/golang.org/x/oauth2) package to +create an `oauth2.TokenSource`. Then pass +[`option.WithTokenSource`](https://godoc.org/google.golang.org/api/option#WithTokenSource) +to the `NewClient` function: +[snip]:# (auth-ts) +```go +tokenSource := ... +client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource)) +``` + +## Cloud Datastore [![GoDoc](https://godoc.org/cloud.google.com/go/datastore?status.svg)](https://godoc.org/cloud.google.com/go/datastore) + +- [About Cloud Datastore][cloud-datastore] +- [Activating the API for your project][cloud-datastore-activation] +- [API documentation][cloud-datastore-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/datastore) +- [Complete sample program](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/datastore/tasks) + +### Example Usage + +First create a `datastore.Client` to use throughout your application: + +[snip]:# (datastore-1) +```go +client, err := datastore.NewClient(ctx, "my-project-id") +if err != nil { + log.Fatal(err) +} +``` + +Then use that client to interact with the API: + +[snip]:# (datastore-2) +```go +type Post struct { + Title string + Body string `datastore:",noindex"` + PublishedAt time.Time +} +keys := []*datastore.Key{ + datastore.NameKey("Post", "post1", nil), + datastore.NameKey("Post", "post2", nil), +} +posts := []*Post{ + {Title: "Post 1", Body: "...", PublishedAt: time.Now()}, + {Title: "Post 2", Body: "...", PublishedAt: time.Now()}, +} +if _, err := client.PutMulti(ctx, keys, posts); err != nil { + log.Fatal(err) +} +``` + +## Cloud Storage [![GoDoc](https://godoc.org/cloud.google.com/go/storage?status.svg)](https://godoc.org/cloud.google.com/go/storage) + +- [About Cloud Storage][cloud-storage] +- [API documentation][cloud-storage-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/storage) +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/storage) + +### Example Usage + +First create a `storage.Client` to use throughout your application: + +[snip]:# (storage-1) +```go +client, err := storage.NewClient(ctx) +if err != nil { + log.Fatal(err) +} +``` + +[snip]:# (storage-2) +```go +// Read the object1 from bucket. +rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx) +if err != nil { + log.Fatal(err) +} +defer rc.Close() +body, err := ioutil.ReadAll(rc) +if err != nil { + log.Fatal(err) +} +``` + +## Cloud Pub/Sub [![GoDoc](https://godoc.org/cloud.google.com/go/pubsub?status.svg)](https://godoc.org/cloud.google.com/go/pubsub) + +- [About Cloud Pubsub][cloud-pubsub] +- [API documentation][cloud-pubsub-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/pubsub) +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/pubsub) + +### Example Usage + +First create a `pubsub.Client` to use throughout your application: + +[snip]:# (pubsub-1) +```go +client, err := pubsub.NewClient(ctx, "project-id") +if err != nil { + log.Fatal(err) +} +``` + +Then use the client to publish and subscribe: + +[snip]:# (pubsub-2) +```go +// Publish "hello world" on topic1. +topic := client.Topic("topic1") +res := topic.Publish(ctx, &pubsub.Message{ + Data: []byte("hello world"), +}) +// The publish happens asynchronously. +// Later, you can get the result from res: +... +msgID, err := res.Get(ctx) +if err != nil { + log.Fatal(err) +} + +// Use a callback to receive messages via subscription1. +sub := client.Subscription("subscription1") +err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { + fmt.Println(m.Data) + m.Ack() // Acknowledge that we've consumed the message. +}) +if err != nil { + log.Println(err) +} +``` + +## BigQuery [![GoDoc](https://godoc.org/cloud.google.com/go/bigquery?status.svg)](https://godoc.org/cloud.google.com/go/bigquery) + +- [About BigQuery][cloud-bigquery] +- [API documentation][cloud-bigquery-docs] +- [Go client documentation][cloud-bigquery-ref] +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/bigquery) + +### Example Usage + +First create a `bigquery.Client` to use throughout your application: +[snip]:# (bq-1) +```go +c, err := bigquery.NewClient(ctx, "my-project-ID") +if err != nil { + // TODO: Handle error. +} +``` + +Then use that client to interact with the API: +[snip]:# (bq-2) +```go +// Construct a query. +q := c.Query(` + SELECT year, SUM(number) + FROM [bigquery-public-data:usa_names.usa_1910_2013] + WHERE name = "William" + GROUP BY year + ORDER BY year +`) +// Execute the query. +it, err := q.Read(ctx) +if err != nil { + // TODO: Handle error. +} +// Iterate through the results. +for { + var values []bigquery.Value + err := it.Next(&values) + if err == iterator.Done { + break + } + if err != nil { + // TODO: Handle error. + } + fmt.Println(values) +} +``` + + +## Stackdriver Logging [![GoDoc](https://godoc.org/cloud.google.com/go/logging?status.svg)](https://godoc.org/cloud.google.com/go/logging) + +- [About Stackdriver Logging][cloud-logging] +- [API documentation][cloud-logging-docs] +- [Go client documentation][cloud-logging-ref] +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging) + +### Example Usage + +First create a `logging.Client` to use throughout your application: +[snip]:# (logging-1) +```go +ctx := context.Background() +client, err := logging.NewClient(ctx, "my-project") +if err != nil { + // TODO: Handle error. +} +``` + +Usually, you'll want to add log entries to a buffer to be periodically flushed +(automatically and asynchronously) to the Stackdriver Logging service. +[snip]:# (logging-2) +```go +logger := client.Logger("my-log") +logger.Log(logging.Entry{Payload: "something happened!"}) +``` + +Close your client before your program exits, to flush any buffered log entries. +[snip]:# (logging-3) +```go +err = client.Close() +if err != nil { + // TODO: Handle error. +} +``` + +## Cloud Spanner [![GoDoc](https://godoc.org/cloud.google.com/go/spanner?status.svg)](https://godoc.org/cloud.google.com/go/spanner) + +- [About Cloud Spanner][cloud-spanner] +- [API documentation][cloud-spanner-docs] +- [Go client documentation](https://godoc.org/cloud.google.com/go/spanner) + +### Example Usage + +First create a `spanner.Client` to use throughout your application: + +[snip]:# (spanner-1) +```go +client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D") +if err != nil { + log.Fatal(err) +} +``` + +[snip]:# (spanner-2) +```go +// Simple Reads And Writes +_, err = client.Apply(ctx, []*spanner.Mutation{ + spanner.Insert("Users", + []string{"name", "email"}, + []interface{}{"alice", "a@example.com"})}) +if err != nil { + log.Fatal(err) +} +row, err := client.Single().ReadRow(ctx, "Users", + spanner.Key{"alice"}, []string{"email"}) +if err != nil { + log.Fatal(err) +} +``` + + +## Contributing + +Contributions are welcome. Please, see the +[CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md) +document for details. We're using Gerrit for our code reviews. Please don't open pull +requests against this repo, new pull requests will be automatically closed. + +Please note that this project is released with a Contributor Code of Conduct. +By participating in this project you agree to abide by its terms. +See [Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/CONTRIBUTING.md#contributor-code-of-conduct) +for more information. + +[cloud-datastore]: https://cloud.google.com/datastore/ +[cloud-datastore-ref]: https://godoc.org/cloud.google.com/go/datastore +[cloud-datastore-docs]: https://cloud.google.com/datastore/docs +[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate + +[cloud-firestore]: https://cloud.google.com/firestore/ +[cloud-firestore-ref]: https://godoc.org/cloud.google.com/go/firestore +[cloud-firestore-docs]: https://cloud.google.com/firestore/docs +[cloud-firestore-activation]: https://cloud.google.com/firestore/docs/activate + +[cloud-pubsub]: https://cloud.google.com/pubsub/ +[cloud-pubsub-ref]: https://godoc.org/cloud.google.com/go/pubsub +[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs + +[cloud-storage]: https://cloud.google.com/storage/ +[cloud-storage-ref]: https://godoc.org/cloud.google.com/go/storage +[cloud-storage-docs]: https://cloud.google.com/storage/docs +[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets + +[cloud-bigtable]: https://cloud.google.com/bigtable/ +[cloud-bigtable-ref]: https://godoc.org/cloud.google.com/go/bigtable + +[cloud-bigquery]: https://cloud.google.com/bigquery/ +[cloud-bigquery-docs]: https://cloud.google.com/bigquery/docs +[cloud-bigquery-ref]: https://godoc.org/cloud.google.com/go/bigquery + +[cloud-logging]: https://cloud.google.com/logging/ +[cloud-logging-docs]: https://cloud.google.com/logging/docs +[cloud-logging-ref]: https://godoc.org/cloud.google.com/go/logging + +[cloud-monitoring]: https://cloud.google.com/monitoring/ +[cloud-monitoring-ref]: https://godoc.org/cloud.google.com/go/monitoring/apiv3 + +[cloud-vision]: https://cloud.google.com/vision +[cloud-vision-ref]: https://godoc.org/cloud.google.com/go/vision/apiv1 + +[cloud-language]: https://cloud.google.com/natural-language +[cloud-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 + +[cloud-oslogin]: https://cloud.google.com/compute/docs/oslogin/rest +[cloud-oslogin-ref]: https://cloud.google.com/compute/docs/oslogin/rest + +[cloud-speech]: https://cloud.google.com/speech +[cloud-speech-ref]: https://godoc.org/cloud.google.com/go/speech/apiv1 + +[cloud-spanner]: https://cloud.google.com/spanner/ +[cloud-spanner-ref]: https://godoc.org/cloud.google.com/go/spanner +[cloud-spanner-docs]: https://cloud.google.com/spanner/docs + +[cloud-translation]: https://cloud.google.com/translation +[cloud-translation-ref]: https://godoc.org/cloud.google.com/go/translation + +[cloud-video]: https://cloud.google.com/video-intelligence/ +[cloud-video-ref]: https://godoc.org/cloud.google.com/go/videointelligence/apiv1beta1 + +[cloud-errors]: https://cloud.google.com/error-reporting/ +[cloud-errors-ref]: https://godoc.org/cloud.google.com/go/errorreporting + +[cloud-container]: https://cloud.google.com/containers/ +[cloud-container-ref]: https://godoc.org/cloud.google.com/go/container/apiv1 + +[cloud-debugger]: https://cloud.google.com/debugger/ +[cloud-debugger-ref]: https://godoc.org/cloud.google.com/go/debugger/apiv2 + +[cloud-dlp]: https://cloud.google.com/dlp/ +[cloud-dlp-ref]: https://godoc.org/cloud.google.com/go/dlp/apiv2beta1 + +[default-creds]: https://developers.google.com/identity/protocols/application-default-credentials + +[cloud-dataproc]: https://cloud.google.com/dataproc/ +[cloud-dataproc-docs]: https://cloud.google.com/dataproc/docs +[cloud-dataproc-ref]: https://godoc.org/cloud.google.com/go/dataproc/apiv1 + +[cloud-iam]: https://cloud.google.com/iam/ +[cloud-iam-docs]: https://cloud.google.com/iam/docs +[cloud-iam-ref]: https://godoc.org/cloud.google.com/go/iam + +[cloud-kms]: https://cloud.google.com/kms/ +[cloud-kms-docs]: https://cloud.google.com/kms/docs +[cloud-kms-ref]: https://godoc.org/cloud.google.com/go/kms/apiv1 + +[cloud-natural-language]: https://cloud.google.com/natural-language/ +[cloud-natural-language-docs]: https://cloud.google.com/natural-language/docs +[cloud-natural-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 + +[cloud-memorystore]: https://cloud.google.com/memorystore/ +[cloud-memorystore-docs]: https://cloud.google.com/memorystore/docs +[cloud-memorystore-ref]: https://godoc.org/cloud.google.com/go/redis/apiv1beta1 + +[cloud-texttospeech]: https://cloud.google.com/texttospeech/ +[cloud-texttospeech-docs]: https://cloud.google.com/texttospeech/docs +[cloud-texttospeech-ref]: https://godoc.org/cloud.google.com/go/texttospeech/apiv1 + +[cloud-trace]: https://cloud.google.com/trace/ +[cloud-trace-docs]: https://cloud.google.com/trace/docs +[cloud-trace-ref]: https://godoc.org/cloud.google.com/go/trace/apiv1 + +[cloud-dialogflow]: https://cloud.google.com/dialogflow-enterprise/ +[cloud-dialogflow-docs]: https://cloud.google.com/dialogflow-enterprise/docs/ +[cloud-dialogflow-ref]: https://godoc.org/cloud.google.com/go/dialogflow/apiv2 + +[cloud-containeranalysis]: https://cloud.google.com/container-registry/docs/container-analysis +[cloud-containeranalysis-docs]: https://cloud.google.com/container-analysis/api/reference/rest/ +[cloud-containeranalysis-ref]: https://godoc.org/cloud.google.com/go/devtools/containeranalysis/apiv1beta1 + +[cloud-asset]: https://cloud.google.com/security-command-center/docs/how-to-asset-inventory +[cloud-asset-docs]: https://cloud.google.com/security-command-center/docs/how-to-asset-inventory +[cloud-asset-ref]: https://godoc.org/cloud.google.com/go/asset/apiv1 + +[cloud-tasks]: https://cloud.google.com/tasks/ +[cloud-tasks-ref]: https://godoc.org/cloud.google.com/go/cloudtasks/apiv2beta3 diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h new file mode 100644 index 0000000000..156c0b87b0 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h @@ -0,0 +1,9 @@ +// +build ignore + +// Empty include file to generate z symbols + + + + + +// EOF diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go new file mode 100644 index 0000000000..dcd7015bb7 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go @@ -0,0 +1,472 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Line tables + */ + +package gosym + +import ( + "encoding/binary" + "sync" +) + +// A LineTable is a data structure mapping program counters to line numbers. +// +// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable, +// and the line number corresponded to a numbering of all source lines in the +// program, across all files. That absolute line number would then have to be +// converted separately to a file name and line number within the file. +// +// In Go 1.2, the format of the data changed so that there is a single LineTable +// for the entire program, shared by all Funcs, and there are no absolute line +// numbers, just line numbers within specific files. +// +// For the most part, LineTable's methods should be treated as an internal +// detail of the package; callers should use the methods on Table instead. +type LineTable struct { + Data []byte + PC uint64 + Line int + + // Go 1.2 state + mu sync.Mutex + go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes + binary binary.ByteOrder + quantum uint32 + ptrsize uint32 + functab []byte + nfunctab uint32 + filetab []byte + nfiletab uint32 + fileMap map[string]uint32 +} + +// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, +// but we have no idea whether we're using arm or not. This only +// matters in the old (pre-Go 1.2) symbol table format, so it's not worth +// fixing. +const oldQuantum = 1 + +func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) { + // The PC/line table can be thought of as a sequence of + // * + // batches. Each update batch results in a (pc, line) pair, + // where line applies to every PC from pc up to but not + // including the pc of the next pair. + // + // Here we process each update individually, which simplifies + // the code, but makes the corner cases more confusing. + b, pc, line = t.Data, t.PC, t.Line + for pc <= targetPC && line != targetLine && len(b) > 0 { + code := b[0] + b = b[1:] + switch { + case code == 0: + if len(b) < 4 { + b = b[0:0] + break + } + val := binary.BigEndian.Uint32(b) + b = b[4:] + line += int(val) + case code <= 64: + line += int(code) + case code <= 128: + line -= int(code - 64) + default: + pc += oldQuantum * uint64(code-128) + continue + } + pc += oldQuantum + } + return b, pc, line +} + +func (t *LineTable) slice(pc uint64) *LineTable { + data, pc, line := t.parse(pc, -1) + return &LineTable{Data: data, PC: pc, Line: line} +} + +// PCToLine returns the line number for the given program counter. +// Callers should use Table's PCToLine method instead. +func (t *LineTable) PCToLine(pc uint64) int { + if t.isGo12() { + return t.go12PCToLine(pc) + } + _, _, line := t.parse(pc, -1) + return line +} + +// LineToPC returns the program counter for the given line number, +// considering only program counters before maxpc. +// Callers should use Table's LineToPC method instead. +func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { + if t.isGo12() { + return 0 + } + _, pc, line1 := t.parse(maxpc, line) + if line1 != line { + return 0 + } + // Subtract quantum from PC to account for post-line increment + return pc - oldQuantum +} + +// NewLineTable returns a new PC/line table +// corresponding to the encoded data. +// Text must be the start address of the +// corresponding text segment. +func NewLineTable(data []byte, text uint64) *LineTable { + return &LineTable{Data: data, PC: text, Line: 0} +} + +// Go 1.2 symbol table format. +// See golang.org/s/go12symtab. +// +// A general note about the methods here: rather than try to avoid +// index out of bounds errors, we trust Go to detect them, and then +// we recover from the panics and treat them as indicative of a malformed +// or incomplete table. +// +// The methods called by symtab.go, which begin with "go12" prefixes, +// are expected to have that recovery logic. + +// isGo12 reports whether this is a Go 1.2 (or later) symbol table. +func (t *LineTable) isGo12() bool { + t.go12Init() + return t.go12 == 1 +} + +const go12magic = 0xfffffffb + +// uintptr returns the pointer-sized value encoded at b. +// The pointer size is dictated by the table being read. +func (t *LineTable) uintptr(b []byte) uint64 { + if t.ptrsize == 4 { + return uint64(t.binary.Uint32(b)) + } + return t.binary.Uint64(b) +} + +// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table. +func (t *LineTable) go12Init() { + t.mu.Lock() + defer t.mu.Unlock() + if t.go12 != 0 { + return + } + + defer func() { + // If we panic parsing, assume it's not a Go 1.2 symbol table. + recover() + }() + + // Check header: 4-byte magic, two zeros, pc quantum, pointer size. + t.go12 = -1 // not Go 1.2 until proven otherwise + if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || + (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size + return + } + + switch uint32(go12magic) { + case binary.LittleEndian.Uint32(t.Data): + t.binary = binary.LittleEndian + case binary.BigEndian.Uint32(t.Data): + t.binary = binary.BigEndian + default: + return + } + + t.quantum = uint32(t.Data[6]) + t.ptrsize = uint32(t.Data[7]) + + t.nfunctab = uint32(t.uintptr(t.Data[8:])) + t.functab = t.Data[8+t.ptrsize:] + functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize + fileoff := t.binary.Uint32(t.functab[functabsize:]) + t.functab = t.functab[:functabsize] + t.filetab = t.Data[fileoff:] + t.nfiletab = t.binary.Uint32(t.filetab) + t.filetab = t.filetab[:t.nfiletab*4] + + t.go12 = 1 // so far so good +} + +// go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table. +func (t *LineTable) go12Funcs() []Func { + // Assume it is malformed and return nil on error. + defer func() { + recover() + }() + + n := len(t.functab) / int(t.ptrsize) / 2 + funcs := make([]Func, n) + for i := range funcs { + f := &funcs[i] + f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):])) + f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])) + info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] + f.LineTable = t + f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) + f.Sym = &Sym{ + Value: f.Entry, + Type: 'T', + Name: t.string(t.binary.Uint32(info[t.ptrsize:])), + GoType: 0, + Func: f, + } + } + return funcs +} + +// findFunc returns the func corresponding to the given program counter. +func (t *LineTable) findFunc(pc uint64) []byte { + if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) { + return nil + } + + // The function table is a list of 2*nfunctab+1 uintptrs, + // alternating program counters and offsets to func structures. + f := t.functab + nf := t.nfunctab + for nf > 0 { + m := nf / 2 + fm := f[2*t.ptrsize*m:] + if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) { + return t.Data[t.uintptr(fm[t.ptrsize:]):] + } else if pc < t.uintptr(fm) { + nf = m + } else { + f = f[(m+1)*2*t.ptrsize:] + nf -= m + 1 + } + } + return nil +} + +// readvarint reads, removes, and returns a varint from *pp. +func (t *LineTable) readvarint(pp *[]byte) uint32 { + var v, shift uint32 + p := *pp + for shift = 0; ; shift += 7 { + b := p[0] + p = p[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + } + *pp = p + return v +} + +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + for i := off; ; i++ { + if t.Data[i] == 0 { + return string(t.Data[off:i]) + } + } +} + +// step advances to the next pc, value pair in the encoded table. +func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { + uvdelta := t.readvarint(p) + if uvdelta == 0 && !first { + return false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + pcdelta := t.readvarint(p) * t.quantum + *pc += uint64(pcdelta) + *val += vdelta + return true +} + +// pcvalue reports the value associated with the target pc. +// off is the offset to the beginning of the pc-value table, +// and entry is the start PC for the corresponding function. +func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { + if off == 0 { + return -1 + } + p := t.Data[off:] + + val := int32(-1) + pc := entry + for t.step(&p, &pc, &val, pc == entry) { + if targetpc < pc { + return val + } + } + return -1 +} + +// findFileLine scans one function in the binary looking for a +// program counter in the given file on the given line. +// It does so by running the pc-value tables mapping program counter +// to file number. Since most functions come from a single file, these +// are usually short and quick to scan. If a file match is found, then the +// code goes to the expense of looking for a simultaneous line number match. +func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 { + if filetab == 0 || linetab == 0 { + return 0 + } + + fp := t.Data[filetab:] + fl := t.Data[linetab:] + fileVal := int32(-1) + filePC := entry + lineVal := int32(-1) + linePC := entry + fileStartPC := filePC + for t.step(&fp, &filePC, &fileVal, filePC == entry) { + if fileVal == filenum && fileStartPC < filePC { + // fileVal is in effect starting at fileStartPC up to + // but not including filePC, and it's the file we want. + // Run the PC table looking for a matching line number + // or until we reach filePC. + lineStartPC := linePC + for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) { + // lineVal is in effect until linePC, and lineStartPC < filePC. + if lineVal == line { + if fileStartPC <= lineStartPC { + return lineStartPC + } + if fileStartPC < linePC { + return fileStartPC + } + } + lineStartPC = linePC + } + } + fileStartPC = filePC + } + return 0 +} + +// go12PCToLine maps program counter to line number for the Go 1.2 pcln table. +func (t *LineTable) go12PCToLine(pc uint64) (line int) { + return t.go12PCToVal(pc, t.ptrsize+5*4) +} + +// go12PCToSPAdj maps program counter to Stack Pointer adjustment for the Go 1.2 pcln table. +func (t *LineTable) go12PCToSPAdj(pc uint64) (spadj int) { + return t.go12PCToVal(pc, t.ptrsize+3*4) +} + +func (t *LineTable) go12PCToVal(pc uint64, fOffset uint32) (val int) { + defer func() { + if recover() != nil { + val = -1 + } + }() + + f := t.findFunc(pc) + if f == nil { + return -1 + } + entry := t.uintptr(f) + linetab := t.binary.Uint32(f[fOffset:]) + return int(t.pcvalue(linetab, entry, pc)) +} + +// go12PCToFile maps program counter to file name for the Go 1.2 pcln table. +func (t *LineTable) go12PCToFile(pc uint64) (file string) { + defer func() { + if recover() != nil { + file = "" + } + }() + + f := t.findFunc(pc) + if f == nil { + return "" + } + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + fno := t.pcvalue(filetab, entry, pc) + if fno <= 0 { + return "" + } + return t.string(t.binary.Uint32(t.filetab[4*fno:])) +} + +// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. +func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { + defer func() { + if recover() != nil { + pc = 0 + } + }() + + t.initFileMap() + filenum := t.fileMap[file] + if filenum == 0 { + return 0 + } + + // Scan all functions. + // If this turns out to be a bottleneck, we could build a map[int32][]int32 + // mapping file number to a list of functions with code from that file. + for i := uint32(0); i < t.nfunctab; i++ { + f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) + if pc != 0 { + return pc + } + } + return 0 +} + +// initFileMap initializes the map from file name to file number. +func (t *LineTable) initFileMap() { + t.mu.Lock() + defer t.mu.Unlock() + + if t.fileMap != nil { + return + } + m := make(map[string]uint32) + + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + t.fileMap = m +} + +// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable. +// Every key maps to obj. That's not a very interesting map, but it provides +// a way for callers to obtain the list of files in the program. +func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) { + defer func() { + recover() + }() + + t.initFileMap() + for file := range t.fileMap { + m[file] = obj + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go new file mode 100644 index 0000000000..2c83b841cf --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go @@ -0,0 +1,731 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package gosym implements access to the Go symbol +// and line number tables embedded in Go binaries generated +// by the gc compilers. +package gosym + +// The table format is a variant of the format used in Plan 9's a.out +// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out. +// The best reference for the differences between the Plan 9 format +// and the Go format is the runtime source, specifically ../../runtime/symtab.c. + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "strings" +) + +/* + * Symbols + */ + +// A Sym represents a single symbol table entry. +type Sym struct { + Value uint64 + Type byte + Name string + GoType uint64 + // If this symbol if a function symbol, the corresponding Func + Func *Func +} + +// Static reports whether this symbol is static (not visible outside its file). +func (s *Sym) Static() bool { return s.Type >= 'a' } + +// PackageName returns the package part of the symbol name, +// or the empty string if there is none. +func (s *Sym) PackageName() string { + if i := strings.Index(s.Name, "."); i != -1 { + return s.Name[0:i] + } + return "" +} + +// ReceiverName returns the receiver type name of this symbol, +// or the empty string if there is none. +func (s *Sym) ReceiverName() string { + l := strings.Index(s.Name, ".") + r := strings.LastIndex(s.Name, ".") + if l == -1 || r == -1 || l == r { + return "" + } + return s.Name[l+1 : r] +} + +// BaseName returns the symbol name without the package or receiver name. +func (s *Sym) BaseName() string { + if i := strings.LastIndex(s.Name, "."); i != -1 { + return s.Name[i+1:] + } + return s.Name +} + +// A Func collects information about a single function. +type Func struct { + Entry uint64 + *Sym + End uint64 + Params []*Sym + Locals []*Sym + FrameSize int + LineTable *LineTable + Obj *Obj +} + +// An Obj represents a collection of functions in a symbol table. +// +// The exact method of division of a binary into separate Objs is an internal detail +// of the symbol table format. +// +// In early versions of Go each source file became a different Obj. +// +// In Go 1 and Go 1.1, each package produced one Obj for all Go sources +// and one Obj per C source file. +// +// In Go 1.2, there is a single Obj for the entire program. +type Obj struct { + // Funcs is a list of functions in the Obj. + Funcs []Func + + // In Go 1.1 and earlier, Paths is a list of symbols corresponding + // to the source file names that produced the Obj. + // In Go 1.2, Paths is nil. + // Use the keys of Table.Files to obtain a list of source files. + Paths []Sym // meta +} + +/* + * Symbol tables + */ + +// Table represents a Go symbol table. It stores all of the +// symbols decoded from the program and provides methods to translate +// between symbols, names, and addresses. +type Table struct { + Syms []Sym + Funcs []Func + Files map[string]*Obj // nil for Go 1.2 and later binaries + Objs []Obj // nil for Go 1.2 and later binaries + + go12line *LineTable // Go 1.2 line number table +} + +type sym struct { + value uint64 + gotype uint64 + typ byte + name []byte +} + +var ( + littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} + bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +) + +func walksymtab(data []byte, fn func(sym) error) error { + if len(data) == 0 { // missing symtab is okay + return nil + } + var order binary.ByteOrder = binary.BigEndian + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. + data = data[6:] + order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] + } + var s sym + p := data + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] + } + + // Name. + var i int + var nnul int + for i = 0; i < len(p); i++ { + if p[i] == 0 { + nnul = 1 + break + } + } + switch typ { + case 'z', 'Z': + p = p[i+nnul:] + for i = 0; i+2 <= len(p); i += 2 { + if p[i] == 0 && p[i+1] == 0 { + nnul = 2 + break + } + } + } + if len(p) < i+nnul { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.name = p[0:i] + i += nnul + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } + fn(s) + } + return nil +} + +// NewTable decodes the Go symbol table in data, +// returning an in-memory representation. +func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { + var n int + err := walksymtab(symtab, func(s sym) error { + n++ + return nil + }) + if err != nil { + return nil, err + } + + var t Table + if pcln.isGo12() { + t.go12line = pcln + } + fname := make(map[uint16]string) + t.Syms = make([]Sym, 0, n) + nf := 0 + nz := 0 + lasttyp := uint8(0) + err = walksymtab(symtab, func(s sym) error { + n := len(t.Syms) + t.Syms = t.Syms[0 : n+1] + ts := &t.Syms[n] + ts.Type = s.typ + ts.Value = uint64(s.value) + ts.GoType = uint64(s.gotype) + switch s.typ { + default: + // rewrite name to use . instead of · (c2 b7) + w := 0 + b := s.name + for i := 0; i < len(b); i++ { + if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 { + i++ + b[i] = '.' + } + b[w] = b[i] + w++ + } + ts.Name = string(s.name[0:w]) + case 'z', 'Z': + if lasttyp != 'z' && lasttyp != 'Z' { + nz++ + } + for i := 0; i < len(s.name); i += 2 { + eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) + elt, ok := fname[eltIdx] + if !ok { + return &DecodingError{-1, "bad filename code", eltIdx} + } + if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { + ts.Name += "/" + } + ts.Name += elt + } + } + switch s.typ { + case 'T', 't', 'L', 'l': + nf++ + case 'f': + fname[uint16(s.value)] = ts.Name + } + lasttyp = s.typ + return nil + }) + if err != nil { + return nil, err + } + + t.Funcs = make([]Func, 0, nf) + t.Files = make(map[string]*Obj) + + var obj *Obj + if t.go12line != nil { + // Put all functions into one Obj. + t.Objs = make([]Obj, 1) + obj = &t.Objs[0] + t.go12line.go12MapFiles(t.Files, obj) + } else { + t.Objs = make([]Obj, 0, nz) + } + + // Count text symbols and attach frame sizes, parameters, and + // locals to them. Also, find object file boundaries. + lastf := 0 + for i := 0; i < len(t.Syms); i++ { + sym := &t.Syms[i] + switch sym.Type { + case 'Z', 'z': // path symbol + if t.go12line != nil { + // Go 1.2 binaries have the file information elsewhere. Ignore. + break + } + // Finish the current object + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + lastf = len(t.Funcs) + + // Start new object + n := len(t.Objs) + t.Objs = t.Objs[0 : n+1] + obj = &t.Objs[n] + + // Count & copy path symbols + var end int + for end = i + 1; end < len(t.Syms); end++ { + if c := t.Syms[end].Type; c != 'Z' && c != 'z' { + break + } + } + obj.Paths = t.Syms[i:end] + i = end - 1 // loop will i++ + + // Record file names + depth := 0 + for j := range obj.Paths { + s := &obj.Paths[j] + if s.Name == "" { + depth-- + } else { + if depth == 0 { + t.Files[s.Name] = obj + } + depth++ + } + } + + case 'T', 't', 'L', 'l': // text symbol + if n := len(t.Funcs); n > 0 { + t.Funcs[n-1].End = sym.Value + } + if sym.Name == "etext" { + continue + } + + // Count parameter and local (auto) syms + var np, na int + var end int + countloop: + for end = i + 1; end < len(t.Syms); end++ { + switch t.Syms[end].Type { + case 'T', 't', 'L', 'l', 'Z', 'z': + break countloop + case 'p': + np++ + case 'a': + na++ + } + } + + // Fill in the function symbol + n := len(t.Funcs) + t.Funcs = t.Funcs[0 : n+1] + fn := &t.Funcs[n] + sym.Func = fn + fn.Params = make([]*Sym, 0, np) + fn.Locals = make([]*Sym, 0, na) + fn.Sym = sym + fn.Entry = sym.Value + fn.Obj = obj + if t.go12line != nil { + // All functions share the same line table. + // It knows how to narrow down to a specific + // function quickly. + fn.LineTable = t.go12line + } else if pcln != nil { + fn.LineTable = pcln.slice(fn.Entry) + pcln = fn.LineTable + } + for j := i; j < end; j++ { + s := &t.Syms[j] + switch s.Type { + case 'm': + fn.FrameSize = int(s.Value) + case 'p': + n := len(fn.Params) + fn.Params = fn.Params[0 : n+1] + fn.Params[n] = s + case 'a': + n := len(fn.Locals) + fn.Locals = fn.Locals[0 : n+1] + fn.Locals[n] = s + } + } + i = end - 1 // loop will i++ + } + } + + if t.go12line != nil && nf == 0 { + t.Funcs = t.go12line.go12Funcs() + } + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + return &t, nil +} + +// PCToFunc returns the function containing the program counter pc, +// or nil if there is no such function. +func (t *Table) PCToFunc(pc uint64) *Func { + funcs := t.Funcs + for len(funcs) > 0 { + m := len(funcs) / 2 + fn := &funcs[m] + switch { + case pc < fn.Entry: + funcs = funcs[0:m] + case fn.Entry <= pc && pc < fn.End: + return fn + default: + funcs = funcs[m+1:] + } + } + return nil +} + +// PCToLine looks up line number information for a program counter. +// If there is no information, it returns fn == nil. +func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { + if fn = t.PCToFunc(pc); fn == nil { + return + } + if t.go12line != nil { + file = t.go12line.go12PCToFile(pc) + line = t.go12line.go12PCToLine(pc) + } else { + file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) + } + return +} + +// PCToSPAdj returns the stack pointer adjustment for a program counter. +func (t *Table) PCToSPAdj(pc uint64) (spadj int) { + if fn := t.PCToFunc(pc); fn == nil { + return 0 + } + if t.go12line != nil { + return t.go12line.go12PCToSPAdj(pc) + } + return 0 +} + +// LineToPC looks up the first program counter on the given line in +// the named file. It returns UnknownPathError or UnknownLineError if +// there is an error looking up this line. +func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { + obj, ok := t.Files[file] + if !ok { + return 0, nil, UnknownFileError(file) + } + + if t.go12line != nil { + pc := t.go12line.go12LineToPC(file, line) + if pc == 0 { + return 0, nil, &UnknownLineError{file, line} + } + return pc, t.PCToFunc(pc), nil + } + + abs, err := obj.alineFromLine(file, line) + if err != nil { + return + } + for i := range obj.Funcs { + f := &obj.Funcs[i] + pc := f.LineTable.LineToPC(abs, f.End) + if pc != 0 { + return pc, f, nil + } + } + return 0, nil, &UnknownLineError{file, line} +} + +// LookupSym returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupSym(name string) *Sym { + // TODO(austin) Maybe make a map + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Name == name { + return s + } + } + } + return nil +} + +// LookupFunc returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupFunc(name string) *Func { + for i := range t.Funcs { + f := &t.Funcs[i] + if f.Sym.Name == name { + return f + } + } + return nil +} + +// SymByAddr returns the text, data, or bss symbol starting at the given address. +func (t *Table) SymByAddr(addr uint64) *Sym { + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Value == addr { + return s + } + } + } + return nil +} + +/* + * Object files + */ + +// This is legacy code for Go 1.1 and earlier, which used the +// Plan 9 format for pc-line tables. This code was never quite +// correct. It's probably very close, and it's usually correct, but +// we never quite found all the corner cases. +// +// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab. + +func (o *Obj) lineFromAline(aline int) (string, int) { + type stackEnt struct { + path string + start int + offset int + prev *stackEnt + } + + noPath := &stackEnt{"", 0, 0, nil} + tos := noPath + +pathloop: + for _, s := range o.Paths { + val := int(s.Value) + switch { + case val > aline: + break pathloop + + case val == 1: + // Start a new stack + tos = &stackEnt{s.Name, val, 0, noPath} + + case s.Name == "": + // Pop + if tos == noPath { + return "", 0 + } + tos.prev.offset += val - tos.start + tos = tos.prev + + default: + // Push + tos = &stackEnt{s.Name, val, 0, tos} + } + } + + if tos == noPath { + return "", 0 + } + return tos.path, aline - tos.start - tos.offset + 1 +} + +func (o *Obj) alineFromLine(path string, line int) (int, error) { + if line < 1 { + return 0, &UnknownLineError{path, line} + } + + for i, s := range o.Paths { + // Find this path + if s.Name != path { + continue + } + + // Find this line at this stack level + depth := 0 + var incstart int + line += int(s.Value) + pathloop: + for _, s := range o.Paths[i:] { + val := int(s.Value) + switch { + case depth == 1 && val >= line: + return line - 1, nil + + case s.Name == "": + depth-- + if depth == 0 { + break pathloop + } else if depth == 1 { + line += val - incstart + } + + default: + if depth == 1 { + incstart = val + } + depth++ + } + } + return 0, &UnknownLineError{path, line} + } + return 0, UnknownFileError(path) +} + +/* + * Errors + */ + +// UnknownFileError represents a failure to find the specific file in +// the symbol table. +type UnknownFileError string + +func (e UnknownFileError) Error() string { return "unknown file: " + string(e) } + +// UnknownLineError represents a failure to map a line to a program +// counter, either because the line is beyond the bounds of the file +// or because there is no code on the given line. +type UnknownLineError struct { + File string + Line int +} + +func (e *UnknownLineError) Error() string { + return "no code at " + e.File + ":" + strconv.Itoa(e.Line) +} + +// DecodingError represents an error during the decoding of +// the symbol table. +type DecodingError struct { + off int + msg string + val interface{} +} + +func (e *DecodingError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" at byte %#x", e.off) + return msg +} diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata.go b/vendor/cloud.google.com/go/compute/metadata/metadata.go new file mode 100644 index 0000000000..0d929a6192 --- /dev/null +++ b/vendor/cloud.google.com/go/compute/metadata/metadata.go @@ -0,0 +1,501 @@ +// Copyright 2014 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 metadata provides access to Google Compute Engine (GCE) +// metadata and API service accounts. +// +// This package is a wrapper around the GCE metadata service, +// as documented at https://developers.google.com/compute/docs/metadata. +package metadata // import "cloud.google.com/go/compute/metadata" + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + "runtime" + "strings" + "sync" + "time" +) + +const ( + // metadataIP is the documented metadata server IP address. + metadataIP = "169.254.169.254" + + // metadataHostEnv is the environment variable specifying the + // GCE metadata hostname. If empty, the default value of + // metadataIP ("169.254.169.254") is used instead. + // This is variable name is not defined by any spec, as far as + // I know; it was made up for the Go package. + metadataHostEnv = "GCE_METADATA_HOST" + + userAgent = "gcloud-golang/0.1" +) + +type cachedValue struct { + k string + trim bool + mu sync.Mutex + v string +} + +var ( + projID = &cachedValue{k: "project/project-id", trim: true} + projNum = &cachedValue{k: "project/numeric-project-id", trim: true} + instID = &cachedValue{k: "instance/id", trim: true} +) + +var ( + defaultClient = &Client{hc: &http.Client{ + Transport: &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 2 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + ResponseHeaderTimeout: 2 * time.Second, + }, + }} + subscribeClient = &Client{hc: &http.Client{ + Transport: &http.Transport{ + Dial: (&net.Dialer{ + Timeout: 2 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + }, + }} +) + +// NotDefinedError is returned when requested metadata is not defined. +// +// The underlying string is the suffix after "/computeMetadata/v1/". +// +// This error is not returned if the value is defined to be the empty +// string. +type NotDefinedError string + +func (suffix NotDefinedError) Error() string { + return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix)) +} + +func (c *cachedValue) get(cl *Client) (v string, err error) { + defer c.mu.Unlock() + c.mu.Lock() + if c.v != "" { + return c.v, nil + } + if c.trim { + v, err = cl.getTrimmed(c.k) + } else { + v, err = cl.Get(c.k) + } + if err == nil { + c.v = v + } + return +} + +var ( + onGCEOnce sync.Once + onGCE bool +) + +// OnGCE reports whether this process is running on Google Compute Engine. +func OnGCE() bool { + onGCEOnce.Do(initOnGCE) + return onGCE +} + +func initOnGCE() { + onGCE = testOnGCE() +} + +func testOnGCE() bool { + // The user explicitly said they're on GCE, so trust them. + if os.Getenv(metadataHostEnv) != "" { + return true + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + resc := make(chan bool, 2) + + // Try two strategies in parallel. + // See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194 + go func() { + req, _ := http.NewRequest("GET", "http://"+metadataIP, nil) + req.Header.Set("User-Agent", userAgent) + res, err := defaultClient.hc.Do(req.WithContext(ctx)) + if err != nil { + resc <- false + return + } + defer res.Body.Close() + resc <- res.Header.Get("Metadata-Flavor") == "Google" + }() + + go func() { + addrs, err := net.LookupHost("metadata.google.internal") + if err != nil || len(addrs) == 0 { + resc <- false + return + } + resc <- strsContains(addrs, metadataIP) + }() + + tryHarder := systemInfoSuggestsGCE() + if tryHarder { + res := <-resc + if res { + // The first strategy succeeded, so let's use it. + return true + } + // Wait for either the DNS or metadata server probe to + // contradict the other one and say we are running on + // GCE. Give it a lot of time to do so, since the system + // info already suggests we're running on a GCE BIOS. + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + select { + case res = <-resc: + return res + case <-timer.C: + // Too slow. Who knows what this system is. + return false + } + } + + // There's no hint from the system info that we're running on + // GCE, so use the first probe's result as truth, whether it's + // true or false. The goal here is to optimize for speed for + // users who are NOT running on GCE. We can't assume that + // either a DNS lookup or an HTTP request to a blackholed IP + // address is fast. Worst case this should return when the + // metaClient's Transport.ResponseHeaderTimeout or + // Transport.Dial.Timeout fires (in two seconds). + return <-resc +} + +// systemInfoSuggestsGCE reports whether the local system (without +// doing network requests) suggests that we're running on GCE. If this +// returns true, testOnGCE tries a bit harder to reach its metadata +// server. +func systemInfoSuggestsGCE() bool { + if runtime.GOOS != "linux" { + // We don't have any non-Linux clues available, at least yet. + return false + } + slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name") + name := strings.TrimSpace(string(slurp)) + return name == "Google" || name == "Google Compute Engine" +} + +// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no +// ResponseHeaderTimeout). +func Subscribe(suffix string, fn func(v string, ok bool) error) error { + return subscribeClient.Subscribe(suffix, fn) +} + +// Get calls Client.Get on the default client. +func Get(suffix string) (string, error) { return defaultClient.Get(suffix) } + +// ProjectID returns the current instance's project ID string. +func ProjectID() (string, error) { return defaultClient.ProjectID() } + +// NumericProjectID returns the current instance's numeric project ID. +func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() } + +// InternalIP returns the instance's primary internal IP address. +func InternalIP() (string, error) { return defaultClient.InternalIP() } + +// ExternalIP returns the instance's primary external (public) IP address. +func ExternalIP() (string, error) { return defaultClient.ExternalIP() } + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func Hostname() (string, error) { return defaultClient.Hostname() } + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() } + +// InstanceID returns the current VM's numeric instance ID. +func InstanceID() (string, error) { return defaultClient.InstanceID() } + +// InstanceName returns the current VM's instance ID string. +func InstanceName() (string, error) { return defaultClient.InstanceName() } + +// Zone returns the current VM's zone, such as "us-central1-b". +func Zone() (string, error) { return defaultClient.Zone() } + +// InstanceAttributes calls Client.InstanceAttributes on the default client. +func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() } + +// ProjectAttributes calls Client.ProjectAttributes on the default client. +func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() } + +// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. +func InstanceAttributeValue(attr string) (string, error) { + return defaultClient.InstanceAttributeValue(attr) +} + +// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. +func ProjectAttributeValue(attr string) (string, error) { + return defaultClient.ProjectAttributeValue(attr) +} + +// Scopes calls Client.Scopes on the default client. +func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) } + +func strsContains(ss []string, s string) bool { + for _, v := range ss { + if v == s { + return true + } + } + return false +} + +// A Client provides metadata. +type Client struct { + hc *http.Client +} + +// NewClient returns a Client that can be used to fetch metadata. All HTTP requests +// will use the given http.Client instead of the default client. +func NewClient(c *http.Client) *Client { + return &Client{hc: c} +} + +// getETag returns a value from the metadata service as well as the associated ETag. +// This func is otherwise equivalent to Get. +func (c *Client) getETag(suffix string) (value, etag string, err error) { + // Using a fixed IP makes it very difficult to spoof the metadata service in + // a container, which is an important use-case for local testing of cloud + // deployments. To enable spoofing of the metadata service, the environment + // variable GCE_METADATA_HOST is first inspected to decide where metadata + // requests shall go. + host := os.Getenv(metadataHostEnv) + if host == "" { + // Using 169.254.169.254 instead of "metadata" here because Go + // binaries built with the "netgo" tag and without cgo won't + // know the search suffix for "metadata" is + // ".google.internal", and this IP address is documented as + // being stable anyway. + host = metadataIP + } + url := "http://" + host + "/computeMetadata/v1/" + suffix + req, _ := http.NewRequest("GET", url, nil) + req.Header.Set("Metadata-Flavor", "Google") + req.Header.Set("User-Agent", userAgent) + res, err := c.hc.Do(req) + if err != nil { + return "", "", err + } + defer res.Body.Close() + if res.StatusCode == http.StatusNotFound { + return "", "", NotDefinedError(suffix) + } + if res.StatusCode != 200 { + return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url) + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", "", err + } + return string(all), res.Header.Get("Etag"), nil +} + +// Get returns a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// +// If the GCE_METADATA_HOST environment variable is not defined, a default of +// 169.254.169.254 will be used instead. +// +// If the requested metadata is not defined, the returned error will +// be of type NotDefinedError. +func (c *Client) Get(suffix string) (string, error) { + val, _, err := c.getETag(suffix) + return val, err +} + +func (c *Client) getTrimmed(suffix string) (s string, err error) { + s, err = c.Get(suffix) + s = strings.TrimSpace(s) + return +} + +func (c *Client) lines(suffix string) ([]string, error) { + j, err := c.Get(suffix) + if err != nil { + return nil, err + } + s := strings.Split(strings.TrimSpace(j), "\n") + for i := range s { + s[i] = strings.TrimSpace(s[i]) + } + return s, nil +} + +// ProjectID returns the current instance's project ID string. +func (c *Client) ProjectID() (string, error) { return projID.get(c) } + +// NumericProjectID returns the current instance's numeric project ID. +func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) } + +// InstanceID returns the current VM's numeric instance ID. +func (c *Client) InstanceID() (string, error) { return instID.get(c) } + +// InternalIP returns the instance's primary internal IP address. +func (c *Client) InternalIP() (string, error) { + return c.getTrimmed("instance/network-interfaces/0/ip") +} + +// ExternalIP returns the instance's primary external (public) IP address. +func (c *Client) ExternalIP() (string, error) { + return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") +} + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func (c *Client) Hostname() (string, error) { + return c.getTrimmed("instance/hostname") +} + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func (c *Client) InstanceTags() ([]string, error) { + var s []string + j, err := c.Get("instance/tags") + if err != nil { + return nil, err + } + if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil { + return nil, err + } + return s, nil +} + +// InstanceName returns the current VM's instance ID string. +func (c *Client) InstanceName() (string, error) { + host, err := c.Hostname() + if err != nil { + return "", err + } + return strings.Split(host, ".")[0], nil +} + +// Zone returns the current VM's zone, such as "us-central1-b". +func (c *Client) Zone() (string, error) { + zone, err := c.getTrimmed("instance/zone") + // zone is of the form "projects//zones/". + if err != nil { + return "", err + } + return zone[strings.LastIndex(zone, "/")+1:], nil +} + +// InstanceAttributes returns the list of user-defined attributes, +// assigned when initially creating a GCE VM instance. The value of an +// attribute can be obtained with InstanceAttributeValue. +func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") } + +// ProjectAttributes returns the list of user-defined attributes +// applying to the project as a whole, not just this VM. The value of +// an attribute can be obtained with ProjectAttributeValue. +func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") } + +// InstanceAttributeValue returns the value of the provided VM +// instance attribute. +// +// If the requested attribute is not defined, the returned error will +// be of type NotDefinedError. +// +// InstanceAttributeValue may return ("", nil) if the attribute was +// defined to be the empty string. +func (c *Client) InstanceAttributeValue(attr string) (string, error) { + return c.Get("instance/attributes/" + attr) +} + +// ProjectAttributeValue returns the value of the provided +// project attribute. +// +// If the requested attribute is not defined, the returned error will +// be of type NotDefinedError. +// +// ProjectAttributeValue may return ("", nil) if the attribute was +// defined to be the empty string. +func (c *Client) ProjectAttributeValue(attr string) (string, error) { + return c.Get("project/attributes/" + attr) +} + +// Scopes returns the service account scopes for the given account. +// The account may be empty or the string "default" to use the instance's +// main account. +func (c *Client) Scopes(serviceAccount string) ([]string, error) { + if serviceAccount == "" { + serviceAccount = "default" + } + return c.lines("instance/service-accounts/" + serviceAccount + "/scopes") +} + +// Subscribe subscribes to a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// The suffix may contain query parameters. +// +// Subscribe calls fn with the latest metadata value indicated by the provided +// suffix. If the metadata value is deleted, fn is called with the empty string +// and ok false. Subscribe blocks until fn returns a non-nil error or the value +// is deleted. Subscribe returns the error value returned from the last call to +// fn, which may be nil when ok == false. +func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { + const failedSubscribeSleep = time.Second * 5 + + // First check to see if the metadata value exists at all. + val, lastETag, err := c.getETag(suffix) + if err != nil { + return err + } + + if err := fn(val, true); err != nil { + return err + } + + ok := true + if strings.ContainsRune(suffix, '?') { + suffix += "&wait_for_change=true&last_etag=" + } else { + suffix += "?wait_for_change=true&last_etag=" + } + for { + val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) + if err != nil { + if _, deleted := err.(NotDefinedError); !deleted { + time.Sleep(failedSubscribeSleep) + continue // Retry on other errors. + } + ok = false + } + lastETag = etag + + if err := fn(val, ok); err != nil || !ok { + return err + } + } +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/LICENSE b/vendor/github.com/docker/compose-on-kubernetes/LICENSE new file mode 100644 index 0000000000..6d630cf595 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/docker/compose-on-kubernetes/README.md b/vendor/github.com/docker/compose-on-kubernetes/README.md new file mode 100644 index 0000000000..683fae428d --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/README.md @@ -0,0 +1,187 @@ +# Compose on Kubernetes + +[![CircleCI](https://circleci.com/gh/docker/compose-on-kubernetes/tree/master.svg?style=svg)](https://circleci.com/gh/docker/compose-on-kubernetes/tree/master) + +Compose on Kubernetes allows you to deploy Docker Compose files onto a +Kubernetes cluster. + +# Table of contents + +- [Get started](#get-started) +- [Developing Compose on Kubernetes](#developing-compose-on-kubernetes) + +More documentation can be found in the [docs/](./docs) directory. This includes: +- [Architecture](./docs/architecture.md) +- [Mapping of stack to Kubernetes objects](./docs/mapping.md) +- [Compatibility matrix](./docs/compatibility.md) + +# Get started + +Compose on Kubernetes comes installed on +[Docker Desktop](https://www.docker.com/products/docker-desktop) and +[Docker Enterprise](https://www.docker.com/products/docker-enterprise). + +On Docker Desktop you will need to activate Kubernetes in the settings to use +Compose on Kubernetes. + +## Check that Compose on Kubernetes is installed + +You can check that Compose on Kubernetes is installed by checking for the +availability of the API using the command: + +```console +$ kubectl api-versions | grep compose +compose.docker.com/v1beta1 +compose.docker.com/v1beta2 +``` + +## Deploy a stack + +To deploy a stack, you can use the Docker CLI: + +```console +$ cat docker-compose.yml +version: '3.3' + +services: + + db: + build: db + image: dockersamples/k8s-wordsmith-db + + words: + build: words + image: dockersamples/k8s-wordsmith-api + deploy: + replicas: 5 + + web: + build: web + image: dockersamples/k8s-wordsmith-web + ports: + - "33000:80" + +$ docker stack deploy --orchestrator=kubernetes -c docker-compose.yml hellokube +``` + +# Developing Compose on Kubernetes + +See the [contributing](./CONTRIBUTING.md) guides for how to contribute code. + +## Pre-requisites + +- `make` +- [Docker Desktop](https://www.docker.com/products/docker-desktop) (Mac or Windows) with engine version 18.09 or later +- Enable Buildkit by setting `DOCKER_BUILDKIT=1` in your environment +- Enable Kubernetes in Docker Desktop settings + +### For live debugging + +- Debugger capable of remote debugging with Delve API version 2 + - Goland run-configs are pre-configured + +## Debug quick start + +### Debug install + +To build and install a debug version of Compose on Kubernetes onto Docker +Desktop, you can use the following command: + +```console +$ make -f debug.Makefile install-debug-images +``` + +This command: +- Builds the images with debug symbols +- Runs the debug installer: + - Installs debug versions of API server and Compose controller in the `docker` namespace + - Creates two debugging _LoadBalancer_ services (unused in this mode) + +You can verify that Compose on Kubernetes is running with `kubectl` as follows: + +```console +$ kubectl get all -n docker +NAME READY STATUS RESTARTS AGE +pod/compose-7c4dfcff76-jgwst 1/1 Running 0 59s +pod/compose-api-759f8dbb4b-2z5n2 2/2 Running 0 59s + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/compose-api ClusterIP 10.98.42.151 443/TCP 59s +service/compose-api-server-remote-debug LoadBalancer 10.101.198.179 localhost 40001:31693/TCP 59s +service/compose-controller-remote-debug LoadBalancer 10.101.158.160 localhost 40000:31167/TCP 59s + +NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE +deployment.apps/compose 1 1 1 1 59s +deployment.apps/compose-api 1 1 1 1 59s + +NAME DESIRED CURRENT READY AGE +replicaset.apps/compose-7c4dfcff76 1 1 1 59s +replicaset.apps/compose-api-759f8dbb4b 1 1 1 59s +``` + +If you describe one of the deployments, you should see `*-debug:latest` in the +image name. + +### Live debugging install + +To build and install a live debugging version of Compose on Kubernetes onto +Docker Desktop, you can use the following command: + +```console +$ make -f debug.Makefile install-live-debug-images +``` + +This command: +- Builds the images with debug symbols +- Sets the image entrypoint to run a [Delve server](https://github.com/derekparker/delve) +- Runs the debug installer + - Installs debug version of API server and Compose controller in the `docker` namespace + - Creates two debugging _LoadBalancer_ services + - `localhost:40000`: Compose controller + - `localhost:40001`: API server +- The API server and Compose controller only start once a debugger is attached + +To attach a debugger you have multiple options: +- Use [GoLand](https://www.jetbrains.com/go/): configuration can be found in `.idea` of the repository + - Select the `Debug all` config, setup breakpoints and start the debugger +- Set your Delve compatible debugger to point to use `locahost:40000` and `localhost:40001` + - Using a terminal: `dlv connect localhost:40000` then type `continue` and hit enter + +To verify that the components are installed, you can use the following command: + +```console +$ kubectl get all -n docker +``` + +To verify that the API server has started, ensure that it has started logging: +```console +$ kubectl logs -f -n docker deployment.apps/compose-api compose +API server listening at: [::]:40000 +ERROR: logging before flag.Parse: I1207 15:25:13.760739 11 plugins.go:158] Loaded 2 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,MutatingAdmissionWebhook. +ERROR: logging before flag.Parse: I1207 15:25:13.763211 11 plugins.go:161] Loaded 1 validating admission controller(s) successfully in the following order: ValidatingAdmissionWebhook. +ERROR: logging before flag.Parse: W1207 15:25:13.767429 11 client_config.go:552] Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work. +ERROR: logging before flag.Parse: W1207 15:25:13.851500 11 genericapiserver.go:319] Skipping API compose.docker.com/storage because it has no resources. +ERROR: logging before flag.Parse: I1207 15:25:13.998154 11 serve.go:116] Serving securely on [::]:9443 +``` + +To verify that the Compose controller has started, ensure that it is logging: +```console +kubectl logs -f -n docker deployment.apps/compose +API server listening at: [::]:40000 +Version: v0.4.16-dirty +Git commit: b2e3a6b-dirty +OS/Arch: linux/amd64 +Built: Fri Dec 7 15:18:13 2018 +time="2018-12-07T15:25:19Z" level=info msg="Controller ready" +``` + +## Reinstall default + +To reinstall the default Compose on Kubernetes on Docker Desktop, simply restart +your Kubernetes cluster. You can do this by deactivating and then reactivating +Kubernetes or by restarting Docker Desktop. +See the [contributing](./CONTRIBUTING.md) and [debugging](./DEBUGGING.md) guides. + +# Deploying Compose on Kubernetes + +- Guide for [Azure AKS](./docs/install-on-aks.md). diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/check.go b/vendor/github.com/docker/compose-on-kubernetes/api/check.go new file mode 100644 index 0000000000..ad36159426 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/check.go @@ -0,0 +1,55 @@ +package apis + +import ( + apiv1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + apiv1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + "github.com/pkg/errors" + apimachinerymetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" +) + +// StackVersion represents the detected Compose Component on Kubernetes side. +type StackVersion string + +const ( + // StackAPIV1Beta1 is returned if it's the most recent version available. + StackAPIV1Beta1 = StackVersion("v1beta1") + // StackAPIV1Beta2 is returned if it's the most recent version available. + StackAPIV1Beta2 = StackVersion("v1beta2") +) + +// GetStackAPIVersion returns the most recent stack API installed. +func GetStackAPIVersion(clientSet *kubernetes.Clientset) (StackVersion, error) { + groups, err := clientSet.Discovery().ServerGroups() + if err != nil { + return "", err + } + + return getAPIVersion(groups) +} + +func getAPIVersion(groups *metav1.APIGroupList) (StackVersion, error) { + switch { + case findVersion(apiv1beta2.SchemeGroupVersion, groups.Groups): + return StackAPIV1Beta2, nil + case findVersion(apiv1beta1.SchemeGroupVersion, groups.Groups): + return StackAPIV1Beta1, nil + default: + return "", errors.Errorf("failed to find a Stack API version") + } +} + +func findVersion(stackAPI schema.GroupVersion, groups []apimachinerymetav1.APIGroup) bool { + for _, group := range groups { + if group.Name == stackAPI.Group { + for _, version := range group.Versions { + if version.Version == stackAPI.Version { + return true + } + } + } + } + return false +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go new file mode 100644 index 0000000000..65e8d4fab3 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go @@ -0,0 +1,107 @@ +package clientset + +import ( + composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" + composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" + glog "github.com/golang/glog" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +// Interface defines the methods a compose kube client should have +// FIXME(vdemeester) is it required ? +type Interface interface { + Discovery() discovery.DiscoveryInterface + ComposeV1beta2() composev1beta2.ComposeV1beta2Interface + ComposeV1beta1() composev1beta1.ComposeV1beta1Interface + // Deprecated: please explicitly pick a version if possible. + Compose() composev1beta1.ComposeV1beta1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + *composev1beta2.ComposeV1beta2Client + *composev1beta1.ComposeV1beta1Client +} + +// ComposeV1beta2 retrieves the ComposeV1beta2Client +func (c *Clientset) ComposeV1beta2() composev1beta2.ComposeV1beta2Interface { + if c == nil { + return nil + } + return c.ComposeV1beta2Client +} + +// ComposeV1beta1 retrieves the ComposeV1beta1Client +func (c *Clientset) ComposeV1beta1() composev1beta1.ComposeV1beta1Interface { + if c == nil { + return nil + } + return c.ComposeV1beta1Client +} + +// Compose retrieves the default version of ComposeClient. +// deprecated: please explicitly pick a version. +func (c *Clientset) Compose() composev1beta1.ComposeV1beta1Interface { + if c == nil { + return nil + } + return c.ComposeV1beta1Client +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.ComposeV1beta2Client, err = composev1beta2.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + cs.ComposeV1beta1Client, err = composev1beta1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + glog.Errorf("failed to create the DiscoveryClient: %v", err) + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.ComposeV1beta2Client = composev1beta2.NewForConfigOrDie(c) + cs.ComposeV1beta1Client = composev1beta1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.ComposeV1beta2Client = composev1beta2.New(c) + cs.ComposeV1beta1Client = composev1beta1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go new file mode 100644 index 0000000000..2b045a6fd1 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go @@ -0,0 +1,45 @@ +package scheme + +import ( + composev1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + composev1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + + // For GKE authentication + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" +) + +// Variables required for registration +var ( + Scheme = runtime.NewScheme() + Codecs = serializer.NewCodecFactory(Scheme) + ParameterCodec = runtime.NewParameterCodec(Scheme) +) + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + AddToScheme(Scheme) +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kuberentes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +func AddToScheme(scheme *runtime.Scheme) { + composev1beta2.AddToScheme(scheme) + composev1beta1.AddToScheme(scheme) + +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/compose_client.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/compose_client.go new file mode 100644 index 0000000000..7a12413c94 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/compose_client.go @@ -0,0 +1,74 @@ +package v1beta1 + +import ( + "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" + v1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +// ComposeV1beta1Interface defines the methods a compose v1beta1 client has +type ComposeV1beta1Interface interface { + RESTClient() rest.Interface + StacksGetter +} + +// ComposeV1beta1Client is used to interact with features provided by the compose.docker.com group. +type ComposeV1beta1Client struct { + restClient rest.Interface +} + +// Stacks returns a stack client +func (c *ComposeV1beta1Client) Stacks(namespace string) StackInterface { + return newStacks(c, namespace) +} + +// NewForConfig creates a new ComposeV1beta1Client for the given config. +func NewForConfig(c *rest.Config) (*ComposeV1beta1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &ComposeV1beta1Client{client}, nil +} + +// NewForConfigOrDie creates a new ComposeV1beta1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ComposeV1beta1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ComposeV1beta1Client for the given RESTClient. +func New(c rest.Interface) *ComposeV1beta1Client { + return &ComposeV1beta1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1beta1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *ComposeV1beta1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/stack.go new file mode 100644 index 0000000000..cf2549a977 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/stack.go @@ -0,0 +1,175 @@ +package v1beta1 + +import ( + scheme "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" + v1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// StacksGetter has a method to return a StackInterface. +// A group's client should implement this interface. +type StacksGetter interface { + Stacks(namespace string) StackInterface +} + +// StackInterface has methods to work with Stack resources. +type StackInterface interface { + Create(*v1beta1.Stack) (*v1beta1.Stack, error) + Update(*v1beta1.Stack) (*v1beta1.Stack, error) + UpdateStatus(*v1beta1.Stack) (*v1beta1.Stack, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1beta1.Stack, error) + List(opts v1.ListOptions) (*v1beta1.StackList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta1.Stack, error) + WithSkipValidation() StackInterface +} + +var _ StackInterface = &stacks{} + +// stacks implements StackInterface +type stacks struct { + client rest.Interface + ns string + skipValidation bool +} + +// newStacks returns a Stacks +func newStacks(c *ComposeV1beta1Client, namespace string) *stacks { + return &stacks{ + client: c.RESTClient(), + ns: namespace, + } +} + +func (c *stacks) handleSkipValidation(req *rest.Request) *rest.Request { + if !c.skipValidation { + return req + } + return req.Param("skip-validation", "1") +} + +// Create takes the representation of a stack and creates it. Returns the server's representation of the stack, and an error, if there is any. +func (c *stacks) Create(stack *v1beta1.Stack) (*v1beta1.Stack, error) { + result := &v1beta1.Stack{} + err := c.handleSkipValidation(c.client.Post(). + Namespace(c.ns). + Resource("stacks"). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// Update takes the representation of a stack and updates it. Returns the server's representation of the stack, and an error, if there is any. +func (c *stacks) Update(stack *v1beta1.Stack) (*v1beta1.Stack, error) { + result := &v1beta1.Stack{} + err := c.handleSkipValidation(c.client.Put(). + Namespace(c.ns). + Resource("stacks"). + Name(stack.Name). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclientstatus=false comment above the type to avoid generating UpdateStatus(). + +func (c *stacks) UpdateStatus(stack *v1beta1.Stack) (*v1beta1.Stack, error) { + result := &v1beta1.Stack{} + err := c.handleSkipValidation(c.client.Put(). + Namespace(c.ns). + Resource("stacks"). + Name(stack.Name). + SubResource("status"). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// Delete takes name of the stack and deletes it. Returns an error if one occurs. +func (c *stacks) Delete(name string, options *v1.DeleteOptions) error { + return c.handleSkipValidation(c.client.Delete(). + Namespace(c.ns). + Resource("stacks"). + Name(name). + Body(options)). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *stacks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.handleSkipValidation(c.client.Delete(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options)). + Do(). + Error() +} + +// Get takes name of the stack, and returns the corresponding stack object, and an error if there is any. +func (c *stacks) Get(name string, options v1.GetOptions) (*v1beta1.Stack, error) { + result := &v1beta1.Stack{} + err := c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return result, err +} + +// List takes label and field selectors, and returns the list of Stacks that match those selectors. +func (c *stacks) List(opts v1.ListOptions) (*v1beta1.StackList, error) { + result := &v1beta1.StackList{} + err := c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return result, err +} + +// Watch returns a watch.Interface that watches the requested stacks. +func (c *stacks) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Patch applies the patch and returns the patched stack. +func (c *stacks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta1.Stack, error) { + result := &v1beta1.Stack{} + err := c.handleSkipValidation(c.client.Patch(pt). + Namespace(c.ns). + Resource("stacks"). + SubResource(subresources...). + Name(name). + Body(data)). + Do(). + Into(result) + return result, err +} + +// WithSkipValidation creates a new Stack Client interface with validation disabled +func (c *stacks) WithSkipValidation() StackInterface { + return &stacks{ + skipValidation: true, + client: c.client, + ns: c.ns, + } +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/compose_client.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/compose_client.go new file mode 100644 index 0000000000..a66899d6f7 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/compose_client.go @@ -0,0 +1,74 @@ +package v1beta2 + +import ( + "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" + v1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +// ComposeV1beta2Interface defines the methods a compose v1beta2 client has +type ComposeV1beta2Interface interface { + RESTClient() rest.Interface + StacksGetter +} + +// ComposeV1beta2Client is used to interact with features provided by the compose.docker.com group. +type ComposeV1beta2Client struct { + restClient rest.Interface +} + +// Stacks returns a stack client +func (c *ComposeV1beta2Client) Stacks(namespace string) StackInterface { + return newStacks(c, namespace) +} + +// NewForConfig creates a new ComposeV1beta2Client for the given config. +func NewForConfig(c *rest.Config) (*ComposeV1beta2Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &ComposeV1beta2Client{client}, nil +} + +// NewForConfigOrDie creates a new ComposeV1beta2Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ComposeV1beta2Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ComposeV1beta2Client for the given RESTClient. +func New(c rest.Interface) *ComposeV1beta2Client { + return &ComposeV1beta2Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1beta2.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *ComposeV1beta2Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/stack.go new file mode 100644 index 0000000000..0098333d7f --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/stack.go @@ -0,0 +1,173 @@ +package v1beta2 + +import ( + scheme "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" + v1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// StacksGetter has a method to return a StackInterface. +// A group's client should implement this interface. +type StacksGetter interface { + Stacks(namespace string) StackInterface +} + +// StackInterface has methods to work with Stack resources. +type StackInterface interface { + Create(*v1beta2.Stack) (*v1beta2.Stack, error) + Update(*v1beta2.Stack) (*v1beta2.Stack, error) + UpdateStatus(*v1beta2.Stack) (*v1beta2.Stack, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1beta2.Stack, error) + List(opts v1.ListOptions) (*v1beta2.StackList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta2.Stack, error) + WithSkipValidation() StackInterface +} + +// stacks implements StackInterface +type stacks struct { + skipValidation bool + client rest.Interface + ns string +} + +// newStacks returns a Stacks +func newStacks(c *ComposeV1beta2Client, namespace string) *stacks { + return &stacks{ + client: c.RESTClient(), + ns: namespace, + } +} + +func (c *stacks) handleSkipValidation(req *rest.Request) *rest.Request { + if !c.skipValidation { + return req + } + return req.Param("skip-validation", "1") +} + +// Create takes the representation of a stack and creates it. Returns the server's representation of the stack, and an error, if there is any. +func (c *stacks) Create(stack *v1beta2.Stack) (*v1beta2.Stack, error) { + result := &v1beta2.Stack{} + err := c.handleSkipValidation(c.client.Post(). + Namespace(c.ns). + Resource("stacks"). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// Update takes the representation of a stack and updates it. Returns the server's representation of the stack, and an error, if there is any. +func (c *stacks) Update(stack *v1beta2.Stack) (*v1beta2.Stack, error) { + result := &v1beta2.Stack{} + err := c.handleSkipValidation(c.client.Put(). + Namespace(c.ns). + Resource("stacks"). + Name(stack.Name). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclientstatus=false comment above the type to avoid generating UpdateStatus(). + +func (c *stacks) UpdateStatus(stack *v1beta2.Stack) (*v1beta2.Stack, error) { + result := &v1beta2.Stack{} + err := c.handleSkipValidation(c.client.Put(). + Namespace(c.ns). + Resource("stacks"). + Name(stack.Name). + SubResource("status"). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// Delete takes name of the stack and deletes it. Returns an error if one occurs. +func (c *stacks) Delete(name string, options *v1.DeleteOptions) error { + return c.handleSkipValidation(c.client.Delete(). + Namespace(c.ns). + Resource("stacks"). + Name(name). + Body(options)). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *stacks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.handleSkipValidation(c.client.Delete(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options)). + Do(). + Error() +} + +// Get takes name of the stack, and returns the corresponding stack object, and an error if there is any. +func (c *stacks) Get(name string, options v1.GetOptions) (*v1beta2.Stack, error) { + result := &v1beta2.Stack{} + err := c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return result, err +} + +// List takes label and field selectors, and returns the list of Stacks that match those selectors. +func (c *stacks) List(opts v1.ListOptions) (*v1beta2.StackList, error) { + result := &v1beta2.StackList{} + err := c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return result, err +} + +// Watch returns a watch.Interface that watches the requested stacks. +func (c *stacks) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Patch applies the patch and returns the patched stack. +func (c *stacks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1beta2.Stack, error) { + result := &v1beta2.Stack{} + err := c.handleSkipValidation(c.client.Patch(pt). + Namespace(c.ns). + Resource("stacks"). + SubResource(subresources...). + Name(name). + Body(data)). + Do(). + Into(result) + return result, err +} + +// WithSkipValidation creates a new Stack Client interface with validation disabled +func (c *stacks) WithSkipValidation() StackInterface { + return &stacks{ + skipValidation: true, + client: c.client, + ns: c.ns, + } +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go new file mode 100644 index 0000000000..d44d1c0db5 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go @@ -0,0 +1,25 @@ +package compose + +import ( + "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2" + "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + V1beta2() v1beta2.Interface +} + +type group struct { + internalinterfaces.SharedInformerFactory +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory) Interface { + return &group{f} +} + +// V1beta2 returns a new v1beta2.Interface. +func (g *group) V1beta2() v1beta2.Interface { + return v1beta2.New(g.SharedInformerFactory) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/interface.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/interface.go new file mode 100644 index 0000000000..f340c34169 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/interface.go @@ -0,0 +1,25 @@ +package v1beta2 + +import ( + "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Stacks returns a StackInformer. + Stacks() StackInformer +} + +type version struct { + internalinterfaces.SharedInformerFactory +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory) Interface { + return &version{f} +} + +// Stacks returns a StackInformer. +func (v *version) Stacks() StackInformer { + return &stackInformer{factory: v.SharedInformerFactory} +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/stack.go new file mode 100644 index 0000000000..f603a73f4f --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/stack.go @@ -0,0 +1,51 @@ +package v1beta2 + +import ( + "time" + + "github.com/docker/compose-on-kubernetes/api/client/clientset" + "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" + "github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2" + compose_v1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +// StackInformer provides access to a shared informer and lister for +// Stacks. +type StackInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1beta2.StackLister +} + +type stackInformer struct { + factory internalinterfaces.SharedInformerFactory +} + +func newStackInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + sharedIndexInformer := cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + return client.ComposeV1beta2().Stacks(v1.NamespaceAll).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + return client.ComposeV1beta2().Stacks(v1.NamespaceAll).Watch(options) + }, + }, + &compose_v1beta2.Stack{}, + resyncPeriod, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, + ) + + return sharedIndexInformer +} + +func (f *stackInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&compose_v1beta2.Stack{}, newStackInformer) +} + +func (f *stackInformer) Lister() v1beta2.StackLister { + return v1beta2.NewStackLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/factory.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/factory.go new file mode 100644 index 0000000000..c350b11334 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/factory.go @@ -0,0 +1,101 @@ +package informers + +import ( + "reflect" + "sync" + "time" + + "github.com/docker/compose-on-kubernetes/api/client/clientset" + "github.com/docker/compose-on-kubernetes/api/client/informers/compose" + "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/cache" +) + +type sharedInformerFactory struct { + client clientset.Interface + lock sync.Mutex + defaultResync time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory +func NewSharedInformerFactory(client clientset.Interface, defaultResync time.Duration) SharedInformerFactory { + return &sharedInformerFactory{ + client: client, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + } +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + informer = newFunc(f.client, f.defaultResync) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Compose() compose.Interface +} + +func (f *sharedInformerFactory) Compose() compose.Interface { + return compose.New(f) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go new file mode 100644 index 0000000000..0605565786 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go @@ -0,0 +1,44 @@ +package informers + +import ( + "fmt" + + "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=Compose, Version=V1beta1 + case v1beta2.SchemeGroupVersion.WithResource("stacks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Compose().V1beta2().Stacks().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces/factory_interfaces.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000000..38c4cb8389 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces/factory_interfaces.go @@ -0,0 +1,18 @@ +package internalinterfaces + +import ( + "time" + + "github.com/docker/compose-on-kubernetes/api/client/clientset" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc defines a Informer constructor (from a clientset and a duration) +type NewInformerFunc func(clientset.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/expansion_generated.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/expansion_generated.go new file mode 100644 index 0000000000..1d0f5529c1 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/expansion_generated.go @@ -0,0 +1,9 @@ +package v1beta2 + +// StackListerExpansion allows custom methods to be added to +// StackLister. +type StackListerExpansion interface{} + +// StackNamespaceListerExpansion allows custom methods to be added to +// StackNamespaceLister. +type StackNamespaceListerExpansion interface{} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/stack.go new file mode 100644 index 0000000000..37f79f9b61 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/stack.go @@ -0,0 +1,78 @@ +package v1beta2 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// StackLister helps list Stacks. +type StackLister interface { + // List lists all Stacks in the indexer. + List(selector labels.Selector) ([]*v1beta2.Stack, error) + // Stacks returns an object that can list and get Stacks. + Stacks(namespace string) StackNamespaceLister + StackListerExpansion +} + +// stackLister implements the StackLister interface. +type stackLister struct { + indexer cache.Indexer +} + +// NewStackLister returns a new StackLister. +func NewStackLister(indexer cache.Indexer) StackLister { + return &stackLister{indexer: indexer} +} + +// List lists all Stacks in the indexer. +func (s *stackLister) List(selector labels.Selector) ([]*v1beta2.Stack, error) { + stacks := []*v1beta2.Stack{} + err := cache.ListAll(s.indexer, selector, func(m interface{}) { + stacks = append(stacks, m.(*v1beta2.Stack)) + }) + return stacks, err +} + +// Stacks returns an object that can list and get Stacks. +func (s *stackLister) Stacks(namespace string) StackNamespaceLister { + return stackNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// StackNamespaceLister helps list and get Stacks. +type StackNamespaceLister interface { + // List lists all Stacks in the indexer for a given namespace. + List(selector labels.Selector) ([]*v1beta2.Stack, error) + // Get retrieves the Stack from the indexer for a given namespace and name. + Get(name string) (*v1beta2.Stack, error) + StackNamespaceListerExpansion +} + +// stackNamespaceLister implements the StackNamespaceLister +// interface. +type stackNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Stacks in the indexer for a given namespace. +func (s stackNamespaceLister) List(selector labels.Selector) ([]*v1beta2.Stack, error) { + stacks := []*v1beta2.Stack{} + err := cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + stacks = append(stacks, m.(*v1beta2.Stack)) + }) + return stacks, err +} + +// Get retrieves the Stack from the indexer for a given namespace and name. +func (s stackNamespaceLister) Get(name string) (*v1beta2.Stack, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1beta2.GroupResource("stack"), name) + } + return obj.(*v1beta2.Stack), nil +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/maps.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/maps.go new file mode 100644 index 0000000000..fc892ddc0a --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/maps.go @@ -0,0 +1,25 @@ +package clone + +// MapOfStringToSliceOfString deep copy a map[string][]string +func MapOfStringToSliceOfString(source map[string][]string) map[string][]string { + if source == nil { + return nil + } + res := make(map[string][]string, len(source)) + for k, v := range source { + res[k] = SliceOfString(v) + } + return res +} + +// MapOfStringToInt deep copy a map[string]int +func MapOfStringToInt(source map[string]int) map[string]int { + if source == nil { + return nil + } + res := make(map[string]int, len(source)) + for k, v := range source { + res[k] = v + } + return res +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/slices.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/slices.go new file mode 100644 index 0000000000..cdfa8ba16b --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/slices.go @@ -0,0 +1,11 @@ +package clone + +// SliceOfString deep copy a slice of strings +func SliceOfString(source []string) []string { + if source == nil { + return nil + } + res := make([]string, len(source)) + copy(res, source) + return res +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/doc.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/doc.go new file mode 100644 index 0000000000..471ed757d5 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/doc.go @@ -0,0 +1,3 @@ +// Package impersonation holds data structures for enabling user impersonation within Conpose for Kubernetes +// +k8s:openapi-gen=true +package impersonation diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/impersonationconfig.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/impersonationconfig.go new file mode 100644 index 0000000000..b432962996 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/impersonationconfig.go @@ -0,0 +1,26 @@ +package impersonation + +import "github.com/docker/compose-on-kubernetes/api/compose/clone" + +// Config contains the data required to impersonate a user. +type Config struct { + // UserName is the username to impersonate on each request. + UserName string + // Groups are the groups to impersonate on each request. + Groups []string + // Extra is a free-form field which can be used to link some authentication information + // to authorization information. This field allows you to impersonate it. + Extra map[string][]string +} + +// Clone clones the impersonation config +func (ic *Config) Clone() *Config { + if ic == nil { + return nil + } + result := new(Config) + result.UserName = ic.UserName + result.Groups = clone.SliceOfString(ic.Groups) + result.Extra = clone.MapOfStringToSliceOfString(ic.Extra) + return result +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/doc.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/doc.go new file mode 100644 index 0000000000..37c73766c9 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/doc.go @@ -0,0 +1,10 @@ +// Api versions allow the api contract for a resource to be changed while keeping +// backward compatibility by support multiple concurrent versions +// of the same resource + +// Package v1beta1 is the first version of the Stack spec, containing only a compose file +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:defaulter-gen=TypeMeta +// +groupName=compose.docker.com +package v1beta1 diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/owner.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/owner.go new file mode 100644 index 0000000000..d3137c2891 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/owner.go @@ -0,0 +1,31 @@ +package v1beta1 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/impersonation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Owner defines the owner of a stack. It is used to impersonate the controller calls +// to kubernetes api. +type Owner struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Owner impersonation.Config `json:"owner,omitempty"` +} + +func (o *Owner) clone() *Owner { + if o == nil { + return nil + } + result := new(Owner) + result.TypeMeta = o.TypeMeta + result.ObjectMeta = o.ObjectMeta + result.Owner = *result.Owner.Clone() + return result +} + +// DeepCopyObject clones the owner +func (o *Owner) DeepCopyObject() runtime.Object { + return o.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/parsing.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/parsing.go new file mode 100644 index 0000000000..634a8428a7 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/parsing.go @@ -0,0 +1,4 @@ +package v1beta1 + +// MaxComposeVersion is the most recent version of compose file Schema supported in v1beta1 +const MaxComposeVersion = "3.5" diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/register.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/register.go new file mode 100644 index 0000000000..3b418230e4 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/register.go @@ -0,0 +1,39 @@ +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name used to register these objects +const GroupName = "compose.docker.com" + +// Alias variables for the registration +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + localSchemeBuilder.Register(addKnownTypes) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Stack{}, + &StackList{}, + &Owner{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/stack.go new file mode 100644 index 0000000000..83ad9f0810 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/stack.go @@ -0,0 +1,87 @@ +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// StackList defines a list of stacks +type StackList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Items []Stack `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// DeepCopyObject clones the stack list +func (s *StackList) DeepCopyObject() runtime.Object { + if s == nil { + return nil + } + result := new(StackList) + result.TypeMeta = s.TypeMeta + result.ListMeta = s.ListMeta + if s.Items == nil { + return result + } + result.Items = make([]Stack, len(s.Items)) + for ix, s := range s.Items { + result.Items[ix] = *s.clone() + } + return result +} + +// Stack defines a stack object to be register in the kubernetes API +type Stack struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec StackSpec `json:"spec,omitempty"` + Status StackStatus `json:"status,omitempty"` +} + +// StackSpec defines the desired state of Stack +type StackSpec struct { + ComposeFile string `json:"composeFile,omitempty"` +} + +// StackPhase defines the status phase in which the stack is. +type StackPhase string + +// These are valid conditions of a stack. +const ( + // StackAvailable means the stack is available. + StackAvailable StackPhase = "Available" + // StackProgressing means the deployment is progressing. + StackProgressing StackPhase = "Progressing" + // StackFailure is added in a stack when one of its members fails to be created + // or deleted. + StackFailure StackPhase = "Failure" +) + +// StackStatus defines the observed state of Stack +type StackStatus struct { + // Current condition of the stack. + Phase StackPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=StackPhase"` + // A human readable message indicating details about the stack. + Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` +} + +func (s *Stack) clone() *Stack { + if s == nil { + return nil + } + // in v1beta1, Stack has no pointer, slice or map. Plain old struct copy is ok + result := *s + return &result +} + +// Clone implements the Cloner interface for kubernetes +func (s *Stack) Clone() *Stack { + return s.clone() +} + +// DeepCopyObject clones the stack +func (s *Stack) DeepCopyObject() runtime.Object { + return s.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/composefile_stack_types.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/composefile_stack_types.go new file mode 100644 index 0000000000..1a1c2cb2a1 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/composefile_stack_types.go @@ -0,0 +1,26 @@ +package v1beta2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// ComposeFile is the content of a stack's compose file if any +type ComposeFile struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + ComposeFile string `json:"composeFile,omitempty"` +} + +func (c *ComposeFile) clone() *ComposeFile { + if c == nil { + return nil + } + res := *c + return &res +} + +// DeepCopyObject clones the ComposeFile +func (c *ComposeFile) DeepCopyObject() runtime.Object { + return c.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/deepcopy_generated.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/deepcopy_generated.go new file mode 100644 index 0000000000..569db57277 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/deepcopy_generated.go @@ -0,0 +1,660 @@ +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by C:\gohome\bin\deepcopy-gen.exe. DO NOT EDIT. + +package v1beta2 + +import ( + time "time" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigObjConfig) DeepCopyInto(out *ConfigObjConfig) { + *out = *in + out.External = in.External + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigObjConfig. +func (in *ConfigObjConfig) DeepCopy() *ConfigObjConfig { + if in == nil { + return nil + } + out := new(ConfigObjConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Constraint) DeepCopyInto(out *Constraint) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Constraint. +func (in *Constraint) DeepCopy() *Constraint { + if in == nil { + return nil + } + out := new(Constraint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Constraints) DeepCopyInto(out *Constraints) { + *out = *in + if in.OperatingSystem != nil { + in, out := &in.OperatingSystem, &out.OperatingSystem + if *in == nil { + *out = nil + } else { + *out = new(Constraint) + **out = **in + } + } + if in.Architecture != nil { + in, out := &in.Architecture, &out.Architecture + if *in == nil { + *out = nil + } else { + *out = new(Constraint) + **out = **in + } + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + if *in == nil { + *out = nil + } else { + *out = new(Constraint) + **out = **in + } + } + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]Constraint, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Constraints. +func (in *Constraints) DeepCopy() *Constraints { + if in == nil { + return nil + } + out := new(Constraints) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeployConfig) DeepCopyInto(out *DeployConfig) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + if *in == nil { + *out = nil + } else { + *out = new(uint64) + **out = **in + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.UpdateConfig != nil { + in, out := &in.UpdateConfig, &out.UpdateConfig + if *in == nil { + *out = nil + } else { + *out = new(UpdateConfig) + (*in).DeepCopyInto(*out) + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.RestartPolicy != nil { + in, out := &in.RestartPolicy, &out.RestartPolicy + if *in == nil { + *out = nil + } else { + *out = new(RestartPolicy) + **out = **in + } + } + in.Placement.DeepCopyInto(&out.Placement) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeployConfig. +func (in *DeployConfig) DeepCopy() *DeployConfig { + if in == nil { + return nil + } + out := new(DeployConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *External) DeepCopyInto(out *External) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new External. +func (in *External) DeepCopy() *External { + if in == nil { + return nil + } + out := new(External) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileObjectConfig) DeepCopyInto(out *FileObjectConfig) { + *out = *in + out.External = in.External + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileObjectConfig. +func (in *FileObjectConfig) DeepCopy() *FileObjectConfig { + if in == nil { + return nil + } + out := new(FileObjectConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileReferenceConfig) DeepCopyInto(out *FileReferenceConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(uint32) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileReferenceConfig. +func (in *FileReferenceConfig) DeepCopy() *FileReferenceConfig { + if in == nil { + return nil + } + out := new(FileReferenceConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HealthCheckConfig) DeepCopyInto(out *HealthCheckConfig) { + *out = *in + if in.Test != nil { + in, out := &in.Test, &out.Test + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } + if in.Retries != nil { + in, out := &in.Retries, &out.Retries + if *in == nil { + *out = nil + } else { + *out = new(uint64) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthCheckConfig. +func (in *HealthCheckConfig) DeepCopy() *HealthCheckConfig { + if in == nil { + return nil + } + out := new(HealthCheckConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Placement) DeepCopyInto(out *Placement) { + *out = *in + if in.Constraints != nil { + in, out := &in.Constraints, &out.Constraints + if *in == nil { + *out = nil + } else { + *out = new(Constraints) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Placement. +func (in *Placement) DeepCopy() *Placement { + if in == nil { + return nil + } + out := new(Placement) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resource) DeepCopyInto(out *Resource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resource. +func (in *Resource) DeepCopy() *Resource { + if in == nil { + return nil + } + out := new(Resource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + if *in == nil { + *out = nil + } else { + *out = new(Resource) + **out = **in + } + } + if in.Reservations != nil { + in, out := &in.Reservations, &out.Reservations + if *in == nil { + *out = nil + } else { + *out = new(Resource) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RestartPolicy) DeepCopyInto(out *RestartPolicy) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestartPolicy. +func (in *RestartPolicy) DeepCopy() *RestartPolicy { + if in == nil { + return nil + } + out := new(RestartPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretConfig) DeepCopyInto(out *SecretConfig) { + *out = *in + out.External = in.External + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretConfig. +func (in *SecretConfig) DeepCopy() *SecretConfig { + if in == nil { + return nil + } + out := new(SecretConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceConfig) DeepCopyInto(out *ServiceConfig) { + *out = *in + if in.CapAdd != nil { + in, out := &in.CapAdd, &out.CapAdd + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.CapDrop != nil { + in, out := &in.CapDrop, &out.CapDrop + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Command != nil { + in, out := &in.Command, &out.Command + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make([]ServiceConfigObjConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Deploy.DeepCopyInto(&out.Deploy) + if in.Entrypoint != nil { + in, out := &in.Entrypoint, &out.Entrypoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = make(map[string]*string, len(*in)) + for key, val := range *in { + if val == nil { + (*out)[key] = nil + } else { + outVal := *val + (*out)[key] = &outVal + } + } + } + if in.ExtraHosts != nil { + in, out := &in.ExtraHosts, &out.ExtraHosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + if *in == nil { + *out = nil + } else { + *out = new(HealthCheckConfig) + (*in).DeepCopyInto(*out) + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]ServicePortConfig, len(*in)) + copy(*out, *in) + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make([]ServiceSecretConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.StopGracePeriod != nil { + in, out := &in.StopGracePeriod, &out.StopGracePeriod + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } + if in.Tmpfs != nil { + in, out := &in.Tmpfs, &out.Tmpfs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.User != nil { + in, out := &in.User, &out.User + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]ServiceVolumeConfig, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceConfig. +func (in *ServiceConfig) DeepCopy() *ServiceConfig { + if in == nil { + return nil + } + out := new(ServiceConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceConfigObjConfig) DeepCopyInto(out *ServiceConfigObjConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(uint32) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceConfigObjConfig. +func (in *ServiceConfigObjConfig) DeepCopy() *ServiceConfigObjConfig { + if in == nil { + return nil + } + out := new(ServiceConfigObjConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServicePortConfig) DeepCopyInto(out *ServicePortConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePortConfig. +func (in *ServicePortConfig) DeepCopy() *ServicePortConfig { + if in == nil { + return nil + } + out := new(ServicePortConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceSecretConfig) DeepCopyInto(out *ServiceSecretConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(uint32) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSecretConfig. +func (in *ServiceSecretConfig) DeepCopy() *ServiceSecretConfig { + if in == nil { + return nil + } + out := new(ServiceSecretConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceVolumeConfig) DeepCopyInto(out *ServiceVolumeConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceVolumeConfig. +func (in *ServiceVolumeConfig) DeepCopy() *ServiceVolumeConfig { + if in == nil { + return nil + } + out := new(ServiceVolumeConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StackSpec) DeepCopyInto(out *StackSpec) { + *out = *in + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]ServiceConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make(map[string]SecretConfig, len(*in)) + for key, val := range *in { + newVal := new(SecretConfig) + val.DeepCopyInto(newVal) + (*out)[key] = *newVal + } + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make(map[string]ConfigObjConfig, len(*in)) + for key, val := range *in { + newVal := new(ConfigObjConfig) + val.DeepCopyInto(newVal) + (*out)[key] = *newVal + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StackSpec. +func (in *StackSpec) DeepCopy() *StackSpec { + if in == nil { + return nil + } + out := new(StackSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateConfig) DeepCopyInto(out *UpdateConfig) { + *out = *in + if in.Parallelism != nil { + in, out := &in.Parallelism, &out.Parallelism + if *in == nil { + *out = nil + } else { + *out = new(uint64) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateConfig. +func (in *UpdateConfig) DeepCopy() *UpdateConfig { + if in == nil { + return nil + } + out := new(UpdateConfig) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/doc.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/doc.go new file mode 100644 index 0000000000..f5b4c798a2 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/doc.go @@ -0,0 +1,7 @@ +// Api versions allow the api contract for a resource to be changed while keeping +// backward compatibility by support multiple concurrent versions +// of the same resource + +// Package v1beta2 is the second version of the stack, containing a structured spec +// +k8s:openapi-gen=true +package v1beta2 diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/owner.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/owner.go new file mode 100644 index 0000000000..21ab500612 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/owner.go @@ -0,0 +1,30 @@ +package v1beta2 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/impersonation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Owner describes the user who created the stack +type Owner struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Owner impersonation.Config `json:"owner,omitempty"` +} + +func (o *Owner) clone() *Owner { + if o == nil { + return nil + } + result := new(Owner) + result.TypeMeta = o.TypeMeta + result.ObjectMeta = o.ObjectMeta + result.Owner = *result.Owner.Clone() + return result +} + +// DeepCopyObject clones the owner +func (o *Owner) DeepCopyObject() runtime.Object { + return o.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/register.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/register.go new file mode 100644 index 0000000000..23555093d5 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/register.go @@ -0,0 +1,42 @@ +package v1beta2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the name of the compose group +const GroupName = "compose.docker.com" + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta2"} + // SchemeBuilder is the scheme builder + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + // AddToScheme adds to scheme + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + localSchemeBuilder.Register(addKnownTypes) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Stack{}, + &StackList{}, + &Owner{}, + &ComposeFile{}, + &Scale{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} + +// GroupResource takes an unqualified resource and returns a Group qualified GroupResource +func GroupResource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/scale.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/scale.go new file mode 100644 index 0000000000..d268e35123 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/scale.go @@ -0,0 +1,29 @@ +package v1beta2 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/clone" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Scale contains the current/desired replica count for services in a stack. +type Scale struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec map[string]int `json:"spec,omitempty"` + Status map[string]int `json:"status,omitempty"` +} + +func (s *Scale) clone() *Scale { + return &Scale{ + TypeMeta: s.TypeMeta, + ObjectMeta: s.ObjectMeta, + Spec: clone.MapOfStringToInt(s.Spec), + Status: clone.MapOfStringToInt(s.Status), + } +} + +// DeepCopyObject clones the scale +func (s *Scale) DeepCopyObject() runtime.Object { + return s.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/stack.go new file mode 100644 index 0000000000..e46fb0f179 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/stack.go @@ -0,0 +1,270 @@ +package v1beta2 + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// StackList is a list of stacks +type StackList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Items []Stack `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// Stack is v1beta2's representation of a Stack +type Stack struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec *StackSpec `json:"spec,omitempty"` + Status *StackStatus `json:"status,omitempty"` +} + +// DeepCopyObject clones the stack +func (s *Stack) DeepCopyObject() runtime.Object { + return s.clone() +} + +// DeepCopyObject clones the stack list +func (s *StackList) DeepCopyObject() runtime.Object { + if s == nil { + return nil + } + result := new(StackList) + result.TypeMeta = s.TypeMeta + result.ListMeta = s.ListMeta + if s.Items == nil { + return result + } + result.Items = make([]Stack, len(s.Items)) + for ix, s := range s.Items { + result.Items[ix] = *s.clone() + } + return result +} + +func (s *Stack) clone() *Stack { + if s == nil { + return nil + } + result := new(Stack) + result.TypeMeta = s.TypeMeta + result.ObjectMeta = s.ObjectMeta + result.Spec = s.Spec.DeepCopy() + result.Status = s.Status.clone() + return result +} + +// StackSpec defines the desired state of Stack +// +k8s:deepcopy-gen=true +type StackSpec struct { + Services []ServiceConfig `json:"services,omitempty"` + Secrets map[string]SecretConfig `json:"secrets,omitempty"` + Configs map[string]ConfigObjConfig `json:"configs,omitempty"` +} + +// ServiceConfig is the configuration of one service +// +k8s:deepcopy-gen=true +type ServiceConfig struct { + Name string `json:"name,omitempty"` + + CapAdd []string `json:"cap_add,omitempty"` + CapDrop []string `json:"cap_drop,omitempty"` + Command []string `json:"command,omitempty"` + Configs []ServiceConfigObjConfig `json:"configs,omitempty"` + Deploy DeployConfig `json:"deploy,omitempty"` + Entrypoint []string `json:"entrypoint,omitempty"` + Environment map[string]*string `json:"environment,omitempty"` + ExtraHosts []string `json:"extra_hosts,omitempty"` + Hostname string `json:"hostname,omitempty"` + HealthCheck *HealthCheckConfig `json:"health_check,omitempty"` + Image string `json:"image,omitempty"` + Ipc string `json:"ipc,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Pid string `json:"pid,omitempty"` + Ports []ServicePortConfig `json:"ports,omitempty"` + Privileged bool `json:"privileged,omitempty"` + ReadOnly bool `json:"read_only,omitempty"` + Secrets []ServiceSecretConfig `json:"secrets,omitempty"` + StdinOpen bool `json:"stdin_open,omitempty"` + StopGracePeriod *time.Duration `json:"stop_grace_period,omitempty"` + Tmpfs []string `json:"tmpfs,omitempty"` + Tty bool `json:"tty,omitempty"` + User *int64 `json:"user,omitempty"` + Volumes []ServiceVolumeConfig `json:"volumes,omitempty"` + WorkingDir string `json:"working_dir,omitempty"` +} + +// ServicePortConfig is the port configuration for a service +// +k8s:deepcopy-gen=true +type ServicePortConfig struct { + Mode string `json:"mode,omitempty"` + Target uint32 `json:"target,omitempty"` + Published uint32 `json:"published,omitempty"` + Protocol string `json:"protocol,omitempty"` +} + +// FileObjectConfig is a config type for a file used by a service +// +k8s:deepcopy-gen=true +type FileObjectConfig struct { + Name string `json:"name,omitempty"` + File string `json:"file,omitempty"` + External External `json:"external,omitempty"` + Labels map[string]string `json:"labels,omitempty"` +} + +// SecretConfig for a secret +// +k8s:deepcopy-gen=true +type SecretConfig FileObjectConfig + +// ConfigObjConfig is the config for the swarm "Config" object +// +k8s:deepcopy-gen=true +type ConfigObjConfig FileObjectConfig + +// External identifies a Volume or Network as a reference to a resource that is +// not managed, and should already exist. +// External.name is deprecated and replaced by Volume.name +// +k8s:deepcopy-gen=true +type External struct { + Name string `json:"name,omitempty"` + External bool `json:"external,omitempty"` +} + +// FileReferenceConfig for a reference to a swarm file object +// +k8s:deepcopy-gen=true +type FileReferenceConfig struct { + Source string `json:"source,omitempty"` + Target string `json:"target,omitempty"` + UID string `json:"uid,omitempty"` + GID string `json:"gid,omitempty"` + Mode *uint32 `json:"mode,omitempty"` +} + +// ServiceConfigObjConfig is the config obj configuration for a service +// +k8s:deepcopy-gen=true +type ServiceConfigObjConfig FileReferenceConfig + +// ServiceSecretConfig is the secret configuration for a service +// +k8s:deepcopy-gen=true +type ServiceSecretConfig FileReferenceConfig + +// DeployConfig is the deployment configuration for a service +// +k8s:deepcopy-gen=true +type DeployConfig struct { + Mode string `json:"mode,omitempty"` + Replicas *uint64 `json:"replicas,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + UpdateConfig *UpdateConfig `json:"update_config,omitempty"` + Resources Resources `json:"resources,omitempty"` + RestartPolicy *RestartPolicy `json:"restart_policy,omitempty"` + Placement Placement `json:"placement,omitempty"` +} + +// UpdateConfig is the service update configuration +// +k8s:deepcopy-gen=true +type UpdateConfig struct { + Parallelism *uint64 `json:"paralellism,omitempty"` +} + +// Resources the resource limits and reservations +// +k8s:deepcopy-gen=true +type Resources struct { + Limits *Resource `json:"limits,omitempty"` + Reservations *Resource `json:"reservations,omitempty"` +} + +// Resource is a resource to be limited or reserved +// +k8s:deepcopy-gen=true +type Resource struct { + NanoCPUs string `json:"cpus,omitempty"` + MemoryBytes int64 `json:"memory,omitempty"` +} + +// RestartPolicy is the service restart policy +// +k8s:deepcopy-gen=true +type RestartPolicy struct { + Condition string `json:"condition,omitempty"` +} + +// Placement constraints for the service +// +k8s:deepcopy-gen=true +type Placement struct { + Constraints *Constraints `json:"constraints,omitempty"` +} + +// Constraints lists constraints that can be set on the service +// +k8s:deepcopy-gen=true +type Constraints struct { + OperatingSystem *Constraint + Architecture *Constraint + Hostname *Constraint + MatchLabels map[string]Constraint +} + +// Constraint defines a constraint and it's operator (== or !=) +// +k8s:deepcopy-gen=true +type Constraint struct { + Value string + Operator string +} + +// HealthCheckConfig the healthcheck configuration for a service +// +k8s:deepcopy-gen=true +type HealthCheckConfig struct { + Test []string `json:"test,omitempty"` + Timeout *time.Duration `json:"timeout,omitempty"` + Interval *time.Duration `json:"interval,omitempty"` + Retries *uint64 `json:"retries,omitempty"` +} + +// ServiceVolumeConfig are references to a volume used by a service +// +k8s:deepcopy-gen=true +type ServiceVolumeConfig struct { + Type string `json:"type,omitempty"` + Source string `json:"source,omitempty"` + Target string `json:"target,omitempty"` + ReadOnly bool `json:"read_only,omitempty"` +} + +// StackPhase is the deployment phase of a stack +type StackPhase string + +// These are valid conditions of a stack. +const ( + // StackAvailable means the stack is available. + StackAvailable StackPhase = "Available" + // StackProgressing means the deployment is progressing. + StackProgressing StackPhase = "Progressing" + // StackFailure is added in a stack when one of its members fails to be created + // or deleted. + StackFailure StackPhase = "Failure" + // StackReconciliationPending means the stack has not yet been reconciled + StackReconciliationPending StackPhase = "ReconciliationPending" +) + +// StackStatus defines the observed state of Stack +type StackStatus struct { + // Current condition of the stack. + // +optional + Phase StackPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=StackPhase"` + // A human readable message indicating details about the stack. + // +optional + Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` +} + +func (s *StackStatus) clone() *StackStatus { + if s == nil { + return nil + } + result := *s + return &result +} + +// Clone clones a Stack +func (s *Stack) Clone() *Stack { + return s.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/config.go b/vendor/github.com/docker/compose-on-kubernetes/api/config.go new file mode 100644 index 0000000000..8e091bd63f --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/config.go @@ -0,0 +1,26 @@ +package apis + +import ( + "os" + "path/filepath" + + "github.com/docker/docker/pkg/homedir" + "k8s.io/client-go/tools/clientcmd" +) + +// NewKubernetesConfig resolves the path to the desired Kubernetes configuration file based on +// the KUBECONFIG environment variable and command line flags. +func NewKubernetesConfig(configPath string) clientcmd.ClientConfig { + kubeConfig := configPath + if kubeConfig == "" { + if config := os.Getenv("KUBECONFIG"); config != "" { + kubeConfig = config + } else { + kubeConfig = filepath.Join(homedir.Get(), ".kube/config") + } + } + + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfig}, + &clientcmd.ConfigOverrides{}) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/doc.go b/vendor/github.com/docker/compose-on-kubernetes/api/doc.go new file mode 100644 index 0000000000..4b648ce8d5 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/doc.go @@ -0,0 +1,4 @@ +// +// +domain=docker.com + +package apis diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/labels/labels.go b/vendor/github.com/docker/compose-on-kubernetes/api/labels/labels.go new file mode 100644 index 0000000000..5709af0361 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/labels/labels.go @@ -0,0 +1,45 @@ +package labels + +import ( + "fmt" + "strings" +) + +const ( + // ForServiceName is the label for the service name. + ForServiceName = "com.docker.service.name" + // ForStackName is the label for the stack name. + ForStackName = "com.docker.stack.namespace" + // ForServiceID is the label for the service id. + ForServiceID = "com.docker.service.id" +) + +// ForService gives the labels to select a given service in a stack. +func ForService(stackName, serviceName string) map[string]string { + labels := map[string]string{} + + if serviceName != "" { + labels[ForServiceName] = serviceName + } + if stackName != "" { + labels[ForStackName] = stackName + } + if serviceName != "" && stackName != "" { + labels[ForServiceID] = stackName + "-" + serviceName + } + + return labels +} + +// SelectorForStack gives the labelSelector to use for a given stack. +// Specific service names can be passed to narrow down the selection. +func SelectorForStack(stackName string, serviceNames ...string) string { + switch len(serviceNames) { + case 0: + return fmt.Sprintf("%s=%s", ForStackName, stackName) + case 1: + return fmt.Sprintf("%s=%s,%s=%s", ForStackName, stackName, ForServiceName, serviceNames[0]) + default: + return fmt.Sprintf("%s=%s,%s in (%s)", ForStackName, stackName, ForServiceName, strings.Join(serviceNames, ",")) + } +} diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/vendor/golang.org/x/oauth2/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +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 Google Inc. 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. diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md new file mode 100644 index 0000000000..eb8dcee179 --- /dev/null +++ b/vendor/golang.org/x/oauth2/README.md @@ -0,0 +1,77 @@ +# OAuth2 for Go + +[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2) +[![GoDoc](https://godoc.org/golang.org/x/oauth2?status.svg)](https://godoc.org/golang.org/x/oauth2) + +oauth2 package contains a client implementation for OAuth 2.0 spec. + +## Installation + +~~~~ +go get golang.org/x/oauth2 +~~~~ + +Or you can manually git clone the repository to +`$(go env GOPATH)/src/golang.org/x/oauth2`. + +See godoc for further documentation and examples. + +* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2) +* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google) + + +## App Engine + +In change 96e89be (March 2015), we removed the `oauth2.Context2` type in favor +of the [`context.Context`](https://golang.org/x/net/context#Context) type from +the `golang.org/x/net/context` package + +This means it's no longer possible to use the "Classic App Engine" +`appengine.Context` type with the `oauth2` package. (You're using +Classic App Engine if you import the package `"appengine"`.) + +To work around this, you may use the new `"google.golang.org/appengine"` +package. This package has almost the same API as the `"appengine"` package, +but it can be fetched with `go get` and used on "Managed VMs" and well as +Classic App Engine. + +See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app) +for information on updating your app. + +If you don't want to update your entire app to use the new App Engine packages, +you may use both sets of packages in parallel, using only the new packages +with the `oauth2` package. + +```go +import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + newappengine "google.golang.org/appengine" + newurlfetch "google.golang.org/appengine/urlfetch" + + "appengine" +) + +func handler(w http.ResponseWriter, r *http.Request) { + var c appengine.Context = appengine.NewContext(r) + c.Infof("Logging a message with the old package") + + var ctx context.Context = newappengine.NewContext(r) + client := &http.Client{ + Transport: &oauth2.Transport{ + Source: google.AppEngineTokenSource(ctx, "scope"), + Base: &newurlfetch.Transport{Context: ctx}, + }, + } + client.Get("...") +} +``` + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit changes to +this repository, see https://golang.org/doc/contribute.html. + +The main issue tracker for the oauth2 repository is located at +https://github.com/golang/oauth2/issues. diff --git a/vendor/golang.org/x/oauth2/google/appengine.go b/vendor/golang.org/x/oauth2/google/appengine.go new file mode 100644 index 0000000000..50d918b878 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine.go @@ -0,0 +1,89 @@ +// Copyright 2014 The Go 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 google + +import ( + "sort" + "strings" + "sync" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// appengineFlex is set at init time by appengineflex_hook.go. If true, we are on App Engine Flex. +var appengineFlex bool + +// Set at init time by appengine_hook.go. If nil, we're not on App Engine. +var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error) + +// Set at init time by appengine_hook.go. If nil, we're not on App Engine. +var appengineAppIDFunc func(c context.Context) string + +// AppEngineTokenSource returns a token source that fetches tokens +// issued to the current App Engine application's service account. +// If you are implementing a 3-legged OAuth 2.0 flow on App Engine +// that involves user accounts, see oauth2.Config instead. +// +// The provided context must have come from appengine.NewContext. +func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + scopes := append([]string{}, scope...) + sort.Strings(scopes) + return &appEngineTokenSource{ + ctx: ctx, + scopes: scopes, + key: strings.Join(scopes, " "), + } +} + +// aeTokens helps the fetched tokens to be reused until their expiration. +var ( + aeTokensMu sync.Mutex + aeTokens = make(map[string]*tokenLock) // key is space-separated scopes +) + +type tokenLock struct { + mu sync.Mutex // guards t; held while fetching or updating t + t *oauth2.Token +} + +type appEngineTokenSource struct { + ctx context.Context + scopes []string + key string // to aeTokens map; space-separated scopes +} + +func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + + aeTokensMu.Lock() + tok, ok := aeTokens[ts.key] + if !ok { + tok = &tokenLock{} + aeTokens[ts.key] = tok + } + aeTokensMu.Unlock() + + tok.mu.Lock() + defer tok.mu.Unlock() + if tok.t.Valid() { + return tok.t, nil + } + access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...) + if err != nil { + return nil, err + } + tok.t = &oauth2.Token{ + AccessToken: access, + Expiry: exp, + } + return tok.t, nil +} diff --git a/vendor/golang.org/x/oauth2/google/appengine_hook.go b/vendor/golang.org/x/oauth2/google/appengine_hook.go new file mode 100644 index 0000000000..56669eaa98 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine_hook.go @@ -0,0 +1,14 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine appenginevm + +package google + +import "google.golang.org/appengine" + +func init() { + appengineTokenFunc = appengine.AccessToken + appengineAppIDFunc = appengine.AppID +} diff --git a/vendor/golang.org/x/oauth2/google/appengineflex_hook.go b/vendor/golang.org/x/oauth2/google/appengineflex_hook.go new file mode 100644 index 0000000000..5d0231af2d --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengineflex_hook.go @@ -0,0 +1,11 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appenginevm + +package google + +func init() { + appengineFlex = true // Flex doesn't support appengine.AccessToken; depend on metadata server. +} diff --git a/vendor/golang.org/x/oauth2/google/default.go b/vendor/golang.org/x/oauth2/google/default.go new file mode 100644 index 0000000000..a31607437d --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/default.go @@ -0,0 +1,115 @@ +// Copyright 2015 The Go 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 google + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + + "cloud.google.com/go/compute/metadata" + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// DefaultClient returns an HTTP Client that uses the +// DefaultTokenSource to obtain authentication credentials. +func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { + ts, err := DefaultTokenSource(ctx, scope...) + if err != nil { + return nil, err + } + return oauth2.NewClient(ctx, ts), nil +} + +// DefaultTokenSource returns the token source for +// "Application Default Credentials". +// It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource. +func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { + creds, err := FindDefaultCredentials(ctx, scope...) + if err != nil { + return nil, err + } + return creds.TokenSource, nil +} + +// Common implementation for FindDefaultCredentials. +func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCredentials, error) { + // First, try the environment variable. + const envVar = "GOOGLE_APPLICATION_CREDENTIALS" + if filename := os.Getenv(envVar); filename != "" { + creds, err := readCredentialsFile(ctx, filename, scopes) + if err != nil { + return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) + } + return creds, nil + } + + // Second, try a well-known file. + filename := wellKnownFile() + if creds, err := readCredentialsFile(ctx, filename, scopes); err == nil { + return creds, nil + } else if !os.IsNotExist(err) { + return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) + } + + // Third, if we're on Google App Engine use those credentials. + if appengineTokenFunc != nil && !appengineFlex { + return &DefaultCredentials{ + ProjectID: appengineAppIDFunc(ctx), + TokenSource: AppEngineTokenSource(ctx, scopes...), + }, nil + } + + // Fourth, if we're on Google Compute Engine use the metadata server. + if metadata.OnGCE() { + id, _ := metadata.ProjectID() + return &DefaultCredentials{ + ProjectID: id, + TokenSource: ComputeTokenSource(""), + }, nil + } + + // None are found; return helpful error. + const url = "https://developers.google.com/accounts/docs/application-default-credentials" + return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) +} + +// Common implementation for CredentialsFromJSON. +func credentialsFromJSON(ctx context.Context, jsonData []byte, scopes []string) (*DefaultCredentials, error) { + var f credentialsFile + if err := json.Unmarshal(jsonData, &f); err != nil { + return nil, err + } + ts, err := f.tokenSource(ctx, append([]string(nil), scopes...)) + if err != nil { + return nil, err + } + return &DefaultCredentials{ + ProjectID: f.ProjectID, + TokenSource: ts, + JSON: jsonData, + }, nil +} + +func wellKnownFile() string { + const f = "application_default_credentials.json" + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) + } + return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) +} + +func readCredentialsFile(ctx context.Context, filename string, scopes []string) (*DefaultCredentials, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return CredentialsFromJSON(ctx, b, scopes...) +} diff --git a/vendor/golang.org/x/oauth2/google/doc_go19.go b/vendor/golang.org/x/oauth2/google/doc_go19.go new file mode 100644 index 0000000000..2a86325fe3 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/doc_go19.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +// Package google provides support for making OAuth2 authorized and authenticated +// HTTP requests to Google APIs. It supports the Web server flow, client-side +// credentials, service accounts, Google Compute Engine service accounts, and Google +// App Engine service accounts. +// +// A brief overview of the package follows. For more information, please read +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +// +// OAuth2 Configs +// +// Two functions in this package return golang.org/x/oauth2.Config values from Google credential +// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON, +// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or +// create an http.Client. +// +// +// Credentials +// +// The Credentials type represents Google credentials, including Application Default +// Credentials. +// +// Use FindDefaultCredentials to obtain Application Default Credentials. +// FindDefaultCredentials looks in some well-known places for a credentials file, and +// will call AppEngineTokenSource or ComputeTokenSource as needed. +// +// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials, +// then use the credentials to construct an http.Client or an oauth2.TokenSource. +// +// Use CredentialsFromJSON to obtain credentials from either of the two JSON formats +// described in OAuth2 Configs, above. The TokenSource in the returned value is the +// same as the one obtained from the oauth2.Config returned from ConfigFromJSON or +// JWTConfigFromJSON, but the Credentials may contain additional information +// that is useful is some circumstances. +package google // import "golang.org/x/oauth2/google" diff --git a/vendor/golang.org/x/oauth2/google/doc_not_go19.go b/vendor/golang.org/x/oauth2/google/doc_not_go19.go new file mode 100644 index 0000000000..5c3c6e1481 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/doc_not_go19.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +// Package google provides support for making OAuth2 authorized and authenticated +// HTTP requests to Google APIs. It supports the Web server flow, client-side +// credentials, service accounts, Google Compute Engine service accounts, and Google +// App Engine service accounts. +// +// A brief overview of the package follows. For more information, please read +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +// +// OAuth2 Configs +// +// Two functions in this package return golang.org/x/oauth2.Config values from Google credential +// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON, +// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or +// create an http.Client. +// +// +// Credentials +// +// The DefaultCredentials type represents Google Application Default Credentials, as +// well as other forms of credential. +// +// Use FindDefaultCredentials to obtain Application Default Credentials. +// FindDefaultCredentials looks in some well-known places for a credentials file, and +// will call AppEngineTokenSource or ComputeTokenSource as needed. +// +// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials, +// then use the credentials to construct an http.Client or an oauth2.TokenSource. +// +// Use CredentialsFromJSON to obtain credentials from either of the two JSON +// formats described in OAuth2 Configs, above. (The DefaultCredentials returned may +// not be "Application Default Credentials".) The TokenSource in the returned value +// is the same as the one obtained from the oauth2.Config returned from +// ConfigFromJSON or JWTConfigFromJSON, but the DefaultCredentials may contain +// additional information that is useful is some circumstances. +package google // import "golang.org/x/oauth2/google" diff --git a/vendor/golang.org/x/oauth2/google/go19.go b/vendor/golang.org/x/oauth2/google/go19.go new file mode 100644 index 0000000000..4d0318b1e1 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/go19.go @@ -0,0 +1,57 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package google + +import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// Credentials holds Google credentials, including "Application Default Credentials". +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +type Credentials struct { + ProjectID string // may be empty + TokenSource oauth2.TokenSource + + // JSON contains the raw bytes from a JSON credentials file. + // This field may be nil if authentication is provided by the + // environment and not with a credentials file, e.g. when code is + // running on Google Cloud Platform. + JSON []byte +} + +// DefaultCredentials is the old name of Credentials. +// +// Deprecated: use Credentials instead. +type DefaultCredentials = Credentials + +// FindDefaultCredentials searches for "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +// 1. A JSON file whose path is specified by the +// GOOGLE_APPLICATION_CREDENTIALS environment variable. +// 2. A JSON file in a location known to the gcloud command-line tool. +// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +// On other systems, $HOME/.config/gcloud/application_default_credentials.json. +// 3. On Google App Engine it uses the appengine.AccessToken function. +// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches +// credentials from the metadata server. +// (In this final case any provided scopes are ignored.) +func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) { + return findDefaultCredentials(ctx, scopes) +} + +// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can +// represent either a Google Developers Console client_credentials.json file (as in +// ConfigFromJSON) or a Google Developers service account key file (as in +// JWTConfigFromJSON). +func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) { + return credentialsFromJSON(ctx, jsonData, scopes) +} diff --git a/vendor/golang.org/x/oauth2/google/google.go b/vendor/golang.org/x/oauth2/google/google.go new file mode 100644 index 0000000000..f7481fbcc6 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/google.go @@ -0,0 +1,192 @@ +// Copyright 2014 The Go 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 google + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "cloud.google.com/go/compute/metadata" + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" +) + +// Endpoint is Google's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://accounts.google.com/o/oauth2/auth", + TokenURL: "https://accounts.google.com/o/oauth2/token", +} + +// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow. +const JWTTokenURL = "https://accounts.google.com/o/oauth2/token" + +// ConfigFromJSON uses a Google Developers Console client_credentials.json +// file to construct a config. +// client_credentials.json can be downloaded from +// https://console.developers.google.com, under "Credentials". Download the Web +// application credentials in the JSON format and provide the contents of the +// file as jsonKey. +func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) { + type cred struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + RedirectURIs []string `json:"redirect_uris"` + AuthURI string `json:"auth_uri"` + TokenURI string `json:"token_uri"` + } + var j struct { + Web *cred `json:"web"` + Installed *cred `json:"installed"` + } + if err := json.Unmarshal(jsonKey, &j); err != nil { + return nil, err + } + var c *cred + switch { + case j.Web != nil: + c = j.Web + case j.Installed != nil: + c = j.Installed + default: + return nil, fmt.Errorf("oauth2/google: no credentials found") + } + if len(c.RedirectURIs) < 1 { + return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json") + } + return &oauth2.Config{ + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + RedirectURL: c.RedirectURIs[0], + Scopes: scope, + Endpoint: oauth2.Endpoint{ + AuthURL: c.AuthURI, + TokenURL: c.TokenURI, + }, + }, nil +} + +// JWTConfigFromJSON uses a Google Developers service account JSON key file to read +// the credentials that authorize and authenticate the requests. +// Create a service account on "Credentials" for your project at +// https://console.developers.google.com to download a JSON key file. +func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { + var f credentialsFile + if err := json.Unmarshal(jsonKey, &f); err != nil { + return nil, err + } + if f.Type != serviceAccountKey { + return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey) + } + scope = append([]string(nil), scope...) // copy + return f.jwtConfig(scope), nil +} + +// JSON key file types. +const ( + serviceAccountKey = "service_account" + userCredentialsKey = "authorized_user" +) + +// credentialsFile is the unmarshalled representation of a credentials file. +type credentialsFile struct { + Type string `json:"type"` // serviceAccountKey or userCredentialsKey + + // Service Account fields + ClientEmail string `json:"client_email"` + PrivateKeyID string `json:"private_key_id"` + PrivateKey string `json:"private_key"` + TokenURL string `json:"token_uri"` + ProjectID string `json:"project_id"` + + // User Credential fields + // (These typically come from gcloud auth.) + ClientSecret string `json:"client_secret"` + ClientID string `json:"client_id"` + RefreshToken string `json:"refresh_token"` +} + +func (f *credentialsFile) jwtConfig(scopes []string) *jwt.Config { + cfg := &jwt.Config{ + Email: f.ClientEmail, + PrivateKey: []byte(f.PrivateKey), + PrivateKeyID: f.PrivateKeyID, + Scopes: scopes, + TokenURL: f.TokenURL, + } + if cfg.TokenURL == "" { + cfg.TokenURL = JWTTokenURL + } + return cfg +} + +func (f *credentialsFile) tokenSource(ctx context.Context, scopes []string) (oauth2.TokenSource, error) { + switch f.Type { + case serviceAccountKey: + cfg := f.jwtConfig(scopes) + return cfg.TokenSource(ctx), nil + case userCredentialsKey: + cfg := &oauth2.Config{ + ClientID: f.ClientID, + ClientSecret: f.ClientSecret, + Scopes: scopes, + Endpoint: Endpoint, + } + tok := &oauth2.Token{RefreshToken: f.RefreshToken} + return cfg.TokenSource(ctx, tok), nil + case "": + return nil, errors.New("missing 'type' field in credentials") + default: + return nil, fmt.Errorf("unknown credential type: %q", f.Type) + } +} + +// ComputeTokenSource returns a token source that fetches access tokens +// from Google Compute Engine (GCE)'s metadata server. It's only valid to use +// this token source if your program is running on a GCE instance. +// If no account is specified, "default" is used. +// Further information about retrieving access tokens from the GCE metadata +// server can be found at https://cloud.google.com/compute/docs/authentication. +func ComputeTokenSource(account string) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, computeSource{account: account}) +} + +type computeSource struct { + account string +} + +func (cs computeSource) Token() (*oauth2.Token, error) { + if !metadata.OnGCE() { + return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE") + } + acct := cs.account + if acct == "" { + acct = "default" + } + tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token") + if err != nil { + return nil, err + } + var res struct { + AccessToken string `json:"access_token"` + ExpiresInSec int `json:"expires_in"` + TokenType string `json:"token_type"` + } + err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res) + if err != nil { + return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err) + } + if res.ExpiresInSec == 0 || res.AccessToken == "" { + return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata") + } + return &oauth2.Token{ + AccessToken: res.AccessToken, + TokenType: res.TokenType, + Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second), + }, nil +} diff --git a/vendor/golang.org/x/oauth2/google/jwt.go b/vendor/golang.org/x/oauth2/google/jwt.go new file mode 100644 index 0000000000..b0fdb3a888 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/jwt.go @@ -0,0 +1,74 @@ +// Copyright 2015 The Go 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 google + +import ( + "crypto/rsa" + "fmt" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" + "golang.org/x/oauth2/jws" +) + +// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON +// key file to read the credentials that authorize and authenticate the +// requests, and returns a TokenSource that does not use any OAuth2 flow but +// instead creates a JWT and sends that as the access token. +// The audience is typically a URL that specifies the scope of the credentials. +// +// Note that this is not a standard OAuth flow, but rather an +// optimization supported by a few Google services. +// Unless you know otherwise, you should use JWTConfigFromJSON instead. +func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) { + cfg, err := JWTConfigFromJSON(jsonKey) + if err != nil { + return nil, fmt.Errorf("google: could not parse JSON key: %v", err) + } + pk, err := internal.ParseKey(cfg.PrivateKey) + if err != nil { + return nil, fmt.Errorf("google: could not parse key: %v", err) + } + ts := &jwtAccessTokenSource{ + email: cfg.Email, + audience: audience, + pk: pk, + pkID: cfg.PrivateKeyID, + } + tok, err := ts.Token() + if err != nil { + return nil, err + } + return oauth2.ReuseTokenSource(tok, ts), nil +} + +type jwtAccessTokenSource struct { + email, audience string + pk *rsa.PrivateKey + pkID string +} + +func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) { + iat := time.Now() + exp := iat.Add(time.Hour) + cs := &jws.ClaimSet{ + Iss: ts.email, + Sub: ts.email, + Aud: ts.audience, + Iat: iat.Unix(), + Exp: exp.Unix(), + } + hdr := &jws.Header{ + Algorithm: "RS256", + Typ: "JWT", + KeyID: string(ts.pkID), + } + msg, err := jws.Encode(hdr, cs, ts.pk) + if err != nil { + return nil, fmt.Errorf("google: could not encode JWT: %v", err) + } + return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil +} diff --git a/vendor/golang.org/x/oauth2/google/not_go19.go b/vendor/golang.org/x/oauth2/google/not_go19.go new file mode 100644 index 0000000000..544e40624e --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/not_go19.go @@ -0,0 +1,54 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package google + +import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// DefaultCredentials holds Google credentials, including "Application Default Credentials". +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +type DefaultCredentials struct { + ProjectID string // may be empty + TokenSource oauth2.TokenSource + + // JSON contains the raw bytes from a JSON credentials file. + // This field may be nil if authentication is provided by the + // environment and not with a credentials file, e.g. when code is + // running on Google Cloud Platform. + JSON []byte +} + +// FindDefaultCredentials searches for "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +// 1. A JSON file whose path is specified by the +// GOOGLE_APPLICATION_CREDENTIALS environment variable. +// 2. A JSON file in a location known to the gcloud command-line tool. +// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +// On other systems, $HOME/.config/gcloud/application_default_credentials.json. +// 3. On Google App Engine it uses the appengine.AccessToken function. +// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches +// credentials from the metadata server. +// (In this final case any provided scopes are ignored.) +func FindDefaultCredentials(ctx context.Context, scopes ...string) (*DefaultCredentials, error) { + return findDefaultCredentials(ctx, scopes) +} + +// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can +// represent either a Google Developers Console client_credentials.json file (as in +// ConfigFromJSON) or a Google Developers service account key file (as in +// JWTConfigFromJSON). +// +// Note: despite the name, the returned credentials may not be Application Default Credentials. +func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*DefaultCredentials, error) { + return credentialsFromJSON(ctx, jsonData, scopes) +} diff --git a/vendor/golang.org/x/oauth2/google/sdk.go b/vendor/golang.org/x/oauth2/google/sdk.go new file mode 100644 index 0000000000..b9660caddf --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/sdk.go @@ -0,0 +1,201 @@ +// Copyright 2015 The Go 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 google + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +type sdkCredentials struct { + Data []struct { + Credential struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenExpiry *time.Time `json:"token_expiry"` + } `json:"credential"` + Key struct { + Account string `json:"account"` + Scope string `json:"scope"` + } `json:"key"` + } +} + +// An SDKConfig provides access to tokens from an account already +// authorized via the Google Cloud SDK. +type SDKConfig struct { + conf oauth2.Config + initialToken *oauth2.Token +} + +// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK +// account. If account is empty, the account currently active in +// Google Cloud SDK properties is used. +// Google Cloud SDK credentials must be created by running `gcloud auth` +// before using this function. +// The Google Cloud SDK is available at https://cloud.google.com/sdk/. +func NewSDKConfig(account string) (*SDKConfig, error) { + configPath, err := sdkConfigPath() + if err != nil { + return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err) + } + credentialsPath := filepath.Join(configPath, "credentials") + f, err := os.Open(credentialsPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err) + } + defer f.Close() + + var c sdkCredentials + if err := json.NewDecoder(f).Decode(&c); err != nil { + return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err) + } + if len(c.Data) == 0 { + return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath) + } + if account == "" { + propertiesPath := filepath.Join(configPath, "properties") + f, err := os.Open(propertiesPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err) + } + defer f.Close() + ini, err := parseINI(f) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err) + } + core, ok := ini["core"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini) + } + active, ok := core["account"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core) + } + account = active + } + + for _, d := range c.Data { + if account == "" || d.Key.Account == account { + if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" { + return nil, fmt.Errorf("oauth2/google: no token available for account %q", account) + } + var expiry time.Time + if d.Credential.TokenExpiry != nil { + expiry = *d.Credential.TokenExpiry + } + return &SDKConfig{ + conf: oauth2.Config{ + ClientID: d.Credential.ClientID, + ClientSecret: d.Credential.ClientSecret, + Scopes: strings.Split(d.Key.Scope, " "), + Endpoint: Endpoint, + RedirectURL: "oob", + }, + initialToken: &oauth2.Token{ + AccessToken: d.Credential.AccessToken, + RefreshToken: d.Credential.RefreshToken, + Expiry: expiry, + }, + }, nil + } + } + return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account) +} + +// Client returns an HTTP client using Google Cloud SDK credentials to +// authorize requests. The token will auto-refresh as necessary. The +// underlying http.RoundTripper will be obtained using the provided +// context. The returned client and its Transport should not be +// modified. +func (c *SDKConfig) Client(ctx context.Context) *http.Client { + return &http.Client{ + Transport: &oauth2.Transport{ + Source: c.TokenSource(ctx), + }, + } +} + +// TokenSource returns an oauth2.TokenSource that retrieve tokens from +// Google Cloud SDK credentials using the provided context. +// It will returns the current access token stored in the credentials, +// and refresh it when it expires, but it won't update the credentials +// with the new access token. +func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource { + return c.conf.TokenSource(ctx, c.initialToken) +} + +// Scopes are the OAuth 2.0 scopes the current account is authorized for. +func (c *SDKConfig) Scopes() []string { + return c.conf.Scopes +} + +func parseINI(ini io.Reader) (map[string]map[string]string, error) { + result := map[string]map[string]string{ + "": {}, // root section + } + scanner := bufio.NewScanner(ini) + currentSection := "" + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, ";") { + // comment. + continue + } + if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { + currentSection = strings.TrimSpace(line[1 : len(line)-1]) + result[currentSection] = map[string]string{} + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 && parts[0] != "" { + result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error scanning ini: %v", err) + } + return result, nil +} + +// sdkConfigPath tries to guess where the gcloud config is located. +// It can be overridden during tests. +var sdkConfigPath = func() (string, error) { + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil + } + homeDir := guessUnixHomeDir() + if homeDir == "" { + return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty") + } + return filepath.Join(homeDir, ".config", "gcloud"), nil +} + +func guessUnixHomeDir() string { + // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 + if v := os.Getenv("HOME"); v != "" { + return v + } + // Else, fall back to user.Current: + if u, err := user.Current(); err == nil { + return u.HomeDir + } + return "" +} diff --git a/vendor/golang.org/x/oauth2/internal/client_appengine.go b/vendor/golang.org/x/oauth2/internal/client_appengine.go new file mode 100644 index 0000000000..7434871880 --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/client_appengine.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package internal + +import "google.golang.org/appengine/urlfetch" + +func init() { + appengineClientHook = urlfetch.Client +} diff --git a/vendor/golang.org/x/oauth2/internal/doc.go b/vendor/golang.org/x/oauth2/internal/doc.go new file mode 100644 index 0000000000..03265e888a --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/doc.go @@ -0,0 +1,6 @@ +// Copyright 2017 The Go 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 internal contains support packages for oauth2 package. +package internal diff --git a/vendor/golang.org/x/oauth2/internal/oauth2.go b/vendor/golang.org/x/oauth2/internal/oauth2.go new file mode 100644 index 0000000000..fc63fcab3f --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/oauth2.go @@ -0,0 +1,37 @@ +// Copyright 2014 The Go 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 internal + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" +) + +// ParseKey converts the binary contents of a private key file +// to an *rsa.PrivateKey. It detects whether the private key is in a +// PEM container or not. If so, it extracts the the private key +// from PEM container before conversion. It only supports PEM +// containers with no passphrase. +func ParseKey(key []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(key) + if block != nil { + key = block.Bytes + } + parsedKey, err := x509.ParsePKCS8PrivateKey(key) + if err != nil { + parsedKey, err = x509.ParsePKCS1PrivateKey(key) + if err != nil { + return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err) + } + } + parsed, ok := parsedKey.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("private key is invalid") + } + return parsed, nil +} diff --git a/vendor/golang.org/x/oauth2/internal/token.go b/vendor/golang.org/x/oauth2/internal/token.go new file mode 100644 index 0000000000..999e668e6f --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/token.go @@ -0,0 +1,270 @@ +// Copyright 2014 The Go 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 internal + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "mime" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/net/context/ctxhttp" +) + +// Token represents the credentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// This type is a mirror of oauth2.Token and exists to break +// an otherwise-circular dependency. Other internal packages +// should convert this Token into an oauth2.Token before use. +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string + + // Expiry is the optional expiration time of the access token. + // + // If zero, TokenSource implementations will reuse the same + // token forever and RefreshToken or equivalent + // mechanisms for that TokenSource will not be used. + Expiry time.Time + + // Raw optionally contains extra metadata from the server + // when updating a token. + Raw interface{} +} + +// tokenJSON is the struct representing the HTTP response from OAuth2 +// providers returning a token in JSON form. +type tokenJSON struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + RefreshToken string `json:"refresh_token"` + ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number + Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in +} + +func (e *tokenJSON) expiry() (t time.Time) { + if v := e.ExpiresIn; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + if v := e.Expires; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + return +} + +type expirationTime int32 + +func (e *expirationTime) UnmarshalJSON(b []byte) error { + var n json.Number + err := json.Unmarshal(b, &n) + if err != nil { + return err + } + i, err := n.Int64() + if err != nil { + return err + } + *e = expirationTime(i) + return nil +} + +var brokenAuthHeaderProviders = []string{ + "https://accounts.google.com/", + "https://api.codeswholesale.com/oauth/token", + "https://api.dropbox.com/", + "https://api.dropboxapi.com/", + "https://api.instagram.com/", + "https://api.netatmo.net/", + "https://api.odnoklassniki.ru/", + "https://api.pushbullet.com/", + "https://api.soundcloud.com/", + "https://api.twitch.tv/", + "https://id.twitch.tv/", + "https://app.box.com/", + "https://connect.stripe.com/", + "https://login.mailchimp.com/", + "https://login.microsoftonline.com/", + "https://login.salesforce.com/", + "https://login.windows.net", + "https://login.live.com/", + "https://oauth.sandbox.trainingpeaks.com/", + "https://oauth.trainingpeaks.com/", + "https://oauth.vk.com/", + "https://openapi.baidu.com/", + "https://slack.com/", + "https://test-sandbox.auth.corp.google.com", + "https://test.salesforce.com/", + "https://user.gini.net/", + "https://www.douban.com/", + "https://www.googleapis.com/", + "https://www.linkedin.com/", + "https://www.strava.com/oauth/", + "https://www.wunderlist.com/oauth/", + "https://api.patreon.com/", + "https://sandbox.codeswholesale.com/oauth/token", + "https://api.sipgate.com/v1/authorization/oauth", + "https://api.medium.com/v1/tokens", + "https://log.finalsurge.com/oauth/token", + "https://multisport.todaysplan.com.au/rest/oauth/access_token", + "https://whats.todaysplan.com.au/rest/oauth/access_token", + "https://stackoverflow.com/oauth/access_token", +} + +// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints. +var brokenAuthHeaderDomains = []string{ + ".auth0.com", + ".force.com", + ".myshopify.com", + ".okta.com", + ".oktapreview.com", +} + +func RegisterBrokenAuthHeaderProvider(tokenURL string) { + brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL) +} + +// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL +// implements the OAuth2 spec correctly +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +// In summary: +// - Reddit only accepts client secret in the Authorization header +// - Dropbox accepts either it in URL param or Auth header, but not both. +// - Google only accepts URL param (not spec compliant?), not Auth header +// - Stripe only accepts client secret in Auth header with Bearer method, not Basic +func providerAuthHeaderWorks(tokenURL string) bool { + for _, s := range brokenAuthHeaderProviders { + if strings.HasPrefix(tokenURL, s) { + // Some sites fail to implement the OAuth2 spec fully. + return false + } + } + + if u, err := url.Parse(tokenURL); err == nil { + for _, s := range brokenAuthHeaderDomains { + if strings.HasSuffix(u.Host, s) { + return false + } + } + } + + // Assume the provider implements the spec properly + // otherwise. We can add more exceptions as they're + // discovered. We will _not_ be adding configurable hooks + // to this package to let users select server bugs. + return true +} + +func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) { + bustedAuth := !providerAuthHeaderWorks(tokenURL) + if bustedAuth { + if clientID != "" { + v.Set("client_id", clientID) + } + if clientSecret != "" { + v.Set("client_secret", clientSecret) + } + } + req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if !bustedAuth { + req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret)) + } + r, err := ctxhttp.Do(ctx, ContextClient(ctx), req) + if err != nil { + return nil, err + } + defer r.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if code := r.StatusCode; code < 200 || code > 299 { + return nil, &RetrieveError{ + Response: r, + Body: body, + } + } + + var token *Token + content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) + switch content { + case "application/x-www-form-urlencoded", "text/plain": + vals, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + token = &Token{ + AccessToken: vals.Get("access_token"), + TokenType: vals.Get("token_type"), + RefreshToken: vals.Get("refresh_token"), + Raw: vals, + } + e := vals.Get("expires_in") + if e == "" { + // TODO(jbd): Facebook's OAuth2 implementation is broken and + // returns expires_in field in expires. Remove the fallback to expires, + // when Facebook fixes their implementation. + e = vals.Get("expires") + } + expires, _ := strconv.Atoi(e) + if expires != 0 { + token.Expiry = time.Now().Add(time.Duration(expires) * time.Second) + } + default: + var tj tokenJSON + if err = json.Unmarshal(body, &tj); err != nil { + return nil, err + } + token = &Token{ + AccessToken: tj.AccessToken, + TokenType: tj.TokenType, + RefreshToken: tj.RefreshToken, + Expiry: tj.expiry(), + Raw: make(map[string]interface{}), + } + json.Unmarshal(body, &token.Raw) // no error checks for optional fields + } + // Don't overwrite `RefreshToken` with an empty value + // if this was a token refreshing request. + if token.RefreshToken == "" { + token.RefreshToken = v.Get("refresh_token") + } + if token.AccessToken == "" { + return token, errors.New("oauth2: server response missing access_token") + } + return token, nil +} + +type RetrieveError struct { + Response *http.Response + Body []byte +} + +func (r *RetrieveError) Error() string { + return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body) +} diff --git a/vendor/golang.org/x/oauth2/internal/transport.go b/vendor/golang.org/x/oauth2/internal/transport.go new file mode 100644 index 0000000000..d16f9ae1fe --- /dev/null +++ b/vendor/golang.org/x/oauth2/internal/transport.go @@ -0,0 +1,34 @@ +// Copyright 2014 The Go 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 internal + +import ( + "net/http" + + "golang.org/x/net/context" +) + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient ContextKey + +// ContextKey is just an empty struct. It exists so HTTPClient can be +// an immutable public variable with a unique type. It's immutable +// because nobody else can create a ContextKey, being unexported. +type ContextKey struct{} + +var appengineClientHook func(context.Context) *http.Client + +func ContextClient(ctx context.Context) *http.Client { + if ctx != nil { + if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok { + return hc + } + } + if appengineClientHook != nil { + return appengineClientHook(ctx) + } + return http.DefaultClient +} diff --git a/vendor/golang.org/x/oauth2/jws/jws.go b/vendor/golang.org/x/oauth2/jws/jws.go new file mode 100644 index 0000000000..683d2d271a --- /dev/null +++ b/vendor/golang.org/x/oauth2/jws/jws.go @@ -0,0 +1,182 @@ +// Copyright 2014 The Go 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 jws provides a partial implementation +// of JSON Web Signature encoding and decoding. +// It exists to support the golang.org/x/oauth2 package. +// +// See RFC 7515. +// +// Deprecated: this package is not intended for public use and might be +// removed in the future. It exists for internal use only. +// Please switch to another JWS package or copy this package into your own +// source tree. +package jws // import "golang.org/x/oauth2/jws" + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + "time" +) + +// ClaimSet contains information about the JWT signature including the +// permissions being requested (scopes), the target of the token, the issuer, +// the time the token was issued, and the lifetime of the token. +type ClaimSet struct { + Iss string `json:"iss"` // email address of the client_id of the application making the access token request + Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests + Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). + Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch) + Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch) + Typ string `json:"typ,omitempty"` // token type (Optional). + + // Email for which the application is requesting delegated access (Optional). + Sub string `json:"sub,omitempty"` + + // The old name of Sub. Client keeps setting Prn to be + // complaint with legacy OAuth 2.0 providers. (Optional) + Prn string `json:"prn,omitempty"` + + // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 + // This array is marshalled using custom code (see (c *ClaimSet) encode()). + PrivateClaims map[string]interface{} `json:"-"` +} + +func (c *ClaimSet) encode() (string, error) { + // Reverting time back for machines whose time is not perfectly in sync. + // If client machine's time is in the future according + // to Google servers, an access token will not be issued. + now := time.Now().Add(-10 * time.Second) + if c.Iat == 0 { + c.Iat = now.Unix() + } + if c.Exp == 0 { + c.Exp = now.Add(time.Hour).Unix() + } + if c.Exp < c.Iat { + return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat) + } + + b, err := json.Marshal(c) + if err != nil { + return "", err + } + + if len(c.PrivateClaims) == 0 { + return base64.RawURLEncoding.EncodeToString(b), nil + } + + // Marshal private claim set and then append it to b. + prv, err := json.Marshal(c.PrivateClaims) + if err != nil { + return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims) + } + + // Concatenate public and private claim JSON objects. + if !bytes.HasSuffix(b, []byte{'}'}) { + return "", fmt.Errorf("jws: invalid JSON %s", b) + } + if !bytes.HasPrefix(prv, []byte{'{'}) { + return "", fmt.Errorf("jws: invalid JSON %s", prv) + } + b[len(b)-1] = ',' // Replace closing curly brace with a comma. + b = append(b, prv[1:]...) // Append private claims. + return base64.RawURLEncoding.EncodeToString(b), nil +} + +// Header represents the header for the signed JWS payloads. +type Header struct { + // The algorithm used for signature. + Algorithm string `json:"alg"` + + // Represents the token type. + Typ string `json:"typ"` + + // The optional hint of which key is being used. + KeyID string `json:"kid,omitempty"` +} + +func (h *Header) encode() (string, error) { + b, err := json.Marshal(h) + if err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(b), nil +} + +// Decode decodes a claim set from a JWS payload. +func Decode(payload string) (*ClaimSet, error) { + // decode returned id token to get expiry + s := strings.Split(payload, ".") + if len(s) < 2 { + // TODO(jbd): Provide more context about the error. + return nil, errors.New("jws: invalid token received") + } + decoded, err := base64.RawURLEncoding.DecodeString(s[1]) + if err != nil { + return nil, err + } + c := &ClaimSet{} + err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c) + return c, err +} + +// Signer returns a signature for the given data. +type Signer func(data []byte) (sig []byte, err error) + +// EncodeWithSigner encodes a header and claim set with the provided signer. +func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) { + head, err := header.encode() + if err != nil { + return "", err + } + cs, err := c.encode() + if err != nil { + return "", err + } + ss := fmt.Sprintf("%s.%s", head, cs) + sig, err := sg([]byte(ss)) + if err != nil { + return "", err + } + return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil +} + +// Encode encodes a signed JWS with provided header and claim set. +// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key. +func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) { + sg := func(data []byte) (sig []byte, err error) { + h := sha256.New() + h.Write(data) + return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) + } + return EncodeWithSigner(header, c, sg) +} + +// Verify tests whether the provided JWT token's signature was produced by the private key +// associated with the supplied public key. +func Verify(token string, key *rsa.PublicKey) error { + parts := strings.Split(token, ".") + if len(parts) != 3 { + return errors.New("jws: invalid token received, token must have 3 parts") + } + + signedContent := parts[0] + "." + parts[1] + signatureString, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + return err + } + + h := sha256.New() + h.Write([]byte(signedContent)) + return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString)) +} diff --git a/vendor/golang.org/x/oauth2/jwt/jwt.go b/vendor/golang.org/x/oauth2/jwt/jwt.go new file mode 100644 index 0000000000..e08f315959 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jwt/jwt.go @@ -0,0 +1,162 @@ +// Copyright 2014 The Go 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 jwt implements the OAuth 2.0 JSON Web Token flow, commonly +// known as "two-legged OAuth 2.0". +// +// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12 +package jwt + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" + "golang.org/x/oauth2/jws" +) + +var ( + defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" + defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"} +) + +// Config is the configuration for using JWT to fetch tokens, +// commonly known as "two-legged OAuth 2.0". +type Config struct { + // Email is the OAuth client identifier used when communicating with + // the configured OAuth provider. + Email string + + // PrivateKey contains the contents of an RSA private key or the + // contents of a PEM file that contains a private key. The provided + // private key is used to sign JWT payloads. + // PEM containers with a passphrase are not supported. + // Use the following command to convert a PKCS 12 file into a PEM. + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + PrivateKey []byte + + // PrivateKeyID contains an optional hint indicating which key is being + // used. + PrivateKeyID string + + // Subject is the optional user to impersonate. + Subject string + + // Scopes optionally specifies a list of requested permission scopes. + Scopes []string + + // TokenURL is the endpoint required to complete the 2-legged JWT flow. + TokenURL string + + // Expires optionally specifies how long the token is valid for. + Expires time.Duration +} + +// TokenSource returns a JWT TokenSource using the configuration +// in c and the HTTP client from the provided context. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c}) +} + +// Client returns an HTTP client wrapping the context's +// HTTP transport and adding Authorization headers with tokens +// obtained from c. +// +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// jwtSource is a source that always does a signed JWT request for a token. +// It should typically be wrapped with a reuseTokenSource. +type jwtSource struct { + ctx context.Context + conf *Config +} + +func (js jwtSource) Token() (*oauth2.Token, error) { + pk, err := internal.ParseKey(js.conf.PrivateKey) + if err != nil { + return nil, err + } + hc := oauth2.NewClient(js.ctx, nil) + claimSet := &jws.ClaimSet{ + Iss: js.conf.Email, + Scope: strings.Join(js.conf.Scopes, " "), + Aud: js.conf.TokenURL, + } + if subject := js.conf.Subject; subject != "" { + claimSet.Sub = subject + // prn is the old name of sub. Keep setting it + // to be compatible with legacy OAuth 2.0 providers. + claimSet.Prn = subject + } + if t := js.conf.Expires; t > 0 { + claimSet.Exp = time.Now().Add(t).Unix() + } + h := *defaultHeader + h.KeyID = js.conf.PrivateKeyID + payload, err := jws.Encode(&h, claimSet, pk) + if err != nil { + return nil, err + } + v := url.Values{} + v.Set("grant_type", defaultGrantType) + v.Set("assertion", payload) + resp, err := hc.PostForm(js.conf.TokenURL, v) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if c := resp.StatusCode; c < 200 || c > 299 { + return nil, &oauth2.RetrieveError{ + Response: resp, + Body: body, + } + } + // tokenRes is the JSON response body. + var tokenRes struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + IDToken string `json:"id_token"` + ExpiresIn int64 `json:"expires_in"` // relative seconds from now + } + if err := json.Unmarshal(body, &tokenRes); err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + token := &oauth2.Token{ + AccessToken: tokenRes.AccessToken, + TokenType: tokenRes.TokenType, + } + raw := make(map[string]interface{}) + json.Unmarshal(body, &raw) // no error checks for optional fields + token = token.WithExtra(raw) + + if secs := tokenRes.ExpiresIn; secs > 0 { + token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) + } + if v := tokenRes.IDToken; v != "" { + // decode returned id token to get expiry + claimSet, err := jws.Decode(v) + if err != nil { + return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err) + } + token.Expiry = time.Unix(claimSet.Exp, 0) + } + return token, nil +} diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go new file mode 100644 index 0000000000..16775d081b --- /dev/null +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -0,0 +1,362 @@ +// Copyright 2014 The Go 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 oauth2 provides support for making +// OAuth2 authorized and authenticated HTTP requests, +// as specified in RFC 6749. +// It can additionally grant authorization with Bearer JWT. +package oauth2 // import "golang.org/x/oauth2" + +import ( + "bytes" + "errors" + "net/http" + "net/url" + "strings" + "sync" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" +) + +// NoContext is the default context you should supply if not using +// your own context.Context (see https://golang.org/x/net/context). +// +// Deprecated: Use context.Background() or context.TODO() instead. +var NoContext = context.TODO() + +// RegisterBrokenAuthHeaderProvider registers an OAuth2 server +// identified by the tokenURL prefix as an OAuth2 implementation +// which doesn't support the HTTP Basic authentication +// scheme to authenticate with the authorization server. +// Once a server is registered, credentials (client_id and client_secret) +// will be passed as query parameters rather than being present +// in the Authorization header. +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +func RegisterBrokenAuthHeaderProvider(tokenURL string) { + internal.RegisterBrokenAuthHeaderProvider(tokenURL) +} + +// Config describes a typical 3-legged OAuth2 flow, with both the +// client application information and the server's endpoint URLs. +// For the client credentials 2-legged OAuth2 flow, see the clientcredentials +// package (https://golang.org/x/oauth2/clientcredentials). +type Config struct { + // ClientID is the application's ID. + ClientID string + + // ClientSecret is the application's secret. + ClientSecret string + + // Endpoint contains the resource server's token endpoint + // URLs. These are constants specific to each server and are + // often available via site-specific packages, such as + // google.Endpoint or github.Endpoint. + Endpoint Endpoint + + // RedirectURL is the URL to redirect users going through + // the OAuth flow, after the resource owner's URLs. + RedirectURL string + + // Scope specifies optional requested permissions. + Scopes []string +} + +// A TokenSource is anything that can return a token. +type TokenSource interface { + // Token returns a token or an error. + // Token must be safe for concurrent use by multiple goroutines. + // The returned Token must not be modified. + Token() (*Token, error) +} + +// Endpoint contains the OAuth 2.0 provider's authorization and token +// endpoint URLs. +type Endpoint struct { + AuthURL string + TokenURL string +} + +var ( + // AccessTypeOnline and AccessTypeOffline are options passed + // to the Options.AuthCodeURL method. They modify the + // "access_type" field that gets sent in the URL returned by + // AuthCodeURL. + // + // Online is the default if neither is specified. If your + // application needs to refresh access tokens when the user + // is not present at the browser, then use offline. This will + // result in your application obtaining a refresh token the + // first time your application exchanges an authorization + // code for a user. + AccessTypeOnline AuthCodeOption = SetAuthURLParam("access_type", "online") + AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline") + + // ApprovalForce forces the users to view the consent dialog + // and confirm the permissions request at the URL returned + // from AuthCodeURL, even if they've already done so. + ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force") +) + +// An AuthCodeOption is passed to Config.AuthCodeURL. +type AuthCodeOption interface { + setValue(url.Values) +} + +type setParam struct{ k, v string } + +func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) } + +// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters +// to a provider's authorization endpoint. +func SetAuthURLParam(key, value string) AuthCodeOption { + return setParam{key, value} +} + +// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page +// that asks for permissions for the required scopes explicitly. +// +// State is a token to protect the user from CSRF attacks. You must +// always provide a non-empty string and validate that it matches the +// the state query parameter on your redirect callback. +// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info. +// +// Opts may include AccessTypeOnline or AccessTypeOffline, as well +// as ApprovalForce. +// It can also be used to pass the PKCE challange. +// See https://www.oauth.com/oauth2-servers/pkce/ for more info. +func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { + var buf bytes.Buffer + buf.WriteString(c.Endpoint.AuthURL) + v := url.Values{ + "response_type": {"code"}, + "client_id": {c.ClientID}, + } + if c.RedirectURL != "" { + v.Set("redirect_uri", c.RedirectURL) + } + if len(c.Scopes) > 0 { + v.Set("scope", strings.Join(c.Scopes, " ")) + } + if state != "" { + // TODO(light): Docs say never to omit state; don't allow empty. + v.Set("state", state) + } + for _, opt := range opts { + opt.setValue(v) + } + if strings.Contains(c.Endpoint.AuthURL, "?") { + buf.WriteByte('&') + } else { + buf.WriteByte('?') + } + buf.WriteString(v.Encode()) + return buf.String() +} + +// PasswordCredentialsToken converts a resource owner username and password +// pair into a token. +// +// Per the RFC, this grant type should only be used "when there is a high +// degree of trust between the resource owner and the client (e.g., the client +// is part of the device operating system or a highly privileged application), +// and when other authorization grant types are not available." +// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info. +// +// The HTTP client to use is derived from the context. +// If nil, http.DefaultClient is used. +func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) { + v := url.Values{ + "grant_type": {"password"}, + "username": {username}, + "password": {password}, + } + if len(c.Scopes) > 0 { + v.Set("scope", strings.Join(c.Scopes, " ")) + } + return retrieveToken(ctx, c, v) +} + +// Exchange converts an authorization code into a token. +// +// It is used after a resource provider redirects the user back +// to the Redirect URI (the URL obtained from AuthCodeURL). +// +// The HTTP client to use is derived from the context. +// If a client is not provided via the context, http.DefaultClient is used. +// +// The code will be in the *http.Request.FormValue("code"). Before +// calling Exchange, be sure to validate FormValue("state"). +// +// Opts may include the PKCE verifier code if previously used in AuthCodeURL. +// See https://www.oauth.com/oauth2-servers/pkce/ for more info. +func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) { + v := url.Values{ + "grant_type": {"authorization_code"}, + "code": {code}, + } + if c.RedirectURL != "" { + v.Set("redirect_uri", c.RedirectURL) + } + for _, opt := range opts { + opt.setValue(v) + } + return retrieveToken(ctx, c, v) +} + +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context, t *Token) *http.Client { + return NewClient(ctx, c.TokenSource(ctx, t)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context. +// +// Most users will use Config.Client instead. +func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource { + tkr := &tokenRefresher{ + ctx: ctx, + conf: c, + } + if t != nil { + tkr.refreshToken = t.RefreshToken + } + return &reuseTokenSource{ + t: t, + new: tkr, + } +} + +// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token" +// HTTP requests to renew a token using a RefreshToken. +type tokenRefresher struct { + ctx context.Context // used to get HTTP requests + conf *Config + refreshToken string +} + +// WARNING: Token is not safe for concurrent access, as it +// updates the tokenRefresher's refreshToken field. +// Within this package, it is used by reuseTokenSource which +// synchronizes calls to this method with its own mutex. +func (tf *tokenRefresher) Token() (*Token, error) { + if tf.refreshToken == "" { + return nil, errors.New("oauth2: token expired and refresh token is not set") + } + + tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ + "grant_type": {"refresh_token"}, + "refresh_token": {tf.refreshToken}, + }) + + if err != nil { + return nil, err + } + if tf.refreshToken != tk.RefreshToken { + tf.refreshToken = tk.RefreshToken + } + return tk, err +} + +// reuseTokenSource is a TokenSource that holds a single token in memory +// and validates its expiry before each call to retrieve it with +// Token. If it's expired, it will be auto-refreshed using the +// new TokenSource. +type reuseTokenSource struct { + new TokenSource // called when t is expired. + + mu sync.Mutex // guards t + t *Token +} + +// Token returns the current token if it's still valid, else will +// refresh the current token (using r.Context for HTTP client +// information) and return the new one. +func (s *reuseTokenSource) Token() (*Token, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.t.Valid() { + return s.t, nil + } + t, err := s.new.Token() + if err != nil { + return nil, err + } + s.t = t + return t, nil +} + +// StaticTokenSource returns a TokenSource that always returns the same token. +// Because the provided token t is never refreshed, StaticTokenSource is only +// useful for tokens that never expire. +func StaticTokenSource(t *Token) TokenSource { + return staticTokenSource{t} +} + +// staticTokenSource is a TokenSource that always returns the same Token. +type staticTokenSource struct { + t *Token +} + +func (s staticTokenSource) Token() (*Token, error) { + return s.t, nil +} + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient internal.ContextKey + +// NewClient creates an *http.Client from a Context and TokenSource. +// The returned client is not valid beyond the lifetime of the context. +// +// Note that if a custom *http.Client is provided via the Context it +// is used only for token acquisition and is not used to configure the +// *http.Client returned from NewClient. +// +// As a special case, if src is nil, a non-OAuth2 client is returned +// using the provided context. This exists to support related OAuth2 +// packages. +func NewClient(ctx context.Context, src TokenSource) *http.Client { + if src == nil { + return internal.ContextClient(ctx) + } + return &http.Client{ + Transport: &Transport{ + Base: internal.ContextClient(ctx).Transport, + Source: ReuseTokenSource(nil, src), + }, + } +} + +// ReuseTokenSource returns a TokenSource which repeatedly returns the +// same token as long as it's valid, starting with t. +// When its cached token is invalid, a new token is obtained from src. +// +// ReuseTokenSource is typically used to reuse tokens from a cache +// (such as a file on disk) between runs of a program, rather than +// obtaining new tokens unnecessarily. +// +// The initial token t may be nil, in which case the TokenSource is +// wrapped in a caching version if it isn't one already. This also +// means it's always safe to wrap ReuseTokenSource around any other +// TokenSource without adverse effects. +func ReuseTokenSource(t *Token, src TokenSource) TokenSource { + // Don't wrap a reuseTokenSource in itself. That would work, + // but cause an unnecessary number of mutex operations. + // Just build the equivalent one. + if rt, ok := src.(*reuseTokenSource); ok { + if t == nil { + // Just use it directly. + return rt + } + src = rt.new + } + return &reuseTokenSource{ + t: t, + new: src, + } +} diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go new file mode 100644 index 0000000000..34db8cdc8a --- /dev/null +++ b/vendor/golang.org/x/oauth2/token.go @@ -0,0 +1,175 @@ +// Copyright 2014 The Go 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 oauth2 + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" +) + +// expiryDelta determines how earlier a token should be considered +// expired than its actual expiration time. It is used to avoid late +// expirations due to client-server time mismatches. +const expiryDelta = 10 * time.Second + +// Token represents the credentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// Most users of this package should not access fields of Token +// directly. They're exported mostly for use by related packages +// implementing derivative OAuth2 flows. +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string `json:"access_token"` + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string `json:"token_type,omitempty"` + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string `json:"refresh_token,omitempty"` + + // Expiry is the optional expiration time of the access token. + // + // If zero, TokenSource implementations will reuse the same + // token forever and RefreshToken or equivalent + // mechanisms for that TokenSource will not be used. + Expiry time.Time `json:"expiry,omitempty"` + + // raw optionally contains extra metadata from the server + // when updating a token. + raw interface{} +} + +// Type returns t.TokenType if non-empty, else "Bearer". +func (t *Token) Type() string { + if strings.EqualFold(t.TokenType, "bearer") { + return "Bearer" + } + if strings.EqualFold(t.TokenType, "mac") { + return "MAC" + } + if strings.EqualFold(t.TokenType, "basic") { + return "Basic" + } + if t.TokenType != "" { + return t.TokenType + } + return "Bearer" +} + +// SetAuthHeader sets the Authorization header to r using the access +// token in t. +// +// This method is unnecessary when using Transport or an HTTP Client +// returned by this package. +func (t *Token) SetAuthHeader(r *http.Request) { + r.Header.Set("Authorization", t.Type()+" "+t.AccessToken) +} + +// WithExtra returns a new Token that's a clone of t, but using the +// provided raw extra map. This is only intended for use by packages +// implementing derivative OAuth2 flows. +func (t *Token) WithExtra(extra interface{}) *Token { + t2 := new(Token) + *t2 = *t + t2.raw = extra + return t2 +} + +// Extra returns an extra field. +// Extra fields are key-value pairs returned by the server as a +// part of the token retrieval response. +func (t *Token) Extra(key string) interface{} { + if raw, ok := t.raw.(map[string]interface{}); ok { + return raw[key] + } + + vals, ok := t.raw.(url.Values) + if !ok { + return nil + } + + v := vals.Get(key) + switch s := strings.TrimSpace(v); strings.Count(s, ".") { + case 0: // Contains no "."; try to parse as int + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + return i + } + case 1: // Contains a single "."; try to parse as float + if f, err := strconv.ParseFloat(s, 64); err == nil { + return f + } + } + + return v +} + +// expired reports whether the token is expired. +// t must be non-nil. +func (t *Token) expired() bool { + if t.Expiry.IsZero() { + return false + } + return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now()) +} + +// Valid reports whether t is non-nil, has an AccessToken, and is not expired. +func (t *Token) Valid() bool { + return t != nil && t.AccessToken != "" && !t.expired() +} + +// tokenFromInternal maps an *internal.Token struct into +// a *Token struct. +func tokenFromInternal(t *internal.Token) *Token { + if t == nil { + return nil + } + return &Token{ + AccessToken: t.AccessToken, + TokenType: t.TokenType, + RefreshToken: t.RefreshToken, + Expiry: t.Expiry, + raw: t.Raw, + } +} + +// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. +// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along +// with an error.. +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { + tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v) + if err != nil { + if rErr, ok := err.(*internal.RetrieveError); ok { + return nil, (*RetrieveError)(rErr) + } + return nil, err + } + return tokenFromInternal(tk), nil +} + +// RetrieveError is the error returned when the token endpoint returns a +// non-2XX HTTP status code. +type RetrieveError struct { + Response *http.Response + // Body is the body that was consumed by reading Response.Body. + // It may be truncated. + Body []byte +} + +func (r *RetrieveError) Error() string { + return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body) +} diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go new file mode 100644 index 0000000000..aa0d34f1e0 --- /dev/null +++ b/vendor/golang.org/x/oauth2/transport.go @@ -0,0 +1,144 @@ +// Copyright 2014 The Go 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 oauth2 + +import ( + "errors" + "io" + "net/http" + "sync" +) + +// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests, +// wrapping a base RoundTripper and adding an Authorization header +// with a token from the supplied Sources. +// +// Transport is a low-level mechanism. Most code will use the +// higher-level Config.Client method instead. +type Transport struct { + // Source supplies the token to add to outgoing requests' + // Authorization headers. + Source TokenSource + + // Base is the base RoundTripper used to make HTTP requests. + // If nil, http.DefaultTransport is used. + Base http.RoundTripper + + mu sync.Mutex // guards modReq + modReq map[*http.Request]*http.Request // original -> modified +} + +// RoundTrip authorizes and authenticates the request with an +// access token from Transport's Source. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + reqBodyClosed := false + if req.Body != nil { + defer func() { + if !reqBodyClosed { + req.Body.Close() + } + }() + } + + if t.Source == nil { + return nil, errors.New("oauth2: Transport's Source is nil") + } + token, err := t.Source.Token() + if err != nil { + return nil, err + } + + req2 := cloneRequest(req) // per RoundTripper contract + token.SetAuthHeader(req2) + t.setModReq(req, req2) + res, err := t.base().RoundTrip(req2) + + // req.Body is assumed to have been closed by the base RoundTripper. + reqBodyClosed = true + + if err != nil { + t.setModReq(req, nil) + return nil, err + } + res.Body = &onEOFReader{ + rc: res.Body, + fn: func() { t.setModReq(req, nil) }, + } + return res, nil +} + +// CancelRequest cancels an in-flight request by closing its connection. +func (t *Transport) CancelRequest(req *http.Request) { + type canceler interface { + CancelRequest(*http.Request) + } + if cr, ok := t.base().(canceler); ok { + t.mu.Lock() + modReq := t.modReq[req] + delete(t.modReq, req) + t.mu.Unlock() + cr.CancelRequest(modReq) + } +} + +func (t *Transport) base() http.RoundTripper { + if t.Base != nil { + return t.Base + } + return http.DefaultTransport +} + +func (t *Transport) setModReq(orig, mod *http.Request) { + t.mu.Lock() + defer t.mu.Unlock() + if t.modReq == nil { + t.modReq = make(map[*http.Request]*http.Request) + } + if mod == nil { + delete(t.modReq, orig) + } else { + t.modReq[orig] = mod + } +} + +// cloneRequest returns a clone of the provided *http.Request. +// The clone is a shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { + // shallow copy of the struct + r2 := new(http.Request) + *r2 = *r + // deep copy of the Header + r2.Header = make(http.Header, len(r.Header)) + for k, s := range r.Header { + r2.Header[k] = append([]string(nil), s...) + } + return r2 +} + +type onEOFReader struct { + rc io.ReadCloser + fn func() +} + +func (r *onEOFReader) Read(p []byte) (n int, err error) { + n, err = r.rc.Read(p) + if err == io.EOF { + r.runFunc() + } + return +} + +func (r *onEOFReader) Close() error { + err := r.rc.Close() + r.runFunc() + return err +} + +func (r *onEOFReader) runFunc() { + if fn := r.fn; fn != nil { + fn() + r.fn = nil + } +} diff --git a/vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp.go b/vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp.go new file mode 100644 index 0000000000..193d0c727b --- /dev/null +++ b/vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp.go @@ -0,0 +1,364 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package gcp + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "os/exec" + "strings" + "sync" + "time" + + "github.com/golang/glog" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + "k8s.io/apimachinery/pkg/util/net" + "k8s.io/apimachinery/pkg/util/yaml" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/util/jsonpath" +) + +func init() { + if err := restclient.RegisterAuthProviderPlugin("gcp", newGCPAuthProvider); err != nil { + glog.Fatalf("Failed to register gcp auth plugin: %v", err) + } +} + +var ( + // Stubbable for testing + execCommand = exec.Command + + // defaultScopes: + // - cloud-platform is the base scope to authenticate to GCP. + // - userinfo.email is used to authenticate to GKE APIs with gserviceaccount + // email instead of numeric uniqueID. + defaultScopes = []string{ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email"} +) + +// gcpAuthProvider is an auth provider plugin that uses GCP credentials to provide +// tokens for kubectl to authenticate itself to the apiserver. A sample json config +// is provided below with all recognized options described. +// +// { +// 'auth-provider': { +// # Required +// "name": "gcp", +// +// 'config': { +// # Authentication options +// # These options are used while getting a token. +// +// # comma-separated list of GCP API scopes. default value of this field +// # is "https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email". +// # to override the API scopes, specify this field explicitly. +// "scopes": "https://www.googleapis.com/auth/cloud-platform" +// +// # Caching options +// +// # Raw string data representing cached access token. +// "access-token": "ya29.CjWdA4GiBPTt", +// # RFC3339Nano expiration timestamp for cached access token. +// "expiry": "2016-10-31 22:31:9.123", +// +// # Command execution options +// # These options direct the plugin to execute a specified command and parse +// # token and expiry time from the output of the command. +// +// # Command to execute for access token. Command output will be parsed as JSON. +// # If "cmd-args" is not present, this value will be split on whitespace, with +// # the first element interpreted as the command, remaining elements as args. +// "cmd-path": "/usr/bin/gcloud", +// +// # Arguments to pass to command to execute for access token. +// "cmd-args": "config config-helper --output=json" +// +// # JSONPath to the string field that represents the access token in +// # command output. If omitted, defaults to "{.access_token}". +// "token-key": "{.credential.access_token}", +// +// # JSONPath to the string field that represents expiration timestamp +// # of the access token in the command output. If omitted, defaults to +// # "{.token_expiry}" +// "expiry-key": ""{.credential.token_expiry}", +// +// # golang reference time in the format that the expiration timestamp uses. +// # If omitted, defaults to time.RFC3339Nano +// "time-fmt": "2006-01-02 15:04:05.999999999" +// } +// } +// } +// +type gcpAuthProvider struct { + tokenSource oauth2.TokenSource + persister restclient.AuthProviderConfigPersister +} + +func newGCPAuthProvider(_ string, gcpConfig map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) { + ts, err := tokenSource(isCmdTokenSource(gcpConfig), gcpConfig) + if err != nil { + return nil, err + } + cts, err := newCachedTokenSource(gcpConfig["access-token"], gcpConfig["expiry"], persister, ts, gcpConfig) + if err != nil { + return nil, err + } + return &gcpAuthProvider{cts, persister}, nil +} + +func isCmdTokenSource(gcpConfig map[string]string) bool { + _, ok := gcpConfig["cmd-path"] + return ok +} + +func tokenSource(isCmd bool, gcpConfig map[string]string) (oauth2.TokenSource, error) { + // Command-based token source + if isCmd { + cmd := gcpConfig["cmd-path"] + if len(cmd) == 0 { + return nil, fmt.Errorf("missing access token cmd") + } + if gcpConfig["scopes"] != "" { + return nil, fmt.Errorf("scopes can only be used when kubectl is using a gcp service account key") + } + var args []string + if cmdArgs, ok := gcpConfig["cmd-args"]; ok { + args = strings.Fields(cmdArgs) + } else { + fields := strings.Fields(cmd) + cmd = fields[0] + args = fields[1:] + } + return newCmdTokenSource(cmd, args, gcpConfig["token-key"], gcpConfig["expiry-key"], gcpConfig["time-fmt"]), nil + } + + // Google Application Credentials-based token source + scopes := parseScopes(gcpConfig) + ts, err := google.DefaultTokenSource(context.Background(), scopes...) + if err != nil { + return nil, fmt.Errorf("cannot construct google default token source: %v", err) + } + return ts, nil +} + +// parseScopes constructs a list of scopes that should be included in token source +// from the config map. +func parseScopes(gcpConfig map[string]string) []string { + scopes, ok := gcpConfig["scopes"] + if !ok { + return defaultScopes + } + if scopes == "" { + return []string{} + } + return strings.Split(gcpConfig["scopes"], ",") +} + +func (g *gcpAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper { + return &conditionalTransport{&oauth2.Transport{Source: g.tokenSource, Base: rt}, g.persister} +} + +func (g *gcpAuthProvider) Login() error { return nil } + +type cachedTokenSource struct { + lk sync.Mutex + source oauth2.TokenSource + accessToken string + expiry time.Time + persister restclient.AuthProviderConfigPersister + cache map[string]string +} + +func newCachedTokenSource(accessToken, expiry string, persister restclient.AuthProviderConfigPersister, ts oauth2.TokenSource, cache map[string]string) (*cachedTokenSource, error) { + var expiryTime time.Time + if parsedTime, err := time.Parse(time.RFC3339Nano, expiry); err == nil { + expiryTime = parsedTime + } + if cache == nil { + cache = make(map[string]string) + } + return &cachedTokenSource{ + source: ts, + accessToken: accessToken, + expiry: expiryTime, + persister: persister, + cache: cache, + }, nil +} + +func (t *cachedTokenSource) Token() (*oauth2.Token, error) { + tok := t.cachedToken() + if tok.Valid() && !tok.Expiry.IsZero() { + return tok, nil + } + tok, err := t.source.Token() + if err != nil { + return nil, err + } + cache := t.update(tok) + if t.persister != nil { + if err := t.persister.Persist(cache); err != nil { + glog.V(4).Infof("Failed to persist token: %v", err) + } + } + return tok, nil +} + +func (t *cachedTokenSource) cachedToken() *oauth2.Token { + t.lk.Lock() + defer t.lk.Unlock() + return &oauth2.Token{ + AccessToken: t.accessToken, + TokenType: "Bearer", + Expiry: t.expiry, + } +} + +func (t *cachedTokenSource) update(tok *oauth2.Token) map[string]string { + t.lk.Lock() + defer t.lk.Unlock() + t.accessToken = tok.AccessToken + t.expiry = tok.Expiry + ret := map[string]string{} + for k, v := range t.cache { + ret[k] = v + } + ret["access-token"] = t.accessToken + ret["expiry"] = t.expiry.Format(time.RFC3339Nano) + return ret +} + +type commandTokenSource struct { + cmd string + args []string + tokenKey string + expiryKey string + timeFmt string +} + +func newCmdTokenSource(cmd string, args []string, tokenKey, expiryKey, timeFmt string) *commandTokenSource { + if len(timeFmt) == 0 { + timeFmt = time.RFC3339Nano + } + if len(tokenKey) == 0 { + tokenKey = "{.access_token}" + } + if len(expiryKey) == 0 { + expiryKey = "{.token_expiry}" + } + return &commandTokenSource{ + cmd: cmd, + args: args, + tokenKey: tokenKey, + expiryKey: expiryKey, + timeFmt: timeFmt, + } +} + +func (c *commandTokenSource) Token() (*oauth2.Token, error) { + fullCmd := strings.Join(append([]string{c.cmd}, c.args...), " ") + cmd := execCommand(c.cmd, c.args...) + var stderr bytes.Buffer + cmd.Stderr = &stderr + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("error executing access token command %q: err=%v output=%s stderr=%s", fullCmd, err, output, string(stderr.Bytes())) + } + token, err := c.parseTokenCmdOutput(output) + if err != nil { + return nil, fmt.Errorf("error parsing output for access token command %q: %v", fullCmd, err) + } + return token, nil +} + +func (c *commandTokenSource) parseTokenCmdOutput(output []byte) (*oauth2.Token, error) { + output, err := yaml.ToJSON(output) + if err != nil { + return nil, err + } + var data interface{} + if err := json.Unmarshal(output, &data); err != nil { + return nil, err + } + + accessToken, err := parseJSONPath(data, "token-key", c.tokenKey) + if err != nil { + return nil, fmt.Errorf("error parsing token-key %q from %q: %v", c.tokenKey, string(output), err) + } + expiryStr, err := parseJSONPath(data, "expiry-key", c.expiryKey) + if err != nil { + return nil, fmt.Errorf("error parsing expiry-key %q from %q: %v", c.expiryKey, string(output), err) + } + var expiry time.Time + if t, err := time.Parse(c.timeFmt, expiryStr); err != nil { + glog.V(4).Infof("Failed to parse token expiry from %s (fmt=%s): %v", expiryStr, c.timeFmt, err) + } else { + expiry = t + } + + return &oauth2.Token{ + AccessToken: accessToken, + TokenType: "Bearer", + Expiry: expiry, + }, nil +} + +func parseJSONPath(input interface{}, name, template string) (string, error) { + j := jsonpath.New(name) + buf := new(bytes.Buffer) + if err := j.Parse(template); err != nil { + return "", err + } + if err := j.Execute(buf, input); err != nil { + return "", err + } + return buf.String(), nil +} + +type conditionalTransport struct { + oauthTransport *oauth2.Transport + persister restclient.AuthProviderConfigPersister +} + +var _ net.RoundTripperWrapper = &conditionalTransport{} + +func (t *conditionalTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if len(req.Header.Get("Authorization")) != 0 { + return t.oauthTransport.Base.RoundTrip(req) + } + + res, err := t.oauthTransport.RoundTrip(req) + + if err != nil { + return nil, err + } + + if res.StatusCode == 401 { + glog.V(4).Infof("The credentials that were supplied are invalid for the target cluster") + emptyCache := make(map[string]string) + t.persister.Persist(emptyCache) + } + + return res, nil +} + +func (t *conditionalTransport) WrappedRoundTripper() http.RoundTripper { return t.oauthTransport.Base } diff --git a/vendor/k8s.io/client-go/third_party/forked/golang/template/exec.go b/vendor/k8s.io/client-go/third_party/forked/golang/template/exec.go new file mode 100644 index 0000000000..739fd3509c --- /dev/null +++ b/vendor/k8s.io/client-go/third_party/forked/golang/template/exec.go @@ -0,0 +1,94 @@ +//This package is copied from Go library text/template. +//The original private functions indirect and printableValue +//are exported as public functions. +package template + +import ( + "fmt" + "reflect" +) + +var Indirect = indirect +var PrintableValue = printableValue + +var ( + errorType = reflect.TypeOf((*error)(nil)).Elem() + fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() +) + +// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. +// We indirect through pointers and empty interfaces (only) because +// non-empty interfaces have methods we might need. +func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { + for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { + if v.IsNil() { + return v, true + } + if v.Kind() == reflect.Interface && v.NumMethod() > 0 { + break + } + } + return v, false +} + +// printableValue returns the, possibly indirected, interface value inside v that +// is best for a call to formatted printer. +func printableValue(v reflect.Value) (interface{}, bool) { + if v.Kind() == reflect.Ptr { + v, _ = indirect(v) // fmt.Fprint handles nil. + } + if !v.IsValid() { + return "", true + } + + if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { + if v.CanAddr() && (reflect.PtrTo(v.Type()).Implements(errorType) || reflect.PtrTo(v.Type()).Implements(fmtStringerType)) { + v = v.Addr() + } else { + switch v.Kind() { + case reflect.Chan, reflect.Func: + return nil, false + } + } + } + return v.Interface(), true +} + +// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. +func canBeNil(typ reflect.Type) bool { + switch typ.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return true + } + return false +} + +// isTrue reports whether the value is 'true', in the sense of not the zero of its type, +// and whether the value has a meaningful truth value. +func isTrue(val reflect.Value) (truth, ok bool) { + if !val.IsValid() { + // Something like var x interface{}, never set. It's a form of nil. + return false, true + } + switch val.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + truth = val.Len() > 0 + case reflect.Bool: + truth = val.Bool() + case reflect.Complex64, reflect.Complex128: + truth = val.Complex() != 0 + case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface: + truth = !val.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + truth = val.Int() != 0 + case reflect.Float32, reflect.Float64: + truth = val.Float() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + truth = val.Uint() != 0 + case reflect.Struct: + truth = true // Struct values are always true. + default: + return + } + return truth, true +} diff --git a/vendor/k8s.io/client-go/third_party/forked/golang/template/funcs.go b/vendor/k8s.io/client-go/third_party/forked/golang/template/funcs.go new file mode 100644 index 0000000000..27a008b0a7 --- /dev/null +++ b/vendor/k8s.io/client-go/third_party/forked/golang/template/funcs.go @@ -0,0 +1,599 @@ +//This package is copied from Go library text/template. +//The original private functions eq, ge, gt, le, lt, and ne +//are exported as public functions. +package template + +import ( + "bytes" + "errors" + "fmt" + "io" + "net/url" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +var Equal = eq +var GreaterEqual = ge +var Greater = gt +var LessEqual = le +var Less = lt +var NotEqual = ne + +// FuncMap is the type of the map defining the mapping from names to functions. +// Each function must have either a single return value, or two return values of +// which the second has type error. In that case, if the second (error) +// return value evaluates to non-nil during execution, execution terminates and +// Execute returns that error. +type FuncMap map[string]interface{} + +var builtins = FuncMap{ + "and": and, + "call": call, + "html": HTMLEscaper, + "index": index, + "js": JSEscaper, + "len": length, + "not": not, + "or": or, + "print": fmt.Sprint, + "printf": fmt.Sprintf, + "println": fmt.Sprintln, + "urlquery": URLQueryEscaper, + + // Comparisons + "eq": eq, // == + "ge": ge, // >= + "gt": gt, // > + "le": le, // <= + "lt": lt, // < + "ne": ne, // != +} + +var builtinFuncs = createValueFuncs(builtins) + +// createValueFuncs turns a FuncMap into a map[string]reflect.Value +func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { + m := make(map[string]reflect.Value) + addValueFuncs(m, funcMap) + return m +} + +// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. +func addValueFuncs(out map[string]reflect.Value, in FuncMap) { + for name, fn := range in { + v := reflect.ValueOf(fn) + if v.Kind() != reflect.Func { + panic("value for " + name + " not a function") + } + if !goodFunc(v.Type()) { + panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut())) + } + out[name] = v + } +} + +// AddFuncs adds to values the functions in funcs. It does no checking of the input - +// call addValueFuncs first. +func addFuncs(out, in FuncMap) { + for name, fn := range in { + out[name] = fn + } +} + +// goodFunc checks that the function or method has the right result signature. +func goodFunc(typ reflect.Type) bool { + // We allow functions with 1 result or 2 results where the second is an error. + switch { + case typ.NumOut() == 1: + return true + case typ.NumOut() == 2 && typ.Out(1) == errorType: + return true + } + return false +} + +// findFunction looks for a function in the template, and global map. +func findFunction(name string) (reflect.Value, bool) { + if fn := builtinFuncs[name]; fn.IsValid() { + return fn, true + } + return reflect.Value{}, false +} + +// Indexing. + +// index returns the result of indexing its first argument by the following +// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each +// indexed item must be a map, slice, or array. +func index(item interface{}, indices ...interface{}) (interface{}, error) { + v := reflect.ValueOf(item) + for _, i := range indices { + index := reflect.ValueOf(i) + var isNil bool + if v, isNil = indirect(v); isNil { + return nil, fmt.Errorf("index of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.String: + var x int64 + switch index.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x = index.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + x = int64(index.Uint()) + default: + return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) + } + if x < 0 || x >= int64(v.Len()) { + return nil, fmt.Errorf("index out of range: %d", x) + } + v = v.Index(int(x)) + case reflect.Map: + if !index.IsValid() { + index = reflect.Zero(v.Type().Key()) + } + if !index.Type().AssignableTo(v.Type().Key()) { + return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) + } + if x := v.MapIndex(index); x.IsValid() { + v = x + } else { + v = reflect.Zero(v.Type().Elem()) + } + default: + return nil, fmt.Errorf("can't index item of type %s", v.Type()) + } + } + return v.Interface(), nil +} + +// Length + +// length returns the length of the item, with an error if it has no defined length. +func length(item interface{}) (int, error) { + v, isNil := indirect(reflect.ValueOf(item)) + if isNil { + return 0, fmt.Errorf("len of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return v.Len(), nil + } + return 0, fmt.Errorf("len of type %s", v.Type()) +} + +// Function invocation + +// call returns the result of evaluating the first argument as a function. +// The function must return 1 result, or 2 results, the second of which is an error. +func call(fn interface{}, args ...interface{}) (interface{}, error) { + v := reflect.ValueOf(fn) + typ := v.Type() + if typ.Kind() != reflect.Func { + return nil, fmt.Errorf("non-function of type %s", typ) + } + if !goodFunc(typ) { + return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) + } + numIn := typ.NumIn() + var dddType reflect.Type + if typ.IsVariadic() { + if len(args) < numIn-1 { + return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) + } + dddType = typ.In(numIn - 1).Elem() + } else { + if len(args) != numIn { + return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) + } + } + argv := make([]reflect.Value, len(args)) + for i, arg := range args { + value := reflect.ValueOf(arg) + // Compute the expected type. Clumsy because of variadics. + var argType reflect.Type + if !typ.IsVariadic() || i < numIn-1 { + argType = typ.In(i) + } else { + argType = dddType + } + if !value.IsValid() && canBeNil(argType) { + value = reflect.Zero(argType) + } + if !value.Type().AssignableTo(argType) { + return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType) + } + argv[i] = value + } + result := v.Call(argv) + if len(result) == 2 && !result[1].IsNil() { + return result[0].Interface(), result[1].Interface().(error) + } + return result[0].Interface(), nil +} + +// Boolean logic. + +func truth(a interface{}) bool { + t, _ := isTrue(reflect.ValueOf(a)) + return t +} + +// and computes the Boolean AND of its arguments, returning +// the first false argument it encounters, or the last argument. +func and(arg0 interface{}, args ...interface{}) interface{} { + if !truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if !truth(arg0) { + break + } + } + return arg0 +} + +// or computes the Boolean OR of its arguments, returning +// the first true argument it encounters, or the last argument. +func or(arg0 interface{}, args ...interface{}) interface{} { + if truth(arg0) { + return arg0 + } + for i := range args { + arg0 = args[i] + if truth(arg0) { + break + } + } + return arg0 +} + +// not returns the Boolean negation of its argument. +func not(arg interface{}) (truth bool) { + truth, _ = isTrue(reflect.ValueOf(arg)) + return !truth +} + +// Comparison. + +// TODO: Perhaps allow comparison between signed and unsigned integers. + +var ( + errBadComparisonType = errors.New("invalid type for comparison") + errBadComparison = errors.New("incompatible types for comparison") + errNoComparison = errors.New("missing argument for comparison") +) + +type kind int + +const ( + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + integerKind + stringKind + uintKind +) + +func basicKind(v reflect.Value) (kind, error) { + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType +} + +// eq evaluates the comparison a == b || a == c || ... +func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + if len(arg2) == 0 { + return false, errNoComparison + } + for _, arg := range arg2 { + v2 := reflect.ValueOf(arg) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } + } + if truth { + return true, nil + } + } + return false, nil +} + +// ne evaluates the comparison a != b. +func ne(arg1, arg2 interface{}) (bool, error) { + // != is the inverse of ==. + equal, err := eq(arg1, arg2) + return !equal, err +} + +// lt evaluates the comparison a < b. +func lt(arg1, arg2 interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + v2 := reflect.ValueOf(arg2) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() + case k1 == uintKind && k2 == intKind: + truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) + default: + return false, errBadComparison + } + } else { + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } + } + return truth, nil +} + +// le evaluates the comparison <= b. +func le(arg1, arg2 interface{}) (bool, error) { + // <= is < or ==. + lessThan, err := lt(arg1, arg2) + if lessThan || err != nil { + return lessThan, err + } + return eq(arg1, arg2) +} + +// gt evaluates the comparison a > b. +func gt(arg1, arg2 interface{}) (bool, error) { + // > is the inverse of <=. + lessOrEqual, err := le(arg1, arg2) + if err != nil { + return false, err + } + return !lessOrEqual, nil +} + +// ge evaluates the comparison a >= b. +func ge(arg1, arg2 interface{}) (bool, error) { + // >= is the inverse of <. + lessThan, err := lt(arg1, arg2) + if err != nil { + return false, err + } + return !lessThan, nil +} + +// HTML escaping. + +var ( + htmlQuot = []byte(""") // shorter than """ + htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 + htmlAmp = []byte("&") + htmlLt = []byte("<") + htmlGt = []byte(">") +) + +// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. +func HTMLEscape(w io.Writer, b []byte) { + last := 0 + for i, c := range b { + var html []byte + switch c { + case '"': + html = htmlQuot + case '\'': + html = htmlApos + case '&': + html = htmlAmp + case '<': + html = htmlLt + case '>': + html = htmlGt + default: + continue + } + w.Write(b[last:i]) + w.Write(html) + last = i + 1 + } + w.Write(b[last:]) +} + +// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. +func HTMLEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexAny(s, `'"&<>`) < 0 { + return s + } + var b bytes.Buffer + HTMLEscape(&b, []byte(s)) + return b.String() +} + +// HTMLEscaper returns the escaped HTML equivalent of the textual +// representation of its arguments. +func HTMLEscaper(args ...interface{}) string { + return HTMLEscapeString(evalArgs(args)) +} + +// JavaScript escaping. + +var ( + jsLowUni = []byte(`\u00`) + hex = []byte("0123456789ABCDEF") + + jsBackslash = []byte(`\\`) + jsApos = []byte(`\'`) + jsQuot = []byte(`\"`) + jsLt = []byte(`\x3C`) + jsGt = []byte(`\x3E`) +) + +// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. +func JSEscape(w io.Writer, b []byte) { + last := 0 + for i := 0; i < len(b); i++ { + c := b[i] + + if !jsIsSpecial(rune(c)) { + // fast path: nothing to do + continue + } + w.Write(b[last:i]) + + if c < utf8.RuneSelf { + // Quotes, slashes and angle brackets get quoted. + // Control characters get written as \u00XX. + switch c { + case '\\': + w.Write(jsBackslash) + case '\'': + w.Write(jsApos) + case '"': + w.Write(jsQuot) + case '<': + w.Write(jsLt) + case '>': + w.Write(jsGt) + default: + w.Write(jsLowUni) + t, b := c>>4, c&0x0f + w.Write(hex[t : t+1]) + w.Write(hex[b : b+1]) + } + } else { + // Unicode rune. + r, size := utf8.DecodeRune(b[i:]) + if unicode.IsPrint(r) { + w.Write(b[i : i+size]) + } else { + fmt.Fprintf(w, "\\u%04X", r) + } + i += size - 1 + } + last = i + 1 + } + w.Write(b[last:]) +} + +// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. +func JSEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexFunc(s, jsIsSpecial) < 0 { + return s + } + var b bytes.Buffer + JSEscape(&b, []byte(s)) + return b.String() +} + +func jsIsSpecial(r rune) bool { + switch r { + case '\\', '\'', '"', '<', '>': + return true + } + return r < ' ' || utf8.RuneSelf <= r +} + +// JSEscaper returns the escaped JavaScript equivalent of the textual +// representation of its arguments. +func JSEscaper(args ...interface{}) string { + return JSEscapeString(evalArgs(args)) +} + +// URLQueryEscaper returns the escaped value of the textual representation of +// its arguments in a form suitable for embedding in a URL query. +func URLQueryEscaper(args ...interface{}) string { + return url.QueryEscape(evalArgs(args)) +} + +// evalArgs formats the list of arguments into a string. It is therefore equivalent to +// fmt.Sprint(args...) +// except that each argument is indirected (if a pointer), as required, +// using the same rules as the default string evaluation during template +// execution. +func evalArgs(args []interface{}) string { + ok := false + var s string + // Fast path for simple common case. + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + for i, arg := range args { + a, ok := printableValue(reflect.ValueOf(arg)) + if ok { + args[i] = a + } // else left fmt do its thing + } + s = fmt.Sprint(args...) + } + return s +} diff --git a/vendor/k8s.io/client-go/util/jsonpath/doc.go b/vendor/k8s.io/client-go/util/jsonpath/doc.go new file mode 100644 index 0000000000..0effb15c41 --- /dev/null +++ b/vendor/k8s.io/client-go/util/jsonpath/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2015 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 jsonpath is a template engine using jsonpath syntax, +// which can be seen at http://goessner.net/articles/JsonPath/. +// In addition, it has {range} {end} function to iterate list and slice. +package jsonpath // import "k8s.io/client-go/util/jsonpath" diff --git a/vendor/k8s.io/client-go/util/jsonpath/jsonpath.go b/vendor/k8s.io/client-go/util/jsonpath/jsonpath.go new file mode 100644 index 0000000000..6633ca0d65 --- /dev/null +++ b/vendor/k8s.io/client-go/util/jsonpath/jsonpath.go @@ -0,0 +1,517 @@ +/* +Copyright 2015 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 jsonpath + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strings" + + "k8s.io/client-go/third_party/forked/golang/template" +) + +type JSONPath struct { + name string + parser *Parser + stack [][]reflect.Value // push and pop values in different scopes + cur []reflect.Value // current scope values + beginRange int + inRange int + endRange int + + allowMissingKeys bool +} + +// New creates a new JSONPath with the given name. +func New(name string) *JSONPath { + return &JSONPath{ + name: name, + beginRange: 0, + inRange: 0, + endRange: 0, + } +} + +// AllowMissingKeys allows a caller to specify whether they want an error if a field or map key +// cannot be located, or simply an empty result. The receiver is returned for chaining. +func (j *JSONPath) AllowMissingKeys(allow bool) *JSONPath { + j.allowMissingKeys = allow + return j +} + +// Parse parses the given template and returns an error. +func (j *JSONPath) Parse(text string) error { + var err error + j.parser, err = Parse(j.name, text) + return err +} + +// Execute bounds data into template and writes the result. +func (j *JSONPath) Execute(wr io.Writer, data interface{}) error { + fullResults, err := j.FindResults(data) + if err != nil { + return err + } + for ix := range fullResults { + if err := j.PrintResults(wr, fullResults[ix]); err != nil { + return err + } + } + return nil +} + +func (j *JSONPath) FindResults(data interface{}) ([][]reflect.Value, error) { + if j.parser == nil { + return nil, fmt.Errorf("%s is an incomplete jsonpath template", j.name) + } + + j.cur = []reflect.Value{reflect.ValueOf(data)} + nodes := j.parser.Root.Nodes + fullResult := [][]reflect.Value{} + for i := 0; i < len(nodes); i++ { + node := nodes[i] + results, err := j.walk(j.cur, node) + if err != nil { + return nil, err + } + + // encounter an end node, break the current block + if j.endRange > 0 && j.endRange <= j.inRange { + j.endRange -= 1 + break + } + // encounter a range node, start a range loop + if j.beginRange > 0 { + j.beginRange -= 1 + j.inRange += 1 + for k, value := range results { + j.parser.Root.Nodes = nodes[i+1:] + if k == len(results)-1 { + j.inRange -= 1 + } + nextResults, err := j.FindResults(value.Interface()) + if err != nil { + return nil, err + } + fullResult = append(fullResult, nextResults...) + } + break + } + fullResult = append(fullResult, results) + } + return fullResult, nil +} + +// PrintResults writes the results into writer +func (j *JSONPath) PrintResults(wr io.Writer, results []reflect.Value) error { + for i, r := range results { + text, err := j.evalToText(r) + if err != nil { + return err + } + if i != len(results)-1 { + text = append(text, ' ') + } + if _, err = wr.Write(text); err != nil { + return err + } + } + return nil +} + +// walk visits tree rooted at the given node in DFS order +func (j *JSONPath) walk(value []reflect.Value, node Node) ([]reflect.Value, error) { + switch node := node.(type) { + case *ListNode: + return j.evalList(value, node) + case *TextNode: + return []reflect.Value{reflect.ValueOf(node.Text)}, nil + case *FieldNode: + return j.evalField(value, node) + case *ArrayNode: + return j.evalArray(value, node) + case *FilterNode: + return j.evalFilter(value, node) + case *IntNode: + return j.evalInt(value, node) + case *BoolNode: + return j.evalBool(value, node) + case *FloatNode: + return j.evalFloat(value, node) + case *WildcardNode: + return j.evalWildcard(value, node) + case *RecursiveNode: + return j.evalRecursive(value, node) + case *UnionNode: + return j.evalUnion(value, node) + case *IdentifierNode: + return j.evalIdentifier(value, node) + default: + return value, fmt.Errorf("unexpected Node %v", node) + } +} + +// evalInt evaluates IntNode +func (j *JSONPath) evalInt(input []reflect.Value, node *IntNode) ([]reflect.Value, error) { + result := make([]reflect.Value, len(input)) + for i := range input { + result[i] = reflect.ValueOf(node.Value) + } + return result, nil +} + +// evalFloat evaluates FloatNode +func (j *JSONPath) evalFloat(input []reflect.Value, node *FloatNode) ([]reflect.Value, error) { + result := make([]reflect.Value, len(input)) + for i := range input { + result[i] = reflect.ValueOf(node.Value) + } + return result, nil +} + +// evalBool evaluates BoolNode +func (j *JSONPath) evalBool(input []reflect.Value, node *BoolNode) ([]reflect.Value, error) { + result := make([]reflect.Value, len(input)) + for i := range input { + result[i] = reflect.ValueOf(node.Value) + } + return result, nil +} + +// evalList evaluates ListNode +func (j *JSONPath) evalList(value []reflect.Value, node *ListNode) ([]reflect.Value, error) { + var err error + curValue := value + for _, node := range node.Nodes { + curValue, err = j.walk(curValue, node) + if err != nil { + return curValue, err + } + } + return curValue, nil +} + +// evalIdentifier evaluates IdentifierNode +func (j *JSONPath) evalIdentifier(input []reflect.Value, node *IdentifierNode) ([]reflect.Value, error) { + results := []reflect.Value{} + switch node.Name { + case "range": + j.stack = append(j.stack, j.cur) + j.beginRange += 1 + results = input + case "end": + if j.endRange < j.inRange { // inside a loop, break the current block + j.endRange += 1 + break + } + // the loop is about to end, pop value and continue the following execution + if len(j.stack) > 0 { + j.cur, j.stack = j.stack[len(j.stack)-1], j.stack[:len(j.stack)-1] + } else { + return results, fmt.Errorf("not in range, nothing to end") + } + default: + return input, fmt.Errorf("unrecognized identifier %v", node.Name) + } + return results, nil +} + +// evalArray evaluates ArrayNode +func (j *JSONPath) evalArray(input []reflect.Value, node *ArrayNode) ([]reflect.Value, error) { + result := []reflect.Value{} + for _, value := range input { + + value, isNil := template.Indirect(value) + if isNil { + continue + } + if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { + return input, fmt.Errorf("%v is not array or slice", value.Type()) + } + params := node.Params + if !params[0].Known { + params[0].Value = 0 + } + if params[0].Value < 0 { + params[0].Value += value.Len() + } + if !params[1].Known { + params[1].Value = value.Len() + } + + if params[1].Value < 0 { + params[1].Value += value.Len() + } + + sliceLength := value.Len() + if params[1].Value != params[0].Value { // if you're requesting zero elements, allow it through. + if params[0].Value >= sliceLength || params[0].Value < 0 { + return input, fmt.Errorf("array index out of bounds: index %d, length %d", params[0].Value, sliceLength) + } + if params[1].Value > sliceLength || params[1].Value < 0 { + return input, fmt.Errorf("array index out of bounds: index %d, length %d", params[1].Value-1, sliceLength) + } + } + + if !params[2].Known { + value = value.Slice(params[0].Value, params[1].Value) + } else { + value = value.Slice3(params[0].Value, params[1].Value, params[2].Value) + } + for i := 0; i < value.Len(); i++ { + result = append(result, value.Index(i)) + } + } + return result, nil +} + +// evalUnion evaluates UnionNode +func (j *JSONPath) evalUnion(input []reflect.Value, node *UnionNode) ([]reflect.Value, error) { + result := []reflect.Value{} + for _, listNode := range node.Nodes { + temp, err := j.evalList(input, listNode) + if err != nil { + return input, err + } + result = append(result, temp...) + } + return result, nil +} + +func (j *JSONPath) findFieldInValue(value *reflect.Value, node *FieldNode) (reflect.Value, error) { + t := value.Type() + var inlineValue *reflect.Value + for ix := 0; ix < t.NumField(); ix++ { + f := t.Field(ix) + jsonTag := f.Tag.Get("json") + parts := strings.Split(jsonTag, ",") + if len(parts) == 0 { + continue + } + if parts[0] == node.Value { + return value.Field(ix), nil + } + if len(parts[0]) == 0 { + val := value.Field(ix) + inlineValue = &val + } + } + if inlineValue != nil { + if inlineValue.Kind() == reflect.Struct { + // handle 'inline' + match, err := j.findFieldInValue(inlineValue, node) + if err != nil { + return reflect.Value{}, err + } + if match.IsValid() { + return match, nil + } + } + } + return value.FieldByName(node.Value), nil +} + +// evalField evaluates field of struct or key of map. +func (j *JSONPath) evalField(input []reflect.Value, node *FieldNode) ([]reflect.Value, error) { + results := []reflect.Value{} + // If there's no input, there's no output + if len(input) == 0 { + return results, nil + } + for _, value := range input { + var result reflect.Value + value, isNil := template.Indirect(value) + if isNil { + continue + } + + if value.Kind() == reflect.Struct { + var err error + if result, err = j.findFieldInValue(&value, node); err != nil { + return nil, err + } + } else if value.Kind() == reflect.Map { + mapKeyType := value.Type().Key() + nodeValue := reflect.ValueOf(node.Value) + // node value type must be convertible to map key type + if !nodeValue.Type().ConvertibleTo(mapKeyType) { + return results, fmt.Errorf("%s is not convertible to %s", nodeValue, mapKeyType) + } + result = value.MapIndex(nodeValue.Convert(mapKeyType)) + } + if result.IsValid() { + results = append(results, result) + } + } + if len(results) == 0 { + if j.allowMissingKeys { + return results, nil + } + return results, fmt.Errorf("%s is not found", node.Value) + } + return results, nil +} + +// evalWildcard extracts all contents of the given value +func (j *JSONPath) evalWildcard(input []reflect.Value, node *WildcardNode) ([]reflect.Value, error) { + results := []reflect.Value{} + for _, value := range input { + value, isNil := template.Indirect(value) + if isNil { + continue + } + + kind := value.Kind() + if kind == reflect.Struct { + for i := 0; i < value.NumField(); i++ { + results = append(results, value.Field(i)) + } + } else if kind == reflect.Map { + for _, key := range value.MapKeys() { + results = append(results, value.MapIndex(key)) + } + } else if kind == reflect.Array || kind == reflect.Slice || kind == reflect.String { + for i := 0; i < value.Len(); i++ { + results = append(results, value.Index(i)) + } + } + } + return results, nil +} + +// evalRecursive visits the given value recursively and pushes all of them to result +func (j *JSONPath) evalRecursive(input []reflect.Value, node *RecursiveNode) ([]reflect.Value, error) { + result := []reflect.Value{} + for _, value := range input { + results := []reflect.Value{} + value, isNil := template.Indirect(value) + if isNil { + continue + } + + kind := value.Kind() + if kind == reflect.Struct { + for i := 0; i < value.NumField(); i++ { + results = append(results, value.Field(i)) + } + } else if kind == reflect.Map { + for _, key := range value.MapKeys() { + results = append(results, value.MapIndex(key)) + } + } else if kind == reflect.Array || kind == reflect.Slice || kind == reflect.String { + for i := 0; i < value.Len(); i++ { + results = append(results, value.Index(i)) + } + } + if len(results) != 0 { + result = append(result, value) + output, err := j.evalRecursive(results, node) + if err != nil { + return result, err + } + result = append(result, output...) + } + } + return result, nil +} + +// evalFilter filters array according to FilterNode +func (j *JSONPath) evalFilter(input []reflect.Value, node *FilterNode) ([]reflect.Value, error) { + results := []reflect.Value{} + for _, value := range input { + value, _ = template.Indirect(value) + + if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { + return input, fmt.Errorf("%v is not array or slice and cannot be filtered", value) + } + for i := 0; i < value.Len(); i++ { + temp := []reflect.Value{value.Index(i)} + lefts, err := j.evalList(temp, node.Left) + + //case exists + if node.Operator == "exists" { + if len(lefts) > 0 { + results = append(results, value.Index(i)) + } + continue + } + + if err != nil { + return input, err + } + + var left, right interface{} + switch { + case len(lefts) == 0: + continue + case len(lefts) > 1: + return input, fmt.Errorf("can only compare one element at a time") + } + left = lefts[0].Interface() + + rights, err := j.evalList(temp, node.Right) + if err != nil { + return input, err + } + switch { + case len(rights) == 0: + continue + case len(rights) > 1: + return input, fmt.Errorf("can only compare one element at a time") + } + right = rights[0].Interface() + + pass := false + switch node.Operator { + case "<": + pass, err = template.Less(left, right) + case ">": + pass, err = template.Greater(left, right) + case "==": + pass, err = template.Equal(left, right) + case "!=": + pass, err = template.NotEqual(left, right) + case "<=": + pass, err = template.LessEqual(left, right) + case ">=": + pass, err = template.GreaterEqual(left, right) + default: + return results, fmt.Errorf("unrecognized filter operator %s", node.Operator) + } + if err != nil { + return results, err + } + if pass { + results = append(results, value.Index(i)) + } + } + } + return results, nil +} + +// evalToText translates reflect value to corresponding text +func (j *JSONPath) evalToText(v reflect.Value) ([]byte, error) { + iface, ok := template.PrintableValue(v) + if !ok { + return nil, fmt.Errorf("can't print type %s", v.Type()) + } + var buffer bytes.Buffer + fmt.Fprint(&buffer, iface) + return buffer.Bytes(), nil +} diff --git a/vendor/k8s.io/client-go/util/jsonpath/node.go b/vendor/k8s.io/client-go/util/jsonpath/node.go new file mode 100644 index 0000000000..bc763357cf --- /dev/null +++ b/vendor/k8s.io/client-go/util/jsonpath/node.go @@ -0,0 +1,255 @@ +/* +Copyright 2015 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 jsonpath + +import "fmt" + +// NodeType identifies the type of a parse tree node. +type NodeType int + +// Type returns itself and provides an easy default implementation +func (t NodeType) Type() NodeType { + return t +} + +func (t NodeType) String() string { + return NodeTypeName[t] +} + +const ( + NodeText NodeType = iota + NodeArray + NodeList + NodeField + NodeIdentifier + NodeFilter + NodeInt + NodeFloat + NodeWildcard + NodeRecursive + NodeUnion + NodeBool +) + +var NodeTypeName = map[NodeType]string{ + NodeText: "NodeText", + NodeArray: "NodeArray", + NodeList: "NodeList", + NodeField: "NodeField", + NodeIdentifier: "NodeIdentifier", + NodeFilter: "NodeFilter", + NodeInt: "NodeInt", + NodeFloat: "NodeFloat", + NodeWildcard: "NodeWildcard", + NodeRecursive: "NodeRecursive", + NodeUnion: "NodeUnion", + NodeBool: "NodeBool", +} + +type Node interface { + Type() NodeType + String() string +} + +// ListNode holds a sequence of nodes. +type ListNode struct { + NodeType + Nodes []Node // The element nodes in lexical order. +} + +func newList() *ListNode { + return &ListNode{NodeType: NodeList} +} + +func (l *ListNode) append(n Node) { + l.Nodes = append(l.Nodes, n) +} + +func (l *ListNode) String() string { + return fmt.Sprintf("%s", l.Type()) +} + +// TextNode holds plain text. +type TextNode struct { + NodeType + Text string // The text; may span newlines. +} + +func newText(text string) *TextNode { + return &TextNode{NodeType: NodeText, Text: text} +} + +func (t *TextNode) String() string { + return fmt.Sprintf("%s: %s", t.Type(), t.Text) +} + +// FieldNode holds field of struct +type FieldNode struct { + NodeType + Value string +} + +func newField(value string) *FieldNode { + return &FieldNode{NodeType: NodeField, Value: value} +} + +func (f *FieldNode) String() string { + return fmt.Sprintf("%s: %s", f.Type(), f.Value) +} + +// IdentifierNode holds an identifier +type IdentifierNode struct { + NodeType + Name string +} + +func newIdentifier(value string) *IdentifierNode { + return &IdentifierNode{ + NodeType: NodeIdentifier, + Name: value, + } +} + +func (f *IdentifierNode) String() string { + return fmt.Sprintf("%s: %s", f.Type(), f.Name) +} + +// ParamsEntry holds param information for ArrayNode +type ParamsEntry struct { + Value int + Known bool // whether the value is known when parse it +} + +// ArrayNode holds start, end, step information for array index selection +type ArrayNode struct { + NodeType + Params [3]ParamsEntry // start, end, step +} + +func newArray(params [3]ParamsEntry) *ArrayNode { + return &ArrayNode{ + NodeType: NodeArray, + Params: params, + } +} + +func (a *ArrayNode) String() string { + return fmt.Sprintf("%s: %v", a.Type(), a.Params) +} + +// FilterNode holds operand and operator information for filter +type FilterNode struct { + NodeType + Left *ListNode + Right *ListNode + Operator string +} + +func newFilter(left, right *ListNode, operator string) *FilterNode { + return &FilterNode{ + NodeType: NodeFilter, + Left: left, + Right: right, + Operator: operator, + } +} + +func (f *FilterNode) String() string { + return fmt.Sprintf("%s: %s %s %s", f.Type(), f.Left, f.Operator, f.Right) +} + +// IntNode holds integer value +type IntNode struct { + NodeType + Value int +} + +func newInt(num int) *IntNode { + return &IntNode{NodeType: NodeInt, Value: num} +} + +func (i *IntNode) String() string { + return fmt.Sprintf("%s: %d", i.Type(), i.Value) +} + +// FloatNode holds float value +type FloatNode struct { + NodeType + Value float64 +} + +func newFloat(num float64) *FloatNode { + return &FloatNode{NodeType: NodeFloat, Value: num} +} + +func (i *FloatNode) String() string { + return fmt.Sprintf("%s: %f", i.Type(), i.Value) +} + +// WildcardNode means a wildcard +type WildcardNode struct { + NodeType +} + +func newWildcard() *WildcardNode { + return &WildcardNode{NodeType: NodeWildcard} +} + +func (i *WildcardNode) String() string { + return fmt.Sprintf("%s", i.Type()) +} + +// RecursiveNode means a recursive descent operator +type RecursiveNode struct { + NodeType +} + +func newRecursive() *RecursiveNode { + return &RecursiveNode{NodeType: NodeRecursive} +} + +func (r *RecursiveNode) String() string { + return fmt.Sprintf("%s", r.Type()) +} + +// UnionNode is union of ListNode +type UnionNode struct { + NodeType + Nodes []*ListNode +} + +func newUnion(nodes []*ListNode) *UnionNode { + return &UnionNode{NodeType: NodeUnion, Nodes: nodes} +} + +func (u *UnionNode) String() string { + return fmt.Sprintf("%s", u.Type()) +} + +// BoolNode holds bool value +type BoolNode struct { + NodeType + Value bool +} + +func newBool(value bool) *BoolNode { + return &BoolNode{NodeType: NodeBool, Value: value} +} + +func (b *BoolNode) String() string { + return fmt.Sprintf("%s: %t", b.Type(), b.Value) +} diff --git a/vendor/k8s.io/client-go/util/jsonpath/parser.go b/vendor/k8s.io/client-go/util/jsonpath/parser.go new file mode 100644 index 0000000000..ef0f9213a4 --- /dev/null +++ b/vendor/k8s.io/client-go/util/jsonpath/parser.go @@ -0,0 +1,525 @@ +/* +Copyright 2015 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 jsonpath + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +const eof = -1 + +const ( + leftDelim = "{" + rightDelim = "}" +) + +type Parser struct { + Name string + Root *ListNode + input string + cur *ListNode + pos int + start int + width int +} + +var ( + ErrSyntax = errors.New("invalid syntax") + dictKeyRex = regexp.MustCompile(`^'([^']*)'$`) + sliceOperatorRex = regexp.MustCompile(`^(-?[\d]*)(:-?[\d]*)?(:[\d]*)?$`) +) + +// Parse parsed the given text and return a node Parser. +// If an error is encountered, parsing stops and an empty +// Parser is returned with the error +func Parse(name, text string) (*Parser, error) { + p := NewParser(name) + err := p.Parse(text) + if err != nil { + p = nil + } + return p, err +} + +func NewParser(name string) *Parser { + return &Parser{ + Name: name, + } +} + +// parseAction parsed the expression inside delimiter +func parseAction(name, text string) (*Parser, error) { + p, err := Parse(name, fmt.Sprintf("%s%s%s", leftDelim, text, rightDelim)) + // when error happens, p will be nil, so we need to return here + if err != nil { + return p, err + } + p.Root = p.Root.Nodes[0].(*ListNode) + return p, nil +} + +func (p *Parser) Parse(text string) error { + p.input = text + p.Root = newList() + p.pos = 0 + return p.parseText(p.Root) +} + +// consumeText return the parsed text since last cosumeText +func (p *Parser) consumeText() string { + value := p.input[p.start:p.pos] + p.start = p.pos + return value +} + +// next returns the next rune in the input. +func (p *Parser) next() rune { + if int(p.pos) >= len(p.input) { + p.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(p.input[p.pos:]) + p.width = w + p.pos += p.width + return r +} + +// peek returns but does not consume the next rune in the input. +func (p *Parser) peek() rune { + r := p.next() + p.backup() + return r +} + +// backup steps back one rune. Can only be called once per call of next. +func (p *Parser) backup() { + p.pos -= p.width +} + +func (p *Parser) parseText(cur *ListNode) error { + for { + if strings.HasPrefix(p.input[p.pos:], leftDelim) { + if p.pos > p.start { + cur.append(newText(p.consumeText())) + } + return p.parseLeftDelim(cur) + } + if p.next() == eof { + break + } + } + // Correctly reached EOF. + if p.pos > p.start { + cur.append(newText(p.consumeText())) + } + return nil +} + +// parseLeftDelim scans the left delimiter, which is known to be present. +func (p *Parser) parseLeftDelim(cur *ListNode) error { + p.pos += len(leftDelim) + p.consumeText() + newNode := newList() + cur.append(newNode) + cur = newNode + return p.parseInsideAction(cur) +} + +func (p *Parser) parseInsideAction(cur *ListNode) error { + prefixMap := map[string]func(*ListNode) error{ + rightDelim: p.parseRightDelim, + "[?(": p.parseFilter, + "..": p.parseRecursive, + } + for prefix, parseFunc := range prefixMap { + if strings.HasPrefix(p.input[p.pos:], prefix) { + return parseFunc(cur) + } + } + + switch r := p.next(); { + case r == eof || isEndOfLine(r): + return fmt.Errorf("unclosed action") + case r == ' ': + p.consumeText() + case r == '@' || r == '$': //the current object, just pass it + p.consumeText() + case r == '[': + return p.parseArray(cur) + case r == '"' || r == '\'': + return p.parseQuote(cur, r) + case r == '.': + return p.parseField(cur) + case r == '+' || r == '-' || unicode.IsDigit(r): + p.backup() + return p.parseNumber(cur) + case isAlphaNumeric(r): + p.backup() + return p.parseIdentifier(cur) + default: + return fmt.Errorf("unrecognized character in action: %#U", r) + } + return p.parseInsideAction(cur) +} + +// parseRightDelim scans the right delimiter, which is known to be present. +func (p *Parser) parseRightDelim(cur *ListNode) error { + p.pos += len(rightDelim) + p.consumeText() + cur = p.Root + return p.parseText(cur) +} + +// parseIdentifier scans build-in keywords, like "range" "end" +func (p *Parser) parseIdentifier(cur *ListNode) error { + var r rune + for { + r = p.next() + if isTerminator(r) { + p.backup() + break + } + } + value := p.consumeText() + + if isBool(value) { + v, err := strconv.ParseBool(value) + if err != nil { + return fmt.Errorf("can not parse bool '%s': %s", value, err.Error()) + } + + cur.append(newBool(v)) + } else { + cur.append(newIdentifier(value)) + } + + return p.parseInsideAction(cur) +} + +// parseRecursive scans the recursive desent operator .. +func (p *Parser) parseRecursive(cur *ListNode) error { + p.pos += len("..") + p.consumeText() + cur.append(newRecursive()) + if r := p.peek(); isAlphaNumeric(r) { + return p.parseField(cur) + } + return p.parseInsideAction(cur) +} + +// parseNumber scans number +func (p *Parser) parseNumber(cur *ListNode) error { + r := p.peek() + if r == '+' || r == '-' { + r = p.next() + } + for { + r = p.next() + if r != '.' && !unicode.IsDigit(r) { + p.backup() + break + } + } + value := p.consumeText() + i, err := strconv.Atoi(value) + if err == nil { + cur.append(newInt(i)) + return p.parseInsideAction(cur) + } + d, err := strconv.ParseFloat(value, 64) + if err == nil { + cur.append(newFloat(d)) + return p.parseInsideAction(cur) + } + return fmt.Errorf("cannot parse number %s", value) +} + +// parseArray scans array index selection +func (p *Parser) parseArray(cur *ListNode) error { +Loop: + for { + switch p.next() { + case eof, '\n': + return fmt.Errorf("unterminated array") + case ']': + break Loop + } + } + text := p.consumeText() + text = string(text[1 : len(text)-1]) + if text == "*" { + text = ":" + } + + //union operator + strs := strings.Split(text, ",") + if len(strs) > 1 { + union := []*ListNode{} + for _, str := range strs { + parser, err := parseAction("union", fmt.Sprintf("[%s]", strings.Trim(str, " "))) + if err != nil { + return err + } + union = append(union, parser.Root) + } + cur.append(newUnion(union)) + return p.parseInsideAction(cur) + } + + // dict key + value := dictKeyRex.FindStringSubmatch(text) + if value != nil { + parser, err := parseAction("arraydict", fmt.Sprintf(".%s", value[1])) + if err != nil { + return err + } + for _, node := range parser.Root.Nodes { + cur.append(node) + } + return p.parseInsideAction(cur) + } + + //slice operator + value = sliceOperatorRex.FindStringSubmatch(text) + if value == nil { + return fmt.Errorf("invalid array index %s", text) + } + value = value[1:] + params := [3]ParamsEntry{} + for i := 0; i < 3; i++ { + if value[i] != "" { + if i > 0 { + value[i] = value[i][1:] + } + if i > 0 && value[i] == "" { + params[i].Known = false + } else { + var err error + params[i].Known = true + params[i].Value, err = strconv.Atoi(value[i]) + if err != nil { + return fmt.Errorf("array index %s is not a number", value[i]) + } + } + } else { + if i == 1 { + params[i].Known = true + params[i].Value = params[0].Value + 1 + } else { + params[i].Known = false + params[i].Value = 0 + } + } + } + cur.append(newArray(params)) + return p.parseInsideAction(cur) +} + +// parseFilter scans filter inside array selection +func (p *Parser) parseFilter(cur *ListNode) error { + p.pos += len("[?(") + p.consumeText() + begin := false + end := false + var pair rune + +Loop: + for { + r := p.next() + switch r { + case eof, '\n': + return fmt.Errorf("unterminated filter") + case '"', '\'': + if begin == false { + //save the paired rune + begin = true + pair = r + continue + } + //only add when met paired rune + if p.input[p.pos-2] != '\\' && r == pair { + end = true + } + case ')': + //in rightParser below quotes only appear zero or once + //and must be paired at the beginning and end + if begin == end { + break Loop + } + } + } + if p.next() != ']' { + return fmt.Errorf("unclosed array expect ]") + } + reg := regexp.MustCompile(`^([^!<>=]+)([!<>=]+)(.+?)$`) + text := p.consumeText() + text = string(text[:len(text)-2]) + value := reg.FindStringSubmatch(text) + if value == nil { + parser, err := parseAction("text", text) + if err != nil { + return err + } + cur.append(newFilter(parser.Root, newList(), "exists")) + } else { + leftParser, err := parseAction("left", value[1]) + if err != nil { + return err + } + rightParser, err := parseAction("right", value[3]) + if err != nil { + return err + } + cur.append(newFilter(leftParser.Root, rightParser.Root, value[2])) + } + return p.parseInsideAction(cur) +} + +// parseQuote unquotes string inside double or single quote +func (p *Parser) parseQuote(cur *ListNode, end rune) error { +Loop: + for { + switch p.next() { + case eof, '\n': + return fmt.Errorf("unterminated quoted string") + case end: + //if it's not escape break the Loop + if p.input[p.pos-2] != '\\' { + break Loop + } + } + } + value := p.consumeText() + s, err := UnquoteExtend(value) + if err != nil { + return fmt.Errorf("unquote string %s error %v", value, err) + } + cur.append(newText(s)) + return p.parseInsideAction(cur) +} + +// parseField scans a field until a terminator +func (p *Parser) parseField(cur *ListNode) error { + p.consumeText() + for p.advance() { + } + value := p.consumeText() + if value == "*" { + cur.append(newWildcard()) + } else { + cur.append(newField(strings.Replace(value, "\\", "", -1))) + } + return p.parseInsideAction(cur) +} + +// advance scans until next non-escaped terminator +func (p *Parser) advance() bool { + r := p.next() + if r == '\\' { + p.next() + } else if isTerminator(r) { + p.backup() + return false + } + return true +} + +// isTerminator reports whether the input is at valid termination character to appear after an identifier. +func isTerminator(r rune) bool { + if isSpace(r) || isEndOfLine(r) { + return true + } + switch r { + case eof, '.', ',', '[', ']', '$', '@', '{', '}': + return true + } + return false +} + +// isSpace reports whether r is a space character. +func isSpace(r rune) bool { + return r == ' ' || r == '\t' +} + +// isEndOfLine reports whether r is an end-of-line character. +func isEndOfLine(r rune) bool { + return r == '\r' || r == '\n' +} + +// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. +func isAlphaNumeric(r rune) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) +} + +// isBool reports whether s is a boolean value. +func isBool(s string) bool { + return s == "true" || s == "false" +} + +//UnquoteExtend is almost same as strconv.Unquote(), but it support parse single quotes as a string +func UnquoteExtend(s string) (string, error) { + n := len(s) + if n < 2 { + return "", ErrSyntax + } + quote := s[0] + if quote != s[n-1] { + return "", ErrSyntax + } + s = s[1 : n-1] + + if quote != '"' && quote != '\'' { + return "", ErrSyntax + } + + // Is it trivial? Avoid allocation. + if !contains(s, '\\') && !contains(s, quote) { + return s, nil + } + + var runeTmp [utf8.UTFMax]byte + buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. + for len(s) > 0 { + c, multibyte, ss, err := strconv.UnquoteChar(s, quote) + if err != nil { + return "", err + } + s = ss + if c < utf8.RuneSelf || !multibyte { + buf = append(buf, byte(c)) + } else { + n := utf8.EncodeRune(runeTmp[:], c) + buf = append(buf, runeTmp[:n]...) + } + } + return string(buf), nil +} + +func contains(s string, c byte) bool { + for i := 0; i < len(s); i++ { + if s[i] == c { + return true + } + } + return false +}