alias kubernetes api to compose-on-kubernetes implementation

Signed-off-by: Jérémie Drouet <jeremie.drouet@gmail.com>
This commit is contained in:
Jérémie Drouet 2018-12-19 16:36:25 +01:00
parent 298c423b57
commit 8a634aa578
104 changed files with 10232 additions and 1465 deletions

View File

@ -1,55 +1,20 @@
package kubernetes package kubernetes
import ( import api "github.com/docker/compose-on-kubernetes/api"
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"
)
// StackVersion represents the detected Compose Component on Kubernetes side. // 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 ( const (
// StackAPIV1Beta1 is returned if it's the most recent version available. // 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 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. // GetStackAPIVersion returns the most recent stack API installed.
func GetStackAPIVersion(clientSet *kubernetes.Clientset) (StackVersion, error) { // Deprecated: Use github.com/docker/compose-on-kubernetes/api.GetStackAPIVersion instead
groups, err := clientSet.Discovery().ServerGroups() var GetStackAPIVersion = api.GetStackAPIVersion
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
}

View File

@ -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,
}
}

View File

@ -1,96 +1,25 @@
package clientset package clientset
import ( import api "github.com/docker/compose-on-kubernetes/api/client/clientset"
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"
)
// Interface defines the methods a compose kube client should have // Interface defines the methods a compose kube client should have
// FIXME(vdemeester) is it required ? // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.Interface instead
type Interface interface { type Interface = api.Interface
Discovery() discovery.DiscoveryInterface
ComposeV1beta2() composev1beta2.ComposeV1beta2Interface
ComposeV1beta1() composev1beta1.ComposeV1beta1Interface
}
// Clientset contains the clients for groups. Each group has exactly one // Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset. // version included in a Clientset.
type Clientset struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.Clientset instead
*discovery.DiscoveryClient type Clientset = api.Clientset
*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
}
// NewForConfig creates a new Clientset for the given config. // NewForConfig creates a new Clientset for the given config.
func NewForConfig(c *rest.Config) (*Clientset, error) { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.NewForConfig instead
configShallowCopy := *c var NewForConfig = api.NewForConfig
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 // NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config. // panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.NewForConfigOrDie instead
var cs Clientset var NewForConfigOrDie = api.NewForConfigOrDie
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. // New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset.New instead
var cs Clientset var New = api.New
cs.ComposeV1beta2Client = composev1beta2.New(c)
cs.ComposeV1beta1Client = composev1beta1.New(c)
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
return &cs
}

View File

@ -1,26 +1,17 @@
package scheme package scheme
import ( import api "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme"
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"
)
// Variables required for registration // Variables required for registration
var ( var (
Scheme = runtime.NewScheme() // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/scheme.Scheme instead
Codecs = serializer.NewCodecFactory(Scheme) Scheme = api.Scheme
ParameterCodec = runtime.NewParameterCodec(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 // AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in: // of clientsets, like in:
// //
@ -35,7 +26,5 @@ func init() {
// //
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly. // correctly.
func AddToScheme(scheme *runtime.Scheme) { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/scheme.AddToScheme instead
composev1beta2.AddToScheme(scheme) var AddToScheme = api.AddToScheme
composev1beta1.AddToScheme(scheme)
}

View File

@ -1,74 +1,24 @@
package v1beta1 package v1beta1
import ( import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
"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"
)
// ComposeV1beta1Interface defines the methods a compose v1beta1 client has // ComposeV1beta1Interface defines the methods a compose v1beta1 client has
type ComposeV1beta1Interface interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.ComposeV1beta1Interface instead
RESTClient() rest.Interface type ComposeV1beta1Interface = api.ComposeV1beta1Interface
StacksGetter
}
// ComposeV1beta1Client is used to interact with features provided by the compose.docker.com group. // ComposeV1beta1Client is used to interact with features provided by the compose.docker.com group.
type ComposeV1beta1Client struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.ComposeV1beta1Client instead
restClient rest.Interface type ComposeV1beta1Client = api.ComposeV1beta1Client
}
// 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. // NewForConfig creates a new ComposeV1beta1Client for the given config.
func NewForConfig(c *rest.Config) (*ComposeV1beta1Client, error) { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.NewForConfig instead
config := *c var NewForConfig = api.NewForConfig
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 // NewForConfigOrDie creates a new ComposeV1beta1Client for the given config and
// panics if there is an error in the config. // panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *ComposeV1beta1Client { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.NewForConfigOrDie instead
client, err := NewForConfig(c) var NewForConfigOrDie = api.NewForConfigOrDie
if err != nil {
panic(err)
}
return client
}
// New creates a new ComposeV1beta1Client for the given RESTClient. // New creates a new ComposeV1beta1Client for the given RESTClient.
func New(c rest.Interface) *ComposeV1beta1Client { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.New instead
return &ComposeV1beta1Client{c} var New = api.New
}
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
}

View File

@ -1,157 +1,12 @@
package v1beta1 package v1beta1
import ( import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1"
"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"
)
// StacksGetter has a method to return a StackInterface. // StacksGetter has a method to return a StackInterface.
// A group's client should implement this interface. // A group's client should implement this interface.
type StacksGetter interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.StacksGetter instead
Stacks(namespace string) StackInterface type StacksGetter = api.StacksGetter
}
// StackInterface has methods to work with Stack resources. // StackInterface has methods to work with Stack resources.
type StackInterface interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1.StackInterface instead
Create(*v1beta1.Stack) (*v1beta1.Stack, error) type StackInterface = api.StackInterface
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
}

View File

@ -1,74 +1,24 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
"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"
)
// ComposeV1beta2Interface defines the methods a compose v1beta2 client has // ComposeV1beta2Interface defines the methods a compose v1beta2 client has
type ComposeV1beta2Interface interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.StackInterface instead
RESTClient() rest.Interface type ComposeV1beta2Interface = api.ComposeV1beta2Interface
StacksGetter
}
// ComposeV1beta2Client is used to interact with features provided by the compose.docker.com group. // ComposeV1beta2Client is used to interact with features provided by the compose.docker.com group.
type ComposeV1beta2Client struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.ComposeV1beta2Client instead
restClient rest.Interface type ComposeV1beta2Client = api.ComposeV1beta2Client
}
// 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. // NewForConfig creates a new ComposeV1beta2Client for the given config.
func NewForConfig(c *rest.Config) (*ComposeV1beta2Client, error) { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.NewForConfig instead
config := *c var NewForConfig = api.NewForConfig
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 // NewForConfigOrDie creates a new ComposeV1beta2Client for the given config and
// panics if there is an error in the config. // panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *ComposeV1beta2Client { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.NewForConfigOrDie instead
client, err := NewForConfig(c) var NewForConfigOrDie = api.NewForConfigOrDie
if err != nil {
panic(err)
}
return client
}
// New creates a new ComposeV1beta2Client for the given RESTClient. // New creates a new ComposeV1beta2Client for the given RESTClient.
func New(c rest.Interface) *ComposeV1beta2Client { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.New instead
return &ComposeV1beta2Client{c} var New = api.New
}
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
}

View File

@ -1,155 +1,12 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2"
"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"
)
// StacksGetter has a method to return a StackInterface. // StacksGetter has a method to return a StackInterface.
// A group's client should implement this interface. // A group's client should implement this interface.
type StacksGetter interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.StacksGetter instead
Stacks(namespace string) StackInterface type StacksGetter = api.StacksGetter
}
// StackInterface has methods to work with Stack resources. // StackInterface has methods to work with Stack resources.
type StackInterface interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2.StackInterface instead
Create(*v1beta2.Stack) (*v1beta2.Stack, error) type StackInterface = api.StackInterface
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
}

View File

@ -1,25 +1,11 @@
package compose package compose
import ( import api "github.com/docker/compose-on-kubernetes/api/client/informers/compose"
"github.com/docker/cli/kubernetes/client/informers/compose/v1beta2"
"github.com/docker/cli/kubernetes/client/informers/internalinterfaces"
)
// Interface provides access to each of this group's versions. // Interface provides access to each of this group's versions.
type Interface interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose.Interface instead
V1beta2() v1beta2.Interface type Interface = api.Interface
}
type group struct {
internalinterfaces.SharedInformerFactory
}
// New returns a new Interface. // New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory) Interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose.New instead
return &group{f} var New = api.New
}
// V1beta2 returns a new v1beta2.Interface.
func (g *group) V1beta2() v1beta2.Interface {
return v1beta2.New(g.SharedInformerFactory)
}

View File

@ -1,25 +1,11 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2"
"github.com/docker/cli/kubernetes/client/informers/internalinterfaces"
)
// Interface provides access to all the informers in this group version. // Interface provides access to all the informers in this group version.
type Interface interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2.Interface instead
// Stacks returns a StackInformer. type Interface = api.Interface
Stacks() StackInformer
}
type version struct {
internalinterfaces.SharedInformerFactory
}
// New returns a new Interface. // New returns a new Interface.
func New(f internalinterfaces.SharedInformerFactory) Interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2.New instead
return &version{f} var New = api.New
}
// Stacks returns a StackInformer.
func (v *version) Stacks() StackInformer {
return &stackInformer{factory: v.SharedInformerFactory}
}

View File

@ -1,51 +1,8 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2"
"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"
)
// StackInformer provides access to a shared informer and lister for // StackInformer provides access to a shared informer and lister for
// Stacks. // Stacks.
type StackInformer interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2.StackInformer instead
Informer() cache.SharedIndexInformer type StackInformer = api.StackInformer
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())
}

View File

@ -1,101 +1,12 @@
package informers package informers
import ( import api "github.com/docker/compose-on-kubernetes/api/client/informers"
"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
}
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory // NewSharedInformerFactory constructs a new instance of sharedInformerFactory
func NewSharedInformerFactory(client clientset.Interface, defaultResync time.Duration) SharedInformerFactory { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers.NewSharedInformerFactory instead
return &sharedInformerFactory{ var NewSharedInformerFactory = api.NewSharedInformerFactory
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 // SharedInformerFactory provides shared informers for resources in all known
// API group versions. // API group versions.
type SharedInformerFactory interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers.SharedInformerFactory instead
internalinterfaces.SharedInformerFactory type SharedInformerFactory = api.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)
}

View File

@ -1,44 +1,8 @@
package informers package informers
import ( import api "github.com/docker/compose-on-kubernetes/api/client/informers"
"fmt"
"github.com/docker/cli/kubernetes/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 // GenericInformer is type of SharedIndexInformer which will locate and delegate to other
// sharedInformers based on type // sharedInformers based on type
type GenericInformer interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers.GenericInformer instead
Informer() cache.SharedIndexInformer type GenericInformer = api.GenericInformer
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)
}

View File

@ -1,18 +1,11 @@
package internalinterfaces package internalinterfaces
import ( import api "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces"
"time"
"github.com/docker/cli/kubernetes/client/clientset"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
)
// NewInformerFunc defines a Informer constructor (from a clientset and a duration) // 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 // SharedInformerFactory a small interface to allow for adding an informer without an import cycle
type SharedInformerFactory interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces.SharedInformerFactory instead
Start(stopCh <-chan struct{}) type SharedInformerFactory = api.SharedInformerFactory
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
}

View File

@ -1,9 +1,13 @@
package v1beta2 package v1beta2
import api "github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2"
// StackListerExpansion allows custom methods to be added to // StackListerExpansion allows custom methods to be added to
// StackLister. // 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 // StackNamespaceListerExpansion allows custom methods to be added to
// StackNamespaceLister. // StackNamespaceLister.
type StackNamespaceListerExpansion interface{} // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackNamespaceListerExpansion instead
type StackNamespaceListerExpansion = api.StackNamespaceListerExpansion

View File

@ -1,78 +1,15 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2"
"github.com/docker/cli/kubernetes/compose/v1beta2"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// StackLister helps list Stacks. // StackLister helps list Stacks.
type StackLister interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackLister instead
// List lists all Stacks in the indexer. type StackLister = api.StackLister
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. // NewStackLister returns a new StackLister.
func NewStackLister(indexer cache.Indexer) StackLister { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.NewStackLister instead
return &stackLister{indexer: indexer} var NewStackLister = api.NewStackLister
}
// 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. // StackNamespaceLister helps list and get Stacks.
type StackNamespaceLister interface { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2.StackNamespaceLister instead
// List lists all Stacks in the indexer for a given namespace. type StackNamespaceLister = api.StackNamespaceLister
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
}

View File

@ -1,25 +1,11 @@
package clone package clone
import api "github.com/docker/compose-on-kubernetes/api/compose/clone"
// MapOfStringToSliceOfString deep copy a map[string][]string // MapOfStringToSliceOfString deep copy a map[string][]string
func MapOfStringToSliceOfString(source map[string][]string) map[string][]string { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/clone.MapOfStringToSliceOfString instead
if source == nil { var MapOfStringToSliceOfString = api.MapOfStringToSliceOfString
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 // MapOfStringToInt deep copy a map[string]int
func MapOfStringToInt(source map[string]int) map[string]int { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/clone.MapOfStringToInt instead
if source == nil { var MapOfStringToInt = api.MapOfStringToInt
return nil
}
res := make(map[string]int, len(source))
for k, v := range source {
res[k] = v
}
return res
}

View File

@ -1,11 +1,7 @@
package clone package clone
import api "github.com/docker/compose-on-kubernetes/api/compose/clone"
// SliceOfString deep copy a slice of strings // SliceOfString deep copy a slice of strings
func SliceOfString(source []string) []string { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/clone.SliceOfString instead
if source == nil { var SliceOfString = api.SliceOfString
return nil
}
res := make([]string, len(source))
copy(res, source)
return res
}

View File

@ -1,26 +1,7 @@
package impersonation 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. // Config contains the data required to impersonate a user.
type Config struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/impersonation.Config instead
// UserName is the username to impersonate on each request. type Config = api.Config
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
}

View File

@ -1,31 +1,8 @@
package v1beta1 package v1beta1
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
"github.com/docker/cli/kubernetes/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 // Owner defines the owner of a stack. It is used to impersonate the controller calls
// to kubernetes api. // to kubernetes api.
type Owner struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.Owner instead
metav1.TypeMeta `json:",inline"` type Owner = api.Owner
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()
}

View File

@ -1,4 +1,7 @@
package v1beta1 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 // 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

View File

@ -1,39 +1,22 @@
package v1beta1 package v1beta1
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
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 // 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 // Alias variables for the registration
var ( var (
// SchemeGroupVersion is group version used to register these objects // SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.SchemeGroupVersion instead
SchemeBuilder runtime.SchemeBuilder SchemeGroupVersion = api.SchemeGroupVersion
localSchemeBuilder = &SchemeBuilder // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.SchemeBuilder instead
AddToScheme = localSchemeBuilder.AddToScheme 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 // Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.Resource instead
return SchemeGroupVersion.WithResource(resource).GroupResource() var Resource = api.Resource
}

View File

@ -1,87 +1,37 @@
package v1beta1 package v1beta1
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// StackList defines a list of stacks // StackList defines a list of stacks
type StackList struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackList instead
metav1.TypeMeta `json:",inline"` type StackList = api.StackList
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 // Stack defines a stack object to be register in the kubernetes API
type Stack struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.Stack instead
metav1.TypeMeta `json:",inline"` type Stack = api.Stack
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec StackSpec `json:"spec,omitempty"`
Status StackStatus `json:"status,omitempty"`
}
// StackSpec defines the desired state of Stack // StackSpec defines the desired state of Stack
type StackSpec struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackSpec instead
ComposeFile string `json:"composeFile,omitempty"` type StackSpec = api.StackSpec
}
// StackPhase defines the status phase in which the stack is. // 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. // These are valid conditions of a stack.
const ( const (
// StackAvailable means the stack is available. // 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 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 // StackFailure is added in a stack when one of its members fails to be created
// or deleted. // 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 // StackStatus defines the observed state of Stack
type StackStatus struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta1.StackStatus instead
// Current condition of the stack. type StackStatus = api.StackStatus
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()
}

View File

@ -1,26 +1,7 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
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 // ComposeFile is the content of a stack's compose file if any
type ComposeFile struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ComposeFile instead
metav1.TypeMeta `json:",inline"` type ComposeFile = api.ComposeFile
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()
}

View File

@ -1,30 +1,7 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
"github.com/docker/cli/kubernetes/compose/impersonation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// Owner describes the user who created the stack // Owner describes the user who created the stack
type Owner struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Owner instead
metav1.TypeMeta `json:",inline"` type Owner = api.Owner
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()
}

View File

@ -1,42 +1,23 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
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 // 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 ( var (
// SchemeGroupVersion is group version used to register these objects // 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 is the scheme builder
SchemeBuilder runtime.SchemeBuilder // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.SchemeBuilder instead
localSchemeBuilder = &SchemeBuilder SchemeBuilder = api.SchemeBuilder
// AddToScheme adds to scheme // 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 // GroupResource takes an unqualified resource and returns a Group qualified GroupResource
func GroupResource(resource string) schema.GroupResource { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.GroupResource instead
return SchemeGroupVersion.WithResource(resource).GroupResource() var GroupResource = api.GroupResource
}

View File

@ -1,29 +1,7 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
"github.com/docker/cli/kubernetes/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. // Scale contains the current/desired replica count for services in a stack.
type Scale struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Scale instead
metav1.TypeMeta `json:",inline"` type Scale = api.Scale
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()
}

View File

@ -1,256 +1,115 @@
package v1beta2 package v1beta2
import ( import api "github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// StackList is a list of stacks // StackList is a list of stacks
type StackList struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackList instead
metav1.TypeMeta `json:",inline"` type StackList = api.StackList
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 // Stack is v1beta2's representation of a Stack
type Stack struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Stack instead
metav1.TypeMeta `json:",inline"` type Stack = api.Stack
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
}
// StackSpec defines the desired state of Stack // StackSpec defines the desired state of Stack
type StackSpec struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackSpec instead
Services []ServiceConfig `json:"services,omitempty"` type StackSpec = api.StackSpec
Secrets map[string]SecretConfig `json:"secrets,omitempty"`
Configs map[string]ConfigObjConfig `json:"configs,omitempty"`
}
// ServiceConfig is the configuration of one service // ServiceConfig is the configuration of one service
type ServiceConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServiceConfig instead
Name string `json:"name,omitempty"` type ServiceConfig = api.ServiceConfig
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 // ServicePortConfig is the port configuration for a service
type ServicePortConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServicePortConfig instead
Mode string `json:"mode,omitempty"` type ServicePortConfig = api.ServicePortConfig
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 // FileObjectConfig is a config type for a file used by a service
type FileObjectConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.FileObjectConfig instead
Name string `json:"name,omitempty"` type FileObjectConfig = api.FileObjectConfig
File string `json:"file,omitempty"`
External External `json:"external,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
// SecretConfig for a secret // 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 // 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 // External identifies a Volume or Network as a reference to a resource that is
// not managed, and should already exist. // not managed, and should already exist.
// External.name is deprecated and replaced by Volume.name // External.name is deprecated and replaced by Volume.name
type External struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.External instead
Name string `json:"name,omitempty"` type External = api.External
External bool `json:"external,omitempty"`
}
// FileReferenceConfig for a reference to a swarm file object // FileReferenceConfig for a reference to a swarm file object
type FileReferenceConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.FileReferenceConfig instead
Source string `json:"source,omitempty"` type FileReferenceConfig = api.FileReferenceConfig
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 // 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 // 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 // DeployConfig is the deployment configuration for a service
type DeployConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.DeployConfig instead
Mode string `json:"mode,omitempty"` type DeployConfig = api.DeployConfig
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 // UpdateConfig is the service update configuration
type UpdateConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.UpdateConfig instead
Parallelism *uint64 `json:"paralellism,omitempty"` type UpdateConfig = api.UpdateConfig
}
// Resources the resource limits and reservations // Resources the resource limits and reservations
type Resources struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Resources instead
Limits *Resource `json:"limits,omitempty"` type Resources = api.Resources
Reservations *Resource `json:"reservations,omitempty"`
}
// Resource is a resource to be limited or reserved // Resource is a resource to be limited or reserved
type Resource struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Resource instead
NanoCPUs string `json:"cpus,omitempty"` type Resource = api.Resource
MemoryBytes int64 `json:"memory,omitempty"`
}
// RestartPolicy is the service restart policy // RestartPolicy is the service restart policy
type RestartPolicy struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.RestartPolicy instead
Condition string `json:"condition,omitempty"` type RestartPolicy = api.RestartPolicy
}
// Placement constraints for the service // Placement constraints for the service
type Placement struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Placement instead
Constraints *Constraints `json:"constraints,omitempty"` type Placement = api.Placement
}
// Constraints lists constraints that can be set on the service // Constraints lists constraints that can be set on the service
type Constraints struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Constraints instead
OperatingSystem *Constraint type Constraints = api.Constraints
Architecture *Constraint
Hostname *Constraint
MatchLabels map[string]Constraint
}
// Constraint defines a constraint and it's operator (== or !=) // Constraint defines a constraint and it's operator (== or !=)
type Constraint struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.Constraint instead
Value string type Constraint = api.Constraint
Operator string
}
// HealthCheckConfig the healthcheck configuration for a service // HealthCheckConfig the healthcheck configuration for a service
type HealthCheckConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.HealthCheckConfig instead
Test []string `json:"test,omitempty"` type HealthCheckConfig = api.HealthCheckConfig
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 // ServiceVolumeConfig are references to a volume used by a service
type ServiceVolumeConfig struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.ServiceVolumeConfig instead
Type string `json:"type,omitempty"` type ServiceVolumeConfig = api.ServiceVolumeConfig
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
}
// StackPhase is the deployment phase of a stack // 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. // These are valid conditions of a stack.
const ( const (
// StackAvailable means the stack is available. // 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 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 // StackFailure is added in a stack when one of its members fails to be created
// or deleted. // 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 // StackStatus defines the observed state of Stack
type StackStatus struct { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/compose/v1beta2.StackStatus instead
// Current condition of the stack. type StackStatus = api.StackStatus
// +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()
}

View File

@ -1,26 +1,8 @@
package kubernetes package kubernetes
import ( import api "github.com/docker/compose-on-kubernetes/api"
"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 // NewKubernetesConfig resolves the path to the desired Kubernetes configuration file based on
// the KUBECONFIG environment variable and command line flags. // the KUBECONFIG environment variable and command line flags.
func NewKubernetesConfig(configPath string) clientcmd.ClientConfig { // Deprecated: Use github.com/docker/compose-on-kubernetes/api.NewKubernetesConfig instead
kubeConfig := configPath var NewKubernetesConfig = api.NewKubernetesConfig
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{})
}

View File

@ -1,45 +1,24 @@
package labels package labels
import ( import labels "github.com/docker/compose-on-kubernetes/api/labels"
"fmt"
"strings"
)
const ( const (
// ForServiceName is the label for the service name. // 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 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 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. // ForService gives the labels to select a given service in a stack.
func ForService(stackName, serviceName string) map[string]string { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.ForService instead
labels := map[string]string{} var ForService = labels.ForService
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. // SelectorForStack gives the labelSelector to use for a given stack.
// Specific service names can be passed to narrow down the selection. // Specific service names can be passed to narrow down the selection.
func SelectorForStack(stackName string, serviceNames ...string) string { // Deprecated: Use github.com/docker/compose-on-kubernetes/api/labels.SelectorForStack instead
switch len(serviceNames) { var SelectorForStack = labels.SelectorForStack
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, ","))
}
}

View File

@ -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")))
}

View File

@ -1,3 +1,4 @@
cloud.google.com/go 0ebda48a7f143b1cce9eb37a8c1106ac762a3430 # v0.34.0
github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403 github.com/agl/ed25519 5312a61534124124185d41f09206b9fef1d88403
github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541 github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 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/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af
github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5
github.com/docker/docker 6e3113f700dea1bf2785d94731b4b5a1e602d9ab github.com/docker/docker 6e3113f700dea1bf2785d94731b4b5a1e602d9ab
github.com/docker/compose-on-kubernetes a6086e2369e39c2058a003a7eb42e567ecfd1f03 # v0.4.17
github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962
# the docker/go package contains a customized version of canonical/json # 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. # 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 github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d
golang.org/x/crypto 0709b304e793a5edb4a2c0145f281ecdc20838a4 golang.org/x/crypto 0709b304e793a5edb4a2c0145f281ecdc20838a4
golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1 golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1
golang.org/x/oauth2 ef147856a6ddbb60760db74283d2424e98c87bff
golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca
golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2
golang.org/x/text f21a4dfb5e38f5895301dc265a8def02365cc3d0 # v0.3.0 golang.org/x/text f21a4dfb5e38f5895301dc265a8def02365cc3d0 # v0.3.0

202
vendor/cloud.google.com/go/LICENSE generated vendored Normal file
View File

@ -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.

505
vendor/cloud.google.com/go/README.md generated vendored Normal file
View File

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

View File

@ -0,0 +1,9 @@
// +build ignore
// Empty include file to generate z symbols
// EOF

View File

@ -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
// <pc update>* <line update>
// 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
}
}

View File

@ -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 "<malformed symbol table>", 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
}

501
vendor/cloud.google.com/go/compute/metadata/metadata.go generated vendored Normal file
View File

@ -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
// "<instanceID>.c.<projID>.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
// "<instanceID>.c.<projID>.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/<projNum>/zones/<zoneName>".
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
}
}
}

201
vendor/github.com/docker/compose-on-kubernetes/LICENSE generated vendored Normal file
View File

@ -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.

View File

@ -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 <none> 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).

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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)
}

View File

@ -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}
}

View File

@ -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())
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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{}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -0,0 +1,3 @@
// Package impersonation holds data structures for enabling user impersonation within Conpose for Kubernetes
// +k8s:openapi-gen=true
package impersonation

View File

@ -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
}

View File

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

View File

@ -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()
}

View File

@ -0,0 +1,4 @@
package v1beta1
// MaxComposeVersion is the most recent version of compose file Schema supported in v1beta1
const MaxComposeVersion = "3.5"

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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
}

View File

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

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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{})
}

View File

@ -0,0 +1,4 @@
//
// +domain=docker.com
package apis

View File

@ -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, ","))
}
}

27
vendor/golang.org/x/oauth2/LICENSE generated vendored Normal file
View File

@ -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.

77
vendor/golang.org/x/oauth2/README.md generated vendored Normal file
View File

@ -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.

89
vendor/golang.org/x/oauth2/google/appengine.go generated vendored Normal file
View File

@ -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
}

14
vendor/golang.org/x/oauth2/google/appengine_hook.go generated vendored Normal file
View File

@ -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
}

View File

@ -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.
}

115
vendor/golang.org/x/oauth2/google/default.go generated vendored Normal file
View File

@ -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...)
}

42
vendor/golang.org/x/oauth2/google/doc_go19.go generated vendored Normal file
View File

@ -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"

43
vendor/golang.org/x/oauth2/google/doc_not_go19.go generated vendored Normal file
View File

@ -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"

57
vendor/golang.org/x/oauth2/google/go19.go generated vendored Normal file
View File

@ -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)
}

192
vendor/golang.org/x/oauth2/google/google.go generated vendored Normal file
View File

@ -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
}

74
vendor/golang.org/x/oauth2/google/jwt.go generated vendored Normal file
View File

@ -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
}

54
vendor/golang.org/x/oauth2/google/not_go19.go generated vendored Normal file
View File

@ -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)
}

201
vendor/golang.org/x/oauth2/google/sdk.go generated vendored Normal file
View File

@ -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 ""
}

View File

@ -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
}

6
vendor/golang.org/x/oauth2/internal/doc.go generated vendored Normal file
View File

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

37
vendor/golang.org/x/oauth2/internal/oauth2.go generated vendored Normal file
View File

@ -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
}

270
vendor/golang.org/x/oauth2/internal/token.go generated vendored Normal file
View File

@ -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)
}

34
vendor/golang.org/x/oauth2/internal/transport.go generated vendored Normal file
View File

@ -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
}

182
vendor/golang.org/x/oauth2/jws/jws.go generated vendored Normal file
View File

@ -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))
}

162
vendor/golang.org/x/oauth2/jwt/jwt.go generated vendored Normal file
View File

@ -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
}

362
vendor/golang.org/x/oauth2/oauth2.go generated vendored Normal file
View File

@ -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,
}
}

175
vendor/golang.org/x/oauth2/token.go generated vendored Normal file
View File

@ -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)
}

144
vendor/golang.org/x/oauth2/transport.go generated vendored Normal file
View File

@ -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
}
}

View File

@ -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 }

View File

@ -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 "<no value>", 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
}

View File

@ -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("&#34;") // shorter than "&quot;"
htmlApos = []byte("&#39;") // shorter than "&apos;" and apos was not in HTML until HTML5
htmlAmp = []byte("&amp;")
htmlLt = []byte("&lt;")
htmlGt = []byte("&gt;")
)
// 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
}

Some files were not shown because too many files have changed in this diff Show More