mirror of https://github.com/docker/cli.git
alias kubernetes api to compose-on-kubernetes implementation
Signed-off-by: Jérémie Drouet <jeremie.drouet@gmail.com>
This commit is contained in:
parent
298c423b57
commit
8a634aa578
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -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{})
|
|
||||||
}
|
|
||||||
|
|
|
@ -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, ","))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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")))
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
9
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h
generated
vendored
Normal file
9
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
// Empty include file to generate z symbols
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// EOF
|
472
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go
generated
vendored
Normal file
472
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go
generated
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
731
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go
generated
vendored
Normal file
731
vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go
generated
vendored
Normal 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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
|
@ -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).
|
|
@ -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
|
||||||
|
}
|
107
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go
generated
vendored
Normal file
107
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go
generated
vendored
Normal 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
|
||||||
|
}
|
45
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go
generated
vendored
Normal file
45
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go
generated
vendored
Normal 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)
|
||||||
|
|
||||||
|
}
|
74
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/compose_client.go
generated
vendored
Normal file
74
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/compose_client.go
generated
vendored
Normal 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
|
||||||
|
}
|
175
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/stack.go
generated
vendored
Normal file
175
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1/stack.go
generated
vendored
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
74
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/compose_client.go
generated
vendored
Normal file
74
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/compose_client.go
generated
vendored
Normal 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
|
||||||
|
}
|
173
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/stack.go
generated
vendored
Normal file
173
vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2/stack.go
generated
vendored
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
25
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go
generated
vendored
Normal file
25
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go
generated
vendored
Normal 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)
|
||||||
|
}
|
25
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/interface.go
generated
vendored
Normal file
25
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/interface.go
generated
vendored
Normal 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}
|
||||||
|
}
|
51
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/stack.go
generated
vendored
Normal file
51
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2/stack.go
generated
vendored
Normal 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())
|
||||||
|
}
|
101
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/factory.go
generated
vendored
Normal file
101
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/factory.go
generated
vendored
Normal 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)
|
||||||
|
}
|
44
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go
generated
vendored
Normal file
44
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go
generated
vendored
Normal 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)
|
||||||
|
}
|
18
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces/factory_interfaces.go
generated
vendored
Normal file
18
vendor/github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces/factory_interfaces.go
generated
vendored
Normal 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
|
||||||
|
}
|
9
vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/expansion_generated.go
generated
vendored
Normal file
9
vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/expansion_generated.go
generated
vendored
Normal 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{}
|
78
vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/stack.go
generated
vendored
Normal file
78
vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1beta2/stack.go
generated
vendored
Normal 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
|
||||||
|
}
|
25
vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/maps.go
generated
vendored
Normal file
25
vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/maps.go
generated
vendored
Normal 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
|
||||||
|
}
|
11
vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/slices.go
generated
vendored
Normal file
11
vendor/github.com/docker/compose-on-kubernetes/api/compose/clone/slices.go
generated
vendored
Normal 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
|
||||||
|
}
|
3
vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/doc.go
generated
vendored
Normal file
3
vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package impersonation holds data structures for enabling user impersonation within Conpose for Kubernetes
|
||||||
|
// +k8s:openapi-gen=true
|
||||||
|
package impersonation
|
26
vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/impersonationconfig.go
generated
vendored
Normal file
26
vendor/github.com/docker/compose-on-kubernetes/api/compose/impersonation/impersonationconfig.go
generated
vendored
Normal 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
|
||||||
|
}
|
10
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/doc.go
generated
vendored
Normal file
10
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/doc.go
generated
vendored
Normal 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
|
31
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/owner.go
generated
vendored
Normal file
31
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/owner.go
generated
vendored
Normal 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()
|
||||||
|
}
|
4
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/parsing.go
generated
vendored
Normal file
4
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/parsing.go
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package v1beta1
|
||||||
|
|
||||||
|
// MaxComposeVersion is the most recent version of compose file Schema supported in v1beta1
|
||||||
|
const MaxComposeVersion = "3.5"
|
39
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/register.go
generated
vendored
Normal file
39
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/register.go
generated
vendored
Normal 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()
|
||||||
|
}
|
87
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/stack.go
generated
vendored
Normal file
87
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta1/stack.go
generated
vendored
Normal 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()
|
||||||
|
}
|
26
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/composefile_stack_types.go
generated
vendored
Normal file
26
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/composefile_stack_types.go
generated
vendored
Normal 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()
|
||||||
|
}
|
660
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/deepcopy_generated.go
generated
vendored
Normal file
660
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/deepcopy_generated.go
generated
vendored
Normal 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
|
||||||
|
}
|
7
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/doc.go
generated
vendored
Normal file
7
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/doc.go
generated
vendored
Normal 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
|
30
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/owner.go
generated
vendored
Normal file
30
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/owner.go
generated
vendored
Normal 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()
|
||||||
|
}
|
42
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/register.go
generated
vendored
Normal file
42
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/register.go
generated
vendored
Normal 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()
|
||||||
|
}
|
29
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/scale.go
generated
vendored
Normal file
29
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/scale.go
generated
vendored
Normal 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()
|
||||||
|
}
|
270
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/stack.go
generated
vendored
Normal file
270
vendor/github.com/docker/compose-on-kubernetes/api/compose/v1beta2/stack.go
generated
vendored
Normal 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()
|
||||||
|
}
|
|
@ -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{})
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
//
|
||||||
|
// +domain=docker.com
|
||||||
|
|
||||||
|
package apis
|
45
vendor/github.com/docker/compose-on-kubernetes/api/labels/labels.go
generated
vendored
Normal file
45
vendor/github.com/docker/compose-on-kubernetes/api/labels/labels.go
generated
vendored
Normal 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, ","))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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.
|
||||||
|
}
|
|
@ -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...)
|
||||||
|
}
|
|
@ -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"
|
|
@ -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"
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 ""
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 }
|
94
vendor/k8s.io/client-go/third_party/forked/golang/template/exec.go
generated
vendored
Normal file
94
vendor/k8s.io/client-go/third_party/forked/golang/template/exec.go
generated
vendored
Normal 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
|
||||||
|
}
|
599
vendor/k8s.io/client-go/third_party/forked/golang/template/funcs.go
generated
vendored
Normal file
599
vendor/k8s.io/client-go/third_party/forked/golang/template/funcs.go
generated
vendored
Normal 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(""") // shorter than """
|
||||||
|
htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5
|
||||||
|
htmlAmp = []byte("&")
|
||||||
|
htmlLt = []byte("<")
|
||||||
|
htmlGt = []byte(">")
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
|
||||||
|
func HTMLEscape(w io.Writer, b []byte) {
|
||||||
|
last := 0
|
||||||
|
for i, c := range b {
|
||||||
|
var html []byte
|
||||||
|
switch c {
|
||||||
|
case '"':
|
||||||
|
html = htmlQuot
|
||||||
|
case '\'':
|
||||||
|
html = htmlApos
|
||||||
|
case '&':
|
||||||
|
html = htmlAmp
|
||||||
|
case '<':
|
||||||
|
html = htmlLt
|
||||||
|
case '>':
|
||||||
|
html = htmlGt
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.Write(b[last:i])
|
||||||
|
w.Write(html)
|
||||||
|
last = i + 1
|
||||||
|
}
|
||||||
|
w.Write(b[last:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
|
||||||
|
func HTMLEscapeString(s string) string {
|
||||||
|
// Avoid allocation if we can.
|
||||||
|
if strings.IndexAny(s, `'"&<>`) < 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
HTMLEscape(&b, []byte(s))
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLEscaper returns the escaped HTML equivalent of the textual
|
||||||
|
// representation of its arguments.
|
||||||
|
func HTMLEscaper(args ...interface{}) string {
|
||||||
|
return HTMLEscapeString(evalArgs(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
// JavaScript escaping.
|
||||||
|
|
||||||
|
var (
|
||||||
|
jsLowUni = []byte(`\u00`)
|
||||||
|
hex = []byte("0123456789ABCDEF")
|
||||||
|
|
||||||
|
jsBackslash = []byte(`\\`)
|
||||||
|
jsApos = []byte(`\'`)
|
||||||
|
jsQuot = []byte(`\"`)
|
||||||
|
jsLt = []byte(`\x3C`)
|
||||||
|
jsGt = []byte(`\x3E`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
|
||||||
|
func JSEscape(w io.Writer, b []byte) {
|
||||||
|
last := 0
|
||||||
|
for i := 0; i < len(b); i++ {
|
||||||
|
c := b[i]
|
||||||
|
|
||||||
|
if !jsIsSpecial(rune(c)) {
|
||||||
|
// fast path: nothing to do
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.Write(b[last:i])
|
||||||
|
|
||||||
|
if c < utf8.RuneSelf {
|
||||||
|
// Quotes, slashes and angle brackets get quoted.
|
||||||
|
// Control characters get written as \u00XX.
|
||||||
|
switch c {
|
||||||
|
case '\\':
|
||||||
|
w.Write(jsBackslash)
|
||||||
|
case '\'':
|
||||||
|
w.Write(jsApos)
|
||||||
|
case '"':
|
||||||
|
w.Write(jsQuot)
|
||||||
|
case '<':
|
||||||
|
w.Write(jsLt)
|
||||||
|
case '>':
|
||||||
|
w.Write(jsGt)
|
||||||
|
default:
|
||||||
|
w.Write(jsLowUni)
|
||||||
|
t, b := c>>4, c&0x0f
|
||||||
|
w.Write(hex[t : t+1])
|
||||||
|
w.Write(hex[b : b+1])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unicode rune.
|
||||||
|
r, size := utf8.DecodeRune(b[i:])
|
||||||
|
if unicode.IsPrint(r) {
|
||||||
|
w.Write(b[i : i+size])
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "\\u%04X", r)
|
||||||
|
}
|
||||||
|
i += size - 1
|
||||||
|
}
|
||||||
|
last = i + 1
|
||||||
|
}
|
||||||
|
w.Write(b[last:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
|
||||||
|
func JSEscapeString(s string) string {
|
||||||
|
// Avoid allocation if we can.
|
||||||
|
if strings.IndexFunc(s, jsIsSpecial) < 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
JSEscape(&b, []byte(s))
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsIsSpecial(r rune) bool {
|
||||||
|
switch r {
|
||||||
|
case '\\', '\'', '"', '<', '>':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return r < ' ' || utf8.RuneSelf <= r
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSEscaper returns the escaped JavaScript equivalent of the textual
|
||||||
|
// representation of its arguments.
|
||||||
|
func JSEscaper(args ...interface{}) string {
|
||||||
|
return JSEscapeString(evalArgs(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLQueryEscaper returns the escaped value of the textual representation of
|
||||||
|
// its arguments in a form suitable for embedding in a URL query.
|
||||||
|
func URLQueryEscaper(args ...interface{}) string {
|
||||||
|
return url.QueryEscape(evalArgs(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalArgs formats the list of arguments into a string. It is therefore equivalent to
|
||||||
|
// fmt.Sprint(args...)
|
||||||
|
// except that each argument is indirected (if a pointer), as required,
|
||||||
|
// using the same rules as the default string evaluation during template
|
||||||
|
// execution.
|
||||||
|
func evalArgs(args []interface{}) string {
|
||||||
|
ok := false
|
||||||
|
var s string
|
||||||
|
// Fast path for simple common case.
|
||||||
|
if len(args) == 1 {
|
||||||
|
s, ok = args[0].(string)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
for i, arg := range args {
|
||||||
|
a, ok := printableValue(reflect.ValueOf(arg))
|
||||||
|
if ok {
|
||||||
|
args[i] = a
|
||||||
|
} // else left fmt do its thing
|
||||||
|
}
|
||||||
|
s = fmt.Sprint(args...)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue