Commit c6950d2c authored by Brad Davidson's avatar Brad Davidson Committed by Brad Davidson

Update Kubernetes to v1.20.0-k3s1

parent cd27c6fc

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

...@@ -16,8 +16,8 @@ replace ( ...@@ -16,8 +16,8 @@ replace (
github.com/containerd/typeurl => github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd github.com/containerd/typeurl => github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
github.com/coreos/flannel => github.com/k3s-io/flannel v0.12.0-k3s2 github.com/coreos/flannel => github.com/k3s-io/flannel v0.12.0-k3s2
github.com/coreos/go-systemd => github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/coreos/go-systemd => github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
github.com/docker/distribution => github.com/docker/distribution v0.0.0-20190205005809-0d3efadf0154 github.com/docker/distribution => github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
github.com/docker/docker => github.com/docker/docker v17.12.0-ce-rc1.0.20190219214528-cbe11bdc6da8+incompatible github.com/docker/docker => github.com/docker/docker v17.12.0-ce-rc1.0.20200310163718-4634ce647cf2+incompatible
github.com/docker/libnetwork => github.com/docker/libnetwork v0.8.0-dev.2.0.20190624125649-f0e46a78ea34 github.com/docker/libnetwork => github.com/docker/libnetwork v0.8.0-dev.2.0.20190624125649-f0e46a78ea34
github.com/golang/protobuf => github.com/golang/protobuf v1.3.5 github.com/golang/protobuf => github.com/golang/protobuf v1.3.5
github.com/juju/errors => github.com/k3s-io/nocode v0.0.0-20200630202308-cb097102c09f github.com/juju/errors => github.com/k3s-io/nocode v0.0.0-20200630202308-cb097102c09f
...@@ -26,44 +26,46 @@ replace ( ...@@ -26,44 +26,46 @@ replace (
github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.0-rc92 github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.0-rc92
github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6
go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea go.etcd.io/etcd => github.com/k3s-io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
golang.org/x/crypto => golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/crypto => golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
golang.org/x/net => golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 golang.org/x/net => golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
golang.org/x/sys => golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 golang.org/x/sys => golang.org/x/sys v0.0.0-20201112073958-5cba982894dd
google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63
google.golang.org/grpc => google.golang.org/grpc v1.26.0 google.golang.org/grpc => google.golang.org/grpc v1.27.1
gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.2.2
k8s.io/api => github.com/k3s-io/kubernetes/staging/src/k8s.io/api v1.19.4-k3s1 k8s.io/api => github.com/brandond/kubernetes/staging/src/k8s.io/api v1.20.0-k3s1
k8s.io/apiextensions-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.19.4-k3s1 k8s.io/apiextensions-apiserver => github.com/brandond/kubernetes/staging/src/k8s.io/apiextensions-apiserver v1.20.0-k3s1
k8s.io/apimachinery => github.com/k3s-io/kubernetes/staging/src/k8s.io/apimachinery v1.19.4-k3s1 k8s.io/apimachinery => github.com/brandond/kubernetes/staging/src/k8s.io/apimachinery v1.20.0-k3s1
k8s.io/apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/apiserver v1.19.4-k3s1 k8s.io/apiserver => github.com/brandond/kubernetes/staging/src/k8s.io/apiserver v1.20.0-k3s1
k8s.io/cli-runtime => github.com/k3s-io/kubernetes/staging/src/k8s.io/cli-runtime v1.19.4-k3s1 k8s.io/cli-runtime => github.com/brandond/kubernetes/staging/src/k8s.io/cli-runtime v1.20.0-k3s1
k8s.io/client-go => github.com/k3s-io/kubernetes/staging/src/k8s.io/client-go v1.19.4-k3s1 k8s.io/client-go => github.com/brandond/kubernetes/staging/src/k8s.io/client-go v1.20.0-k3s1
k8s.io/cloud-provider => github.com/k3s-io/kubernetes/staging/src/k8s.io/cloud-provider v1.19.4-k3s1 k8s.io/cloud-provider => github.com/brandond/kubernetes/staging/src/k8s.io/cloud-provider v1.20.0-k3s1
k8s.io/cluster-bootstrap => github.com/k3s-io/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.19.4-k3s1 k8s.io/cluster-bootstrap => github.com/brandond/kubernetes/staging/src/k8s.io/cluster-bootstrap v1.20.0-k3s1
k8s.io/code-generator => github.com/k3s-io/kubernetes/staging/src/k8s.io/code-generator v1.19.4-k3s1 k8s.io/code-generator => github.com/brandond/kubernetes/staging/src/k8s.io/code-generator v1.20.0-k3s1
k8s.io/component-base => github.com/k3s-io/kubernetes/staging/src/k8s.io/component-base v1.19.4-k3s1 k8s.io/component-base => github.com/brandond/kubernetes/staging/src/k8s.io/component-base v1.20.0-k3s1
k8s.io/cri-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/cri-api v1.19.4-k3s1 k8s.io/component-helpers => github.com/brandond/kubernetes/staging/src/k8s.io/component-helpers v1.20.0-k3s1
k8s.io/csi-translation-lib => github.com/k3s-io/kubernetes/staging/src/k8s.io/csi-translation-lib v1.19.4-k3s1 k8s.io/controller-manager => github.com/brandond/kubernetes/staging/src/k8s.io/controller-manager v1.20.0-k3s1
k8s.io/kube-aggregator => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-aggregator v1.19.4-k3s1 k8s.io/cri-api => github.com/brandond/kubernetes/staging/src/k8s.io/cri-api v1.20.0-k3s1
k8s.io/kube-controller-manager => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-controller-manager v1.19.4-k3s1 k8s.io/csi-translation-lib => github.com/brandond/kubernetes/staging/src/k8s.io/csi-translation-lib v1.20.0-k3s1
k8s.io/kube-proxy => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-proxy v1.19.4-k3s1 k8s.io/kube-aggregator => github.com/brandond/kubernetes/staging/src/k8s.io/kube-aggregator v1.20.0-k3s1
k8s.io/kube-scheduler => github.com/k3s-io/kubernetes/staging/src/k8s.io/kube-scheduler v1.19.4-k3s1 k8s.io/kube-controller-manager => github.com/brandond/kubernetes/staging/src/k8s.io/kube-controller-manager v1.20.0-k3s1
k8s.io/kubectl => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubectl v1.19.4-k3s1 k8s.io/kube-proxy => github.com/brandond/kubernetes/staging/src/k8s.io/kube-proxy v1.20.0-k3s1
k8s.io/kubelet => github.com/k3s-io/kubernetes/staging/src/k8s.io/kubelet v1.19.4-k3s1 k8s.io/kube-scheduler => github.com/brandond/kubernetes/staging/src/k8s.io/kube-scheduler v1.20.0-k3s1
k8s.io/kubernetes => github.com/k3s-io/kubernetes v1.19.4-k3s1 k8s.io/kubectl => github.com/brandond/kubernetes/staging/src/k8s.io/kubectl v1.20.0-k3s1
k8s.io/legacy-cloud-providers => github.com/k3s-io/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.19.4-k3s1 k8s.io/kubelet => github.com/brandond/kubernetes/staging/src/k8s.io/kubelet v1.20.0-k3s1
k8s.io/metrics => github.com/k3s-io/kubernetes/staging/src/k8s.io/metrics v1.19.4-k3s1 k8s.io/kubernetes => github.com/brandond/kubernetes v1.20.0-k3s1
k8s.io/node-api => github.com/k3s-io/kubernetes/staging/src/k8s.io/node-api v1.19.4-k3s1 k8s.io/legacy-cloud-providers => github.com/brandond/kubernetes/staging/src/k8s.io/legacy-cloud-providers v1.20.0-k3s1
k8s.io/sample-apiserver => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-apiserver v1.19.4-k3s1 k8s.io/metrics => github.com/brandond/kubernetes/staging/src/k8s.io/metrics v1.20.0-k3s1
k8s.io/sample-cli-plugin => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.19.4-k3s1 k8s.io/mount-utils => github.com/brandond/kubernetes/staging/src/k8s.io/mount-utils v1.20.0-k3s1
k8s.io/sample-controller => github.com/k3s-io/kubernetes/staging/src/k8s.io/sample-controller v1.19.4-k3s1 k8s.io/node-api => github.com/brandond/kubernetes/staging/src/k8s.io/node-api v1.20.0-k3s1
k8s.io/sample-apiserver => github.com/brandond/kubernetes/staging/src/k8s.io/sample-apiserver v1.20.0-k3s1
k8s.io/sample-cli-plugin => github.com/brandond/kubernetes/staging/src/k8s.io/sample-cli-plugin v1.20.0-k3s1
k8s.io/sample-controller => github.com/brandond/kubernetes/staging/src/k8s.io/sample-controller v1.20.0-k3s1
mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 mvdan.cc/unparam => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34
) )
require ( require (
github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0 github.com/AkihiroSuda/containerd-fuse-overlayfs v1.0.0
github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340 github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340
github.com/containerd/containerd v1.4.1 github.com/containerd/containerd v1.4.1
...@@ -71,15 +73,15 @@ require ( ...@@ -71,15 +73,15 @@ require (
github.com/coreos/flannel v0.12.0 github.com/coreos/flannel v0.12.0
github.com/coreos/go-iptables v0.4.5 github.com/coreos/go-iptables v0.4.5
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
github.com/docker/docker v17.12.0-ce-rc1.0.20200821074627-7ae5222c72cc+incompatible github.com/docker/docker v17.12.0-ce-rc1.0.20200916142827-bd33bbf0497b+incompatible
github.com/erikdubbelboer/gspt v0.0.0-20190125194910-e68493906b83 github.com/erikdubbelboer/gspt v0.0.0-20190125194910-e68493906b83
github.com/frankban/quicktest v1.10.2 // indirect github.com/frankban/quicktest v1.10.2 // indirect
github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/go-bindata/go-bindata v3.1.2+incompatible
github.com/go-sql-driver/mysql v1.4.1 github.com/go-sql-driver/mysql v1.4.1
github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.2
github.com/gorilla/mux v1.7.4 github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.1 github.com/gorilla/websocket v1.4.2
github.com/k3s-io/helm-controller v0.8.0 github.com/k3s-io/helm-controller v0.8.0
github.com/k3s-io/kine v0.6.0 github.com/k3s-io/kine v0.6.0
github.com/kubernetes-sigs/cri-tools v0.0.0-00010101000000-000000000000 github.com/kubernetes-sigs/cri-tools v0.0.0-00010101000000-000000000000
...@@ -101,10 +103,10 @@ require ( ...@@ -101,10 +103,10 @@ require (
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
github.com/tchap/go-patricia v2.3.0+incompatible // indirect github.com/tchap/go-patricia v2.3.0+incompatible // indirect
github.com/urfave/cli v1.22.2 github.com/urfave/cli v1.22.2
go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5 go.etcd.io/etcd v0.5.0-alpha.5.0.20201208200253-50621aee4aea
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
golang.org/x/net v0.0.0-20200822124328-c89045814202 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd
google.golang.org/grpc v1.31.1 google.golang.org/grpc v1.31.1
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
k8s.io/api v0.19.0 k8s.io/api v0.19.0
...@@ -113,8 +115,10 @@ require ( ...@@ -113,8 +115,10 @@ require (
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible
k8s.io/cloud-provider v0.0.0 k8s.io/cloud-provider v0.0.0
k8s.io/component-base v0.19.0 k8s.io/component-base v0.19.0
k8s.io/controller-manager v0.0.0
k8s.io/cri-api v0.19.0 k8s.io/cri-api v0.19.0
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/kubernetes v1.19.4 k8s.io/kubectl v0.0.0
k8s.io/kubernetes v1.20.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
"k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/net"
kubeapiserverflag "k8s.io/component-base/cli/flag" kubeapiserverflag "k8s.io/component-base/cli/flag"
"k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/controlplane"
_ "github.com/go-sql-driver/mysql" // ensure we have mysql _ "github.com/go-sql-driver/mysql" // ensure we have mysql
_ "github.com/lib/pq" // ensure we have postgres _ "github.com/lib/pq" // ensure we have postgres
...@@ -152,7 +152,7 @@ func run(app *cli.Context, cfg *cmds.Server) error { ...@@ -152,7 +152,7 @@ func run(app *cli.Context, cfg *cmds.Server) error {
return errors.Wrapf(err, "Invalid CIDR %s: %v", cfg.ServiceCIDR, err) return errors.Wrapf(err, "Invalid CIDR %s: %v", cfg.ServiceCIDR, err)
} }
_, apiServerServiceIP, err := master.ServiceIPRange(*serverConfig.ControlConfig.ServiceIPRange) _, apiServerServiceIP, err := controlplane.ServiceIPRange(*serverConfig.ControlConfig.ServiceIPRange)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -37,10 +37,12 @@ import ( ...@@ -37,10 +37,12 @@ import (
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
ccmapp "k8s.io/kubernetes/cmd/cloud-controller-manager/app" ccm "k8s.io/cloud-provider"
app2 "k8s.io/kubernetes/cmd/controller-manager/app" ccmapp "k8s.io/cloud-provider/app"
ccmopt "k8s.io/cloud-provider/options"
app2 "k8s.io/controller-manager/app"
"k8s.io/kubernetes/pkg/controlplane"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
"k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/proxy/util" "k8s.io/kubernetes/pkg/proxy/util"
// registering k3s cloud provider // registering k3s cloud provider
...@@ -611,7 +613,7 @@ func genServerCerts(config *config.Control, runtime *config.ControlRuntime) erro ...@@ -611,7 +613,7 @@ func genServerCerts(config *config.Control, runtime *config.ControlRuntime) erro
return err return err
} }
_, apiServerServiceIP, err := master.ServiceIPRange(*config.ServiceIPRange) _, apiServerServiceIP, err := controlplane.ServiceIPRange(*config.ServiceIPRange)
if err != nil { if err != nil {
return err return err
} }
...@@ -910,25 +912,57 @@ func expired(certFile string, pool *x509.CertPool) bool { ...@@ -910,25 +912,57 @@ func expired(certFile string, pool *x509.CertPool) bool {
} }
func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *config.ControlRuntime) { func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *config.ControlRuntime) {
// Reference: https://github.com/kubernetes/kubernetes/pull/93764
// The above-linked change made the in-tree cloud-controller-manager command much more flexible
// but much more complicated to wrap. It now validates some of the configuration very early on, before
// the CLI args are parsed, so some of the configuration needs to be hardcoded instead of set via flags.
argsMap := map[string]string{ argsMap := map[string]string{
"kubeconfig": runtime.KubeConfigCloudController,
"allocate-node-cidrs": "true",
"configure-cloud-routes": "false",
"controllers": "*,-service,-route",
"cluster-cidr": cfg.ClusterIPRange.String(),
"bind-address": localhostIP.String(),
"secure-port": "0",
"cloud-provider": version.Program,
"node-status-update-frequency": "1m",
"profiling": "false", "profiling": "false",
} }
args := config.GetArgsList(argsMap, cfg.ExtraCloudControllerArgs)
s, err := ccmopt.NewCloudControllerManagerOptions()
if err != nil {
logrus.Fatalf("Unable to initialize cloudcontroller options: %v", err)
}
s.KubeCloudShared.AllocateNodeCIDRs = true
s.KubeCloudShared.CloudProvider.Name = version.Program
s.KubeCloudShared.ClusterCIDR = cfg.ClusterIPRange.String()
s.KubeCloudShared.ConfigureCloudRoutes = false
s.Kubeconfig = runtime.KubeConfigCloudController
s.NodeStatusUpdateFrequency = metav1.Duration{Duration: 1 * time.Minute}
s.SecureServing.BindAddress = localhostIP
s.SecureServing.BindPort = 0
if cfg.NoLeaderElect { if cfg.NoLeaderElect {
argsMap["leader-elect"] = "false" s.Generic.LeaderElection.LeaderElect = false
} }
args := config.GetArgsList(argsMap, cfg.ExtraCloudControllerArgs) c, err := s.Config([]string{}, []string{})
if err != nil {
logrus.Fatalf("Unable to create cloudcontroller config from options: %v", err)
}
cloud, err := ccm.InitCloudProvider(version.Program, runtime.KubeConfigCloudController)
if err != nil {
logrus.Fatalf("Cloud provider could not be initialized: %v", err)
}
if cloud == nil {
logrus.Fatalf("Cloud provider is nil")
}
cloud.Initialize(c.ClientBuilder, make(chan struct{}))
if informerUserCloud, ok := cloud.(ccm.InformerUser); ok {
informerUserCloud.SetInformers(c.SharedInformers)
}
controllerInitializers := ccmapp.DefaultControllerInitializers(c.Complete(), cloud)
delete(controllerInitializers, "service")
delete(controllerInitializers, "route")
command := ccmapp.NewCloudControllerManagerCommand() command := ccmapp.NewCloudControllerManagerCommand(s, c, controllerInitializers)
command.SetArgs(args) command.SetArgs(args)
// register k3s cloud provider // register k3s cloud provider
...@@ -946,8 +980,8 @@ func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *c ...@@ -946,8 +980,8 @@ func cloudControllerManager(ctx context.Context, cfg *config.Control, runtime *c
} }
break break
} }
logrus.Infof("Running cloud-controller-manager %s", config.ArgString(args)) logrus.Infof("Running cloud-controller-manager for provider %v with args %v", cloud.ProviderName(), config.ArgString(args))
logrus.Fatalf("cloud-controller-manager exited: %v", command.Execute()) logrus.Fatalf("cloud-controller-manager exited: %v", command.ExecuteContext(ctx))
}() }()
} }
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
utilflag "k8s.io/component-base/cli/flag" utilflag "k8s.io/component-base/cli/flag"
"k8s.io/component-base/logs" "k8s.io/component-base/logs"
"k8s.io/kubernetes/pkg/kubectl/cmd" "k8s.io/kubectl/pkg/cmd"
) )
func Main() { func Main() {
......
{
"name": "metadata",
"name_pretty": "Google Compute Engine Metadata API",
"product_documentation": "https://cloud.google.com/compute/docs/storing-retrieving-metadata",
"client_documentation": "https://godoc.org/cloud.google.com/go/compute/metadata",
"release_level": "ga",
"language": "go",
"repo": "googleapis/google-cloud-go",
"distribution_name": "cloud.google.com/go/compute/metadata",
"api_id": "compute:metadata",
"requires_billing": false
}
...@@ -61,25 +61,14 @@ var ( ...@@ -61,25 +61,14 @@ var (
instID = &cachedValue{k: "instance/id", trim: true} instID = &cachedValue{k: "instance/id", trim: true}
) )
var ( var defaultClient = &Client{hc: &http.Client{
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{ Transport: &http.Transport{
Dial: (&net.Dialer{ Dial: (&net.Dialer{
Timeout: 2 * time.Second, Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second, KeepAlive: 30 * time.Second,
}).Dial, }).Dial,
}, },
}} }}
)
// NotDefinedError is returned when requested metadata is not defined. // NotDefinedError is returned when requested metadata is not defined.
// //
...@@ -206,10 +195,9 @@ func systemInfoSuggestsGCE() bool { ...@@ -206,10 +195,9 @@ func systemInfoSuggestsGCE() bool {
return name == "Google" || name == "Google Compute Engine" return name == "Google" || name == "Google Compute Engine"
} }
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no // Subscribe calls Client.Subscribe on the default client.
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error { func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn) return defaultClient.Subscribe(suffix, fn)
} }
// Get calls Client.Get on the default client. // Get calls Client.Get on the default client.
...@@ -280,9 +268,14 @@ type Client struct { ...@@ -280,9 +268,14 @@ type Client struct {
hc *http.Client hc *http.Client
} }
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests // NewClient returns a Client that can be used to fetch metadata.
// will use the given http.Client instead of the default client. // Returns the client that uses the specified http.Client for HTTP requests.
// If nil is specified, returns the default client.
func NewClient(c *http.Client) *Client { func NewClient(c *http.Client) *Client {
if c == nil {
return defaultClient
}
return &Client{hc: c} return &Client{hc: c}
} }
......
# The standard Go .gitignore file follows. (Sourced from: github.com/github/gitignore/master/Go.gitignore)
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
.DS_Store
.idea/
.vscode/
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
# go-autorest specific
vendor/
autorest/azure/example/example
DIR?=./autorest/
default: build
build: fmt
go install $(DIR)
test:
go test $(DIR) || exit 1
vet:
@echo "go vet ."
@go vet $(DIR)... ; if [ $$? -eq 1 ]; then \
echo ""; \
echo "Vet found suspicious constructs. Please check the reported constructs"; \
echo "and fix them if necessary before submitting the code for review."; \
exit 1; \
fi
fmt:
gofmt -w $(DIR)
.PHONY: build test vet fmt
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:892e39e5c083d0943f1e80ab8351690f183c6a5ab24e1d280adcad424c26255e"
name = "contrib.go.opencensus.io/exporter/ocagent"
packages = ["."]
pruneopts = "UT"
revision = "a8a6f458bbc1d5042322ad1f9b65eeb0b69be9ea"
version = "v0.6.0"
[[projects]]
digest = "1:8f5acd4d4462b5136af644d25101f0968a7a94ee90fcb2059cec5b7cc42e0b20"
name = "github.com/census-instrumentation/opencensus-proto"
packages = [
"gen-go/agent/common/v1",
"gen-go/agent/metrics/v1",
"gen-go/agent/trace/v1",
"gen-go/metrics/v1",
"gen-go/resource/v1",
"gen-go/trace/v1",
]
pruneopts = "UT"
revision = "d89fa54de508111353cb0b06403c00569be780d8"
version = "v0.2.1"
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
pruneopts = "UT"
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
digest = "1:cf0d2e435fd4ce45b789e93ef24b5f08e86be0e9807a16beb3694e2d8c9af965"
name = "github.com/dimchansky/utfbom"
packages = ["."]
pruneopts = "UT"
revision = "d2133a1ce379ef6fa992b0514a77146c60db9d1c"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:b7cb6054d3dff43b38ad2e92492f220f57ae6087ee797dca298139776749ace8"
name = "github.com/golang/groupcache"
packages = ["lru"]
pruneopts = "UT"
revision = "611e8accdfc92c4187d399e95ce826046d4c8d73"
[[projects]]
digest = "1:e3839df32927e8d3403cd5aa7253d966e8ff80fc8f10e2e35d146461cd83fcfa"
name = "github.com/golang/protobuf"
packages = [
"descriptor",
"jsonpb",
"proto",
"protoc-gen-go/descriptor",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/struct",
"ptypes/timestamp",
"ptypes/wrappers",
]
pruneopts = "UT"
revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7"
version = "v1.3.2"
[[projects]]
digest = "1:c560cd79300fac84f124b96225181a637a70b60155919a3c36db50b7cca6b806"
name = "github.com/grpc-ecosystem/grpc-gateway"
packages = [
"internal",
"runtime",
"utilities",
]
pruneopts = "UT"
revision = "f7120437bb4f6c71f7f5076ad65a45310de2c009"
version = "v1.12.1"
[[projects]]
digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
pruneopts = "UT"
revision = "af06845cf3004701891bf4fdb884bfe4920b3727"
version = "v1.1.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:99d32780e5238c2621fff621123997c3e3cca96db8be13179013aea77dfab551"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require",
]
pruneopts = "UT"
revision = "221dbe5ed46703ee255b1da0dec05086f5035f62"
version = "v1.4.0"
[[projects]]
digest = "1:7c5e00383399fe13de0b4b65c9fdde16275407ce8ac02d867eafeaa916edcc71"
name = "go.opencensus.io"
packages = [
".",
"internal",
"internal/tagencoding",
"metric/metricdata",
"metric/metricproducer",
"plugin/ocgrpc",
"plugin/ochttp",
"plugin/ochttp/propagation/b3",
"plugin/ochttp/propagation/tracecontext",
"resource",
"stats",
"stats/internal",
"stats/view",
"tag",
"trace",
"trace/internal",
"trace/propagation",
"trace/tracestate",
]
pruneopts = "UT"
revision = "aad2c527c5defcf89b5afab7f37274304195a6b2"
version = "v0.22.2"
[[projects]]
branch = "master"
digest = "1:f604f5e2ee721b6757d962dfe7bab4f28aae50c456e39cfb2f3819762a44a6ae"
name = "golang.org/x/crypto"
packages = [
"pkcs12",
"pkcs12/internal/rc2",
]
pruneopts = "UT"
revision = "e9b2fee46413994441b28dfca259d911d963dfed"
[[projects]]
branch = "master"
digest = "1:334b27eac455cb6567ea28cd424230b07b1a64334a2f861a8075ac26ce10af43"
name = "golang.org/x/lint"
packages = [
".",
"golint",
]
pruneopts = "UT"
revision = "fdd1cda4f05fd1fd86124f0ef9ce31a0b72c8448"
[[projects]]
branch = "master"
digest = "1:257a75d024975428ab9192bfc334c3490882f8cb21322ea5784ca8eca000a910"
name = "golang.org/x/net"
packages = [
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"trace",
]
pruneopts = "UT"
revision = "1ddd1de85cb0337b623b740a609d35817d516a8d"
[[projects]]
branch = "master"
digest = "1:382bb5a7fb4034db3b6a2d19e5a4a6bcf52f4750530603c01ca18a172fa3089b"
name = "golang.org/x/sync"
packages = ["semaphore"]
pruneopts = "UT"
revision = "cd5d95a43a6e21273425c7ae415d3df9ea832eeb"
[[projects]]
branch = "master"
digest = "1:4da420ceda5f68e8d748aa2169d0ed44ffadb1bbd6537cf778a49563104189b8"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = "UT"
revision = "ce4227a45e2eb77e5c847278dcc6a626742e2945"
[[projects]]
digest = "1:8d8faad6b12a3a4c819a3f9618cb6ee1fa1cfc33253abeeea8b55336721e3405"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/language",
"internal/language/compact",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = "UT"
revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475"
version = "v0.3.2"
[[projects]]
branch = "master"
digest = "1:4eb5ea8395fb60212dd58b92c9db80bab59d5e99c7435f9a6a0a528c373b60e7"
name = "golang.org/x/tools"
packages = [
"go/ast/astutil",
"go/gcexportdata",
"go/internal/gcimporter",
"go/types/typeutil",
]
pruneopts = "UT"
revision = "259af5ff87bdcd4abf2ecda8edc3f13f04f26a42"
[[projects]]
digest = "1:964bb30febc27fabfbec4759fa530c6ec35e77a7c85fed90b9317ea39a054877"
name = "google.golang.org/api"
packages = ["support/bundler"]
pruneopts = "UT"
revision = "8a410c21381766a810817fd6200fce8838ecb277"
version = "v0.14.0"
[[projects]]
branch = "master"
digest = "1:a8d5c2c6e746b3485e36908ab2a9e3d77b86b81f8156d88403c7d2b462431dfd"
name = "google.golang.org/genproto"
packages = [
"googleapis/api/httpbody",
"googleapis/rpc/status",
"protobuf/field_mask",
]
pruneopts = "UT"
revision = "51378566eb590fa106d1025ea12835a4416dda84"
[[projects]]
digest = "1:b59ce3ddb11daeeccccc9cb3183b58ebf8e9a779f1c853308cd91612e817a301"
name = "google.golang.org/grpc"
packages = [
".",
"backoff",
"balancer",
"balancer/base",
"balancer/roundrobin",
"binarylog/grpc_binarylog_v1",
"codes",
"connectivity",
"credentials",
"credentials/internal",
"encoding",
"encoding/proto",
"grpclog",
"internal",
"internal/backoff",
"internal/balancerload",
"internal/binarylog",
"internal/buffer",
"internal/channelz",
"internal/envconfig",
"internal/grpcrand",
"internal/grpcsync",
"internal/resolver/dns",
"internal/resolver/passthrough",
"internal/syscall",
"internal/transport",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"serviceconfig",
"stats",
"status",
"tap",
]
pruneopts = "UT"
revision = "1a3960e4bd028ac0cec0a2afd27d7d8e67c11514"
version = "v1.25.1"
[[projects]]
digest = "1:b75b3deb2bce8bc079e16bb2aecfe01eb80098f5650f9e93e5643ca8b7b73737"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "1f64d6156d11335c3f22d9330b0ad14fc1e789ce"
version = "v2.2.7"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"contrib.go.opencensus.io/exporter/ocagent",
"github.com/dgrijalva/jwt-go",
"github.com/dimchansky/utfbom",
"github.com/mitchellh/go-homedir",
"github.com/stretchr/testify/require",
"go.opencensus.io/plugin/ochttp",
"go.opencensus.io/plugin/ochttp/propagation/tracecontext",
"go.opencensus.io/stats/view",
"go.opencensus.io/trace",
"golang.org/x/crypto/pkcs12",
"golang.org/x/lint/golint",
]
solver-name = "gps-cdcl"
solver-version = 1
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
required = ["golang.org/x/lint/golint"]
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "contrib.go.opencensus.io/exporter/ocagent"
version = "0.6.0"
[[constraint]]
name = "github.com/dgrijalva/jwt-go"
version = "3.2.0"
[[constraint]]
name = "github.com/dimchansky/utfbom"
version = "1.1.0"
[[constraint]]
name = "github.com/mitchellh/go-homedir"
version = "1.1.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.3.0"
[[constraint]]
name = "go.opencensus.io"
version = "0.22.0"
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
# go-autorest
[![GoDoc](https://godoc.org/github.com/Azure/go-autorest/autorest?status.png)](https://godoc.org/github.com/Azure/go-autorest/autorest)
[![Build Status](https://dev.azure.com/azure-sdk/public/_apis/build/status/go/Azure.go-autorest?branchName=master)](https://dev.azure.com/azure-sdk/public/_build/latest?definitionId=625&branchName=master)
[![Go Report Card](https://goreportcard.com/badge/Azure/go-autorest)](https://goreportcard.com/report/Azure/go-autorest)
Package go-autorest provides an HTTP request client for use with [Autorest](https://github.com/Azure/autorest.go)-generated API client packages.
An authentication client tested with Azure Active Directory (AAD) is also
provided in this repo in the package
`github.com/Azure/go-autorest/autorest/adal`. Despite its name, this package
is maintained only as part of the Azure Go SDK and is not related to other
"ADAL" libraries in [github.com/AzureAD](https://github.com/AzureAD).
## Overview
Package go-autorest implements an HTTP request pipeline suitable for use across
multiple goroutines and provides the shared routines used by packages generated
by [Autorest](https://github.com/Azure/autorest.go).
The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
and Responding. A typical pattern is:
```go
req, err := Prepare(&http.Request{},
token.WithAuthorization())
resp, err := Send(req,
WithLogging(logger),
DoErrorIfStatusCode(http.StatusInternalServerError),
DoCloseIfError(),
DoRetryForAttempts(5, time.Second))
err = Respond(resp,
ByDiscardingBody(),
ByClosing())
```
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
and then pass the data along, pass the data first and then modify the result, or wrap themselves
around passing the data (such as a logger might do). Decorators run in the order provided. For
example, the following:
```go
req, err := Prepare(&http.Request{},
WithBaseURL("https://microsoft.com/"),
WithPath("a"),
WithPath("b"),
WithPath("c"))
```
will set the URL to:
```
https://microsoft.com/a/b/c
```
Preparers and Responders may be shared and re-used (assuming the underlying decorators support
sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
all bound together by means of input / output channels.
Decorators hold their passed state within a closure (such as the path components in the example
above). Be careful to share Preparers and Responders only in a context where such held state
applies. For example, it may not make sense to share a Preparer that applies a query string from a
fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
struct (e.g., `ByUnmarshallingJson`) is likely incorrect.
Errors raised by autorest objects and methods will conform to the `autorest.Error` interface.
See the included examples for more detail. For details on the suggested use of this package by
generated clients, see the Client described below.
## Helpers
### Handling Swagger Dates
The Swagger specification (https://swagger.io) that drives AutoRest
(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure correct
parsing and formatting.
### Handling Empty Values
In JSON, missing values have different semantics than empty values. This is especially true for
services using the HTTP PATCH verb. The JSON submitted with a PATCH request generally contains
only those values to modify. Missing values are to be left unchanged. Developers, then, require a
means to both specify an empty value and to leave the value out of the submitted JSON.
The Go JSON package (`encoding/json`) supports the `omitempty` tag. When specified, it omits
empty values from the rendered JSON. Since Go defines default values for all base types (such as ""
for string and 0 for int) and provides no means to mark a value as actually empty, the JSON package
treats default values as meaning empty, omitting them from the rendered JSON. This means that, using
the Go base types encoded through the default JSON package, it is not possible to create JSON to
clear a value at the server.
The workaround within the Go community is to use pointers to base types in lieu of base types within
structures that map to JSON. For example, instead of a value of type `string`, the workaround uses
`*string`. While this enables distinguishing empty values from those to be unchanged, creating
pointers to a base type (notably constant, in-line values) requires additional variables. This, for
example,
```go
s := struct {
S *string
}{ S: &"foo" }
```
fails, while, this
```go
v := "foo"
s := struct {
S *string
}{ S: &v }
```
succeeds.
To ease using pointers, the subpackage `to` contains helpers that convert to and from pointers for
Go base types which have Swagger analogs. It also provides a helper that converts between
`map[string]string` and `map[string]*string`, enabling the JSON to specify that the value
associated with a key should be cleared. With the helpers, the previous example becomes
```go
s := struct {
S *string
}{ S: to.StringPtr("foo") }
```
## Install
```bash
go get github.com/Azure/go-autorest/autorest
go get github.com/Azure/go-autorest/autorest/azure
go get github.com/Azure/go-autorest/autorest/date
go get github.com/Azure/go-autorest/autorest/to
```
### Using with Go Modules
In [v12.0.1](https://github.com/Azure/go-autorest/pull/386), this repository introduced the following modules.
- autorest/adal
- autorest/azure/auth
- autorest/azure/cli
- autorest/date
- autorest/mocks
- autorest/to
- autorest/validation
- autorest
- logger
- tracing
Tagging cumulative SDK releases as a whole (e.g. `v12.3.0`) is still enabled to support consumers of this repo that have not yet migrated to modules.
## License
See LICENSE file.
-----
This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
see the [Code of Conduct
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
questions or comments.
...@@ -222,6 +222,10 @@ func CheckForUserCompletionWithContext(ctx context.Context, sender Sender, code ...@@ -222,6 +222,10 @@ func CheckForUserCompletionWithContext(ctx context.Context, sender Sender, code
case "code_expired": case "code_expired":
return nil, ErrDeviceCodeExpired return nil, ErrDeviceCodeExpired
default: default:
// return a more meaningful error message if available
if token.ErrorDescription != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, *token.Error, *token.ErrorDescription)
}
return nil, ErrDeviceGeneric return nil, ErrDeviceGeneric
} }
} }
......
...@@ -3,10 +3,10 @@ module github.com/Azure/go-autorest/autorest/adal ...@@ -3,10 +3,10 @@ module github.com/Azure/go-autorest/autorest/adal
go 1.12 go 1.12
require ( require (
github.com/Azure/go-autorest/autorest v0.9.0 github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest/autorest/date v0.2.0 github.com/Azure/go-autorest/autorest/date v0.3.0
github.com/Azure/go-autorest/autorest/mocks v0.3.0 github.com/Azure/go-autorest/autorest/mocks v0.4.1
github.com/Azure/go-autorest/tracing v0.5.0 github.com/Azure/go-autorest/tracing v0.6.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/form3tech-oss/jwt-go v3.2.2+incompatible
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
) )
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
......
...@@ -16,9 +16,9 @@ package adal ...@@ -16,9 +16,9 @@ package adal
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of // This file, and the github.com/Azure/go-autorest import, won't actually become part of
// the resultant binary. // the resultant binary.
// Necessary for safely adding multi-module repo. // Necessary for safely adding multi-module repo.
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository // See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
import _ "github.com/Azure/go-autorest/autorest" import _ "github.com/Azure/go-autorest"
...@@ -15,11 +15,24 @@ package adal ...@@ -15,11 +15,24 @@ package adal
// limitations under the License. // limitations under the License.
import ( import (
"crypto/rsa"
"crypto/x509"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"golang.org/x/crypto/pkcs12"
)
var (
// ErrMissingCertificate is returned when no local certificate is found in the provided PFX data.
ErrMissingCertificate = errors.New("adal: certificate missing")
// ErrMissingPrivateKey is returned when no private key is found in the provided PFX data.
ErrMissingPrivateKey = errors.New("adal: private key missing")
) )
// LoadToken restores a Token object from a file located at 'path'. // LoadToken restores a Token object from a file located at 'path'.
...@@ -71,3 +84,52 @@ func SaveToken(path string, mode os.FileMode, token Token) error { ...@@ -71,3 +84,52 @@ func SaveToken(path string, mode os.FileMode, token Token) error {
} }
return nil return nil
} }
// DecodePfxCertificateData extracts the x509 certificate and RSA private key from the provided PFX data.
// The PFX data must contain a private key along with a certificate whose public key matches that of the
// private key or an error is returned.
// If the private key is not password protected pass the empty string for password.
func DecodePfxCertificateData(pfxData []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
blocks, err := pkcs12.ToPEM(pfxData, password)
if err != nil {
return nil, nil, err
}
// first extract the private key
var priv *rsa.PrivateKey
for _, block := range blocks {
if block.Type == "PRIVATE KEY" {
priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, nil, err
}
break
}
}
if priv == nil {
return nil, nil, ErrMissingPrivateKey
}
// now find the certificate with the matching public key of our private key
var cert *x509.Certificate
for _, block := range blocks {
if block.Type == "CERTIFICATE" {
pcert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, nil, err
}
certKey, ok := pcert.PublicKey.(*rsa.PublicKey)
if !ok {
// keep looking
continue
}
if priv.E == certKey.E && priv.N.Cmp(certKey.N) == 0 {
// found a match
cert = pcert
break
}
}
}
if cert == nil {
return nil, nil, ErrMissingCertificate
}
return cert, priv, nil
}
...@@ -35,7 +35,7 @@ import ( ...@@ -35,7 +35,7 @@ import (
"time" "time"
"github.com/Azure/go-autorest/autorest/date" "github.com/Azure/go-autorest/autorest/date"
"github.com/dgrijalva/jwt-go" "github.com/form3tech-oss/jwt-go"
) )
const ( const (
...@@ -62,6 +62,9 @@ const ( ...@@ -62,6 +62,9 @@ const (
// msiEndpoint is the well known endpoint for getting MSI authentications tokens // msiEndpoint is the well known endpoint for getting MSI authentications tokens
msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token" msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
// the API version to use for the MSI endpoint
msiAPIVersion = "2018-02-01"
// the default number of attempts to refresh an MSI authentication token // the default number of attempts to refresh an MSI authentication token
defaultMaxMSIRefreshAttempts = 5 defaultMaxMSIRefreshAttempts = 5
...@@ -70,6 +73,9 @@ const ( ...@@ -70,6 +73,9 @@ const (
// asMSISecretEnv is the environment variable used to store the request secret on App Service and Functions // asMSISecretEnv is the environment variable used to store the request secret on App Service and Functions
asMSISecretEnv = "MSI_SECRET" asMSISecretEnv = "MSI_SECRET"
// the API version to use for the App Service MSI endpoint
appServiceAPIVersion = "2017-09-01"
) )
// OAuthTokenProvider is an interface which should be implemented by an access token retriever // OAuthTokenProvider is an interface which should be implemented by an access token retriever
...@@ -354,6 +360,7 @@ type ServicePrincipalToken struct { ...@@ -354,6 +360,7 @@ type ServicePrincipalToken struct {
customRefreshFunc TokenRefresh customRefreshFunc TokenRefresh
refreshCallbacks []TokenRefreshCallback refreshCallbacks []TokenRefreshCallback
// MaxMSIRefreshAttempts is the maximum number of attempts to refresh an MSI token. // MaxMSIRefreshAttempts is the maximum number of attempts to refresh an MSI token.
// Settings this to a value less than 1 will use the default value.
MaxMSIRefreshAttempts int MaxMSIRefreshAttempts int
} }
...@@ -650,6 +657,8 @@ func GetMSIVMEndpoint() (string, error) { ...@@ -650,6 +657,8 @@ func GetMSIVMEndpoint() (string, error) {
return msiEndpoint, nil return msiEndpoint, nil
} }
// NOTE: this only indicates if the ASE environment credentials have been set
// which does not necessarily mean that the caller is authenticating via ASE!
func isAppService() bool { func isAppService() bool {
_, asMSIEndpointEnvExists := os.LookupEnv(asMSIEndpointEnv) _, asMSIEndpointEnvExists := os.LookupEnv(asMSIEndpointEnv)
_, asMSISecretEnvExists := os.LookupEnv(asMSISecretEnv) _, asMSISecretEnvExists := os.LookupEnv(asMSISecretEnv)
...@@ -678,16 +687,22 @@ func GetMSIEndpoint() (string, error) { ...@@ -678,16 +687,22 @@ func GetMSIEndpoint() (string, error) {
// NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension. // NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension.
// It will use the system assigned identity when creating the token. // It will use the system assigned identity when creating the token.
func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, callbacks...) return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, nil, callbacks...)
} }
// NewServicePrincipalTokenFromMSIWithUserAssignedID creates a ServicePrincipalToken via the MSI VM Extension. // NewServicePrincipalTokenFromMSIWithUserAssignedID creates a ServicePrincipalToken via the MSI VM Extension.
// It will use the specified user assigned identity when creating the token. // It will use the clientID of specified user assigned identity when creating the token.
func NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, resource string, userAssignedID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { func NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, resource string, userAssignedID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, &userAssignedID, callbacks...) return newServicePrincipalTokenFromMSI(msiEndpoint, resource, &userAssignedID, nil, callbacks...)
}
// NewServicePrincipalTokenFromMSIWithIdentityResourceID creates a ServicePrincipalToken via the MSI VM Extension.
// It will use the azure resource id of user assigned identity when creating the token.
func NewServicePrincipalTokenFromMSIWithIdentityResourceID(msiEndpoint, resource string, identityResourceID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, &identityResourceID, callbacks...)
} }
func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedID *string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedID *string, identityResourceID *string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
if err := validateStringParam(msiEndpoint, "msiEndpoint"); err != nil { if err := validateStringParam(msiEndpoint, "msiEndpoint"); err != nil {
return nil, err return nil, err
} }
...@@ -699,6 +714,11 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI ...@@ -699,6 +714,11 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
return nil, err return nil, err
} }
} }
if identityResourceID != nil {
if err := validateStringParam(*identityResourceID, "identityResourceID"); err != nil {
return nil, err
}
}
// We set the oauth config token endpoint to be MSI's endpoint // We set the oauth config token endpoint to be MSI's endpoint
msiEndpointURL, err := url.Parse(msiEndpoint) msiEndpointURL, err := url.Parse(msiEndpoint)
if err != nil { if err != nil {
...@@ -709,13 +729,16 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI ...@@ -709,13 +729,16 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
v.Set("resource", resource) v.Set("resource", resource)
// App Service MSI currently only supports token API version 2017-09-01 // App Service MSI currently only supports token API version 2017-09-01
if isAppService() { if isAppService() {
v.Set("api-version", "2017-09-01") v.Set("api-version", appServiceAPIVersion)
} else { } else {
v.Set("api-version", "2018-02-01") v.Set("api-version", msiAPIVersion)
} }
if userAssignedID != nil { if userAssignedID != nil {
v.Set("client_id", *userAssignedID) v.Set("client_id", *userAssignedID)
} }
if identityResourceID != nil {
v.Set("mi_res_id", *identityResourceID)
}
msiEndpointURL.RawQuery = v.Encode() msiEndpointURL.RawQuery = v.Encode()
spt := &ServicePrincipalToken{ spt := &ServicePrincipalToken{
...@@ -771,8 +794,9 @@ func (spt *ServicePrincipalToken) EnsureFresh() error { ...@@ -771,8 +794,9 @@ func (spt *ServicePrincipalToken) EnsureFresh() error {
// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by // EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by
// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use. // RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error { func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error {
if spt.inner.AutoRefresh && spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) { // must take the read lock when initially checking the token's expiration
// take the write lock then check to see if the token was already refreshed if spt.inner.AutoRefresh && spt.Token().WillExpireIn(spt.inner.RefreshWithin) {
// take the write lock then check again to see if the token was already refreshed
spt.refreshLock.Lock() spt.refreshLock.Lock()
defer spt.refreshLock.Unlock() defer spt.refreshLock.Unlock()
if spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) { if spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) {
...@@ -835,11 +859,28 @@ func (spt *ServicePrincipalToken) getGrantType() string { ...@@ -835,11 +859,28 @@ func (spt *ServicePrincipalToken) getGrantType() string {
} }
func isIMDS(u url.URL) bool { func isIMDS(u url.URL) bool {
imds, err := url.Parse(msiEndpoint) return isMSIEndpoint(u) == true || isASEEndpoint(u) == true
}
func isMSIEndpoint(endpoint url.URL) bool {
msi, err := url.Parse(msiEndpoint)
if err != nil { if err != nil {
return false return false
} }
return (u.Host == imds.Host && u.Path == imds.Path) || isAppService() return endpoint.Host == msi.Host && endpoint.Path == msi.Path
}
func isASEEndpoint(endpoint url.URL) bool {
aseEndpoint, err := GetMSIAppServiceEndpoint()
if err != nil {
// app service environment isn't enabled
return false
}
ase, err := url.Parse(aseEndpoint)
if err != nil {
return false
}
return endpoint.Host == ase.Host && endpoint.Path == ase.Path
} }
func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error { func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error {
...@@ -858,7 +899,7 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource ...@@ -858,7 +899,7 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
} }
req.Header.Add("User-Agent", UserAgent()) req.Header.Add("User-Agent", UserAgent())
// Add header when runtime is on App Service or Functions // Add header when runtime is on App Service or Functions
if isAppService() { if isASEEndpoint(spt.inner.OauthConfig.TokenEndpoint) {
asMSISecret, _ := os.LookupEnv(asMSISecretEnv) asMSISecret, _ := os.LookupEnv(asMSISecretEnv)
req.Header.Add("Secret", asMSISecret) req.Header.Add("Secret", asMSISecret)
} }
...@@ -900,6 +941,14 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource ...@@ -900,6 +941,14 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
} }
var resp *http.Response var resp *http.Response
if isMSIEndpoint(spt.inner.OauthConfig.TokenEndpoint) {
resp, err = getMSIEndpoint(ctx, spt.sender)
if err != nil {
// return a TokenRefreshError here so that we don't keep retrying
return newTokenRefreshError(fmt.Sprintf("the MSI endpoint is not available. Failed HTTP request to MSI endpoint: %v", err), nil)
}
resp.Body.Close()
}
if isIMDS(spt.inner.OauthConfig.TokenEndpoint) { if isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts) resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts)
} else { } else {
...@@ -972,6 +1021,11 @@ func retryForIMDS(sender Sender, req *http.Request, maxAttempts int) (resp *http ...@@ -972,6 +1021,11 @@ func retryForIMDS(sender Sender, req *http.Request, maxAttempts int) (resp *http
attempt := 0 attempt := 0
delay := time.Duration(0) delay := time.Duration(0)
// maxAttempts is user-specified, ensure that its value is greater than zero else no request will be made
if maxAttempts < 1 {
maxAttempts = defaultMaxMSIRefreshAttempts
}
for attempt < maxAttempts { for attempt < maxAttempts {
if resp != nil && resp.Body != nil { if resp != nil && resp.Body != nil {
io.Copy(ioutil.Discard, resp.Body) io.Copy(ioutil.Discard, resp.Body)
...@@ -1133,3 +1187,12 @@ func NewMultiTenantServicePrincipalToken(multiTenantCfg MultiTenantOAuthConfig, ...@@ -1133,3 +1187,12 @@ func NewMultiTenantServicePrincipalToken(multiTenantCfg MultiTenantOAuthConfig,
} }
return &m, nil return &m, nil
} }
// MSIAvailable returns true if the MSI endpoint is available for authentication.
func MSIAvailable(ctx context.Context, sender Sender) bool {
resp, err := getMSIEndpoint(ctx, sender)
if err == nil {
resp.Body.Close()
}
return err == nil
}
// +build go1.13
// Copyright 2017 Microsoft Corporation
//
// 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 adal
import (
"context"
"net/http"
"time"
)
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
// this cannot fail, the return sig is due to legacy reasons
msiEndpoint, _ := GetMSIVMEndpoint()
tempCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
// http.NewRequestWithContext() was added in Go 1.13
req, _ := http.NewRequestWithContext(tempCtx, http.MethodGet, msiEndpoint, nil)
q := req.URL.Query()
q.Add("api-version", msiAPIVersion)
req.URL.RawQuery = q.Encode()
return sender.Do(req)
}
// +build !go1.13
// Copyright 2017 Microsoft Corporation
//
// 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 adal
import (
"context"
"net/http"
"time"
)
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
// this cannot fail, the return sig is due to legacy reasons
msiEndpoint, _ := GetMSIVMEndpoint()
tempCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
req, _ := http.NewRequest(http.MethodGet, msiEndpoint, nil)
req = req.WithContext(tempCtx)
q := req.URL.Query()
q.Add("api-version", msiAPIVersion)
req.URL.RawQuery = q.Encode()
return sender.Do(req)
}
...@@ -138,6 +138,11 @@ func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator { ...@@ -138,6 +138,11 @@ func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
} }
} }
// TokenProvider returns OAuthTokenProvider so that it can be used for authorization outside the REST.
func (ba *BearerAuthorizer) TokenProvider() adal.OAuthTokenProvider {
return ba.tokenProvider
}
// BearerAuthorizerCallbackFunc is the authentication callback signature. // BearerAuthorizerCallbackFunc is the authentication callback signature.
type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error) type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
...@@ -331,7 +336,7 @@ func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator { ...@@ -331,7 +336,7 @@ func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
for i := range auxTokens { for i := range auxTokens {
auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i]) auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
} }
return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, "; "))) return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, ", ")))
}) })
} }
} }
...@@ -167,7 +167,13 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien ...@@ -167,7 +167,13 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien
cancelCtx, cancel = context.WithTimeout(ctx, d) cancelCtx, cancel = context.WithTimeout(ctx, d)
defer cancel() defer cancel()
} }
// if the initial response has a Retry-After, sleep for the specified amount of time before starting to poll
if delay, ok := f.GetPollingDelay(); ok {
if delayElapsed := autorest.DelayForBackoff(delay, 0, cancelCtx.Done()); !delayElapsed {
err = cancelCtx.Err()
return
}
}
done, err := f.DoneWithContext(ctx, client) done, err := f.DoneWithContext(ctx, client)
for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) { for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) {
if attempts >= client.RetryAttempts { if attempts >= client.RetryAttempts {
......
...@@ -127,7 +127,7 @@ var ( ...@@ -127,7 +127,7 @@ var (
KeyVaultDNSSuffix: "vault.usgovcloudapi.net", KeyVaultDNSSuffix: "vault.usgovcloudapi.net",
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net", ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
ServiceManagementVMDNSSuffix: "usgovcloudapp.net", ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us", ResourceManagerVMDNSSuffix: "cloudapp.usgovcloudapi.net",
ContainerRegistryDNSSuffix: "azurecr.us", ContainerRegistryDNSSuffix: "azurecr.us",
CosmosDBDNSSuffix: "documents.azure.us", CosmosDBDNSSuffix: "documents.azure.us",
TokenAudience: "https://management.usgovcloudapi.net/", TokenAudience: "https://management.usgovcloudapi.net/",
...@@ -160,7 +160,7 @@ var ( ...@@ -160,7 +160,7 @@ var (
KeyVaultDNSSuffix: "vault.azure.cn", KeyVaultDNSSuffix: "vault.azure.cn",
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn", ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn",
ServiceManagementVMDNSSuffix: "chinacloudapp.cn", ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
ResourceManagerVMDNSSuffix: "cloudapp.azure.cn", ResourceManagerVMDNSSuffix: "cloudapp.chinacloudapi.cn",
ContainerRegistryDNSSuffix: "azurecr.cn", ContainerRegistryDNSSuffix: "azurecr.cn",
CosmosDBDNSSuffix: "documents.azure.cn", CosmosDBDNSSuffix: "documents.azure.cn",
TokenAudience: "https://management.chinacloudapi.cn/", TokenAudience: "https://management.chinacloudapi.cn/",
...@@ -242,3 +242,8 @@ func EnvironmentFromFile(location string) (unmarshaled Environment, err error) { ...@@ -242,3 +242,8 @@ func EnvironmentFromFile(location string) (unmarshaled Environment, err error) {
return return
} }
// SetEnvironment updates the environment map with the specified values.
func SetEnvironment(name string, env Environment) {
environments[strings.ToUpper(name)] = env
}
...@@ -2,4 +2,4 @@ module github.com/Azure/go-autorest/autorest/date ...@@ -2,4 +2,4 @@ module github.com/Azure/go-autorest/autorest/date
go 1.12 go 1.12
require github.com/Azure/go-autorest/autorest v0.9.0 require github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
...@@ -16,9 +16,9 @@ package date ...@@ -16,9 +16,9 @@ package date
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of // This file, and the github.com/Azure/go-autorest import, won't actually become part of
// the resultant binary. // the resultant binary.
// Necessary for safely adding multi-module repo. // Necessary for safely adding multi-module repo.
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository // See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
import _ "github.com/Azure/go-autorest/autorest" import _ "github.com/Azure/go-autorest"
...@@ -3,9 +3,10 @@ module github.com/Azure/go-autorest/autorest ...@@ -3,9 +3,10 @@ module github.com/Azure/go-autorest/autorest
go 1.12 go 1.12
require ( require (
github.com/Azure/go-autorest/autorest/adal v0.8.2 github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest/autorest/mocks v0.3.0 github.com/Azure/go-autorest/autorest/adal v0.9.0
github.com/Azure/go-autorest/logger v0.1.0 github.com/Azure/go-autorest/autorest/mocks v0.4.0
github.com/Azure/go-autorest/tracing v0.5.0 github.com/Azure/go-autorest/logger v0.2.0
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 github.com/Azure/go-autorest/tracing v0.6.0
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
) )
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM=
github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c=
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
......
// +build modhack
package autorest
// Copyright 2017 Microsoft Corporation
//
// 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.
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
// the resultant binary.
// Necessary for safely adding multi-module repo.
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
import _ "github.com/Azure/go-autorest"
...@@ -2,4 +2,4 @@ module github.com/Azure/go-autorest/autorest/mocks ...@@ -2,4 +2,4 @@ module github.com/Azure/go-autorest/autorest/mocks
go 1.12 go 1.12
require github.com/Azure/go-autorest/autorest v0.9.0 require github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
...@@ -16,9 +16,9 @@ package mocks ...@@ -16,9 +16,9 @@ package mocks
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of // This file, and the github.com/Azure/go-autorest import, won't actually become part of
// the resultant binary. // the resultant binary.
// Necessary for safely adding multi-module repo. // Necessary for safely adding multi-module repo.
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository // See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
import _ "github.com/Azure/go-autorest/autorest" import _ "github.com/Azure/go-autorest"
...@@ -131,7 +131,13 @@ func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) { ...@@ -131,7 +131,13 @@ func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) {
} else { } else {
err = c.responses[0].e err = c.responses[0].e
} }
time.Sleep(c.responses[0].d) select {
case <-time.After(c.responses[0].d):
// do nothing
case <-r.Context().Done():
err = r.Context().Err()
return
}
c.repeatResponse[0]-- c.repeatResponse[0]--
if c.repeatResponse[0] == 0 { if c.repeatResponse[0] == 0 {
c.responses = c.responses[1:] c.responses = c.responses[1:]
......
...@@ -257,6 +257,12 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { ...@@ -257,6 +257,12 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
} }
} }
// Count429AsRetry indicates that a 429 response should be included as a retry attempt.
var Count429AsRetry = true
// Max429Delay is the maximum duration to wait between retries on a 429 if no Retry-After header was received.
var Max429Delay time.Duration
// DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified // DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified
// number of attempts, exponentially backing off between requests using the supplied backoff // number of attempts, exponentially backing off between requests using the supplied backoff
// time.Duration (which may be zero). Retrying may be canceled by cancelling the context on the http.Request. // time.Duration (which may be zero). Retrying may be canceled by cancelling the context on the http.Request.
...@@ -264,7 +270,7 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { ...@@ -264,7 +270,7 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator { func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
return func(s Sender) Sender { return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) { return SenderFunc(func(r *http.Request) (*http.Response, error) {
return doRetryForStatusCodesImpl(s, r, false, attempts, backoff, 0, codes...) return doRetryForStatusCodesImpl(s, r, Count429AsRetry, attempts, backoff, 0, codes...)
}) })
} }
} }
...@@ -276,7 +282,7 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se ...@@ -276,7 +282,7 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se
func DoRetryForStatusCodesWithCap(attempts int, backoff, cap time.Duration, codes ...int) SendDecorator { func DoRetryForStatusCodesWithCap(attempts int, backoff, cap time.Duration, codes ...int) SendDecorator {
return func(s Sender) Sender { return func(s Sender) Sender {
return SenderFunc(func(r *http.Request) (*http.Response, error) { return SenderFunc(func(r *http.Request) (*http.Response, error) {
return doRetryForStatusCodesImpl(s, r, true, attempts, backoff, cap, codes...) return doRetryForStatusCodesImpl(s, r, Count429AsRetry, attempts, backoff, cap, codes...)
}) })
} }
} }
...@@ -297,11 +303,10 @@ func doRetryForStatusCodesImpl(s Sender, r *http.Request, count429 bool, attempt ...@@ -297,11 +303,10 @@ func doRetryForStatusCodesImpl(s Sender, r *http.Request, count429 bool, attempt
return resp, err return resp, err
} }
delayed := DelayWithRetryAfter(resp, r.Context().Done()) delayed := DelayWithRetryAfter(resp, r.Context().Done())
// enforce a 2 minute cap between requests when 429 status codes are // if this was a 429 set the delay cap as specified.
// not going to be counted as an attempt and when the cap is 0. // applicable only in the absence of a retry-after header.
// this should only happen in the absence of a retry-after header. if resp != nil && resp.StatusCode == http.StatusTooManyRequests {
if !count429 && cap == 0 { cap = Max429Delay
cap = 2 * time.Minute
} }
if !delayed && !DelayForBackoffWithCap(backoff, cap, delayCount, r.Context().Done()) { if !delayed && !DelayForBackoffWithCap(backoff, cap, delayCount, r.Context().Done()) {
return resp, r.Context().Err() return resp, r.Context().Err()
......
...@@ -19,7 +19,7 @@ import ( ...@@ -19,7 +19,7 @@ import (
"runtime" "runtime"
) )
const number = "v13.4.0" const number = "v14.2.1"
var ( var (
userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s", userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
......
variables:
GOPATH: '$(system.defaultWorkingDirectory)/work'
sdkPath: '$(GOPATH)/src/github.com/$(build.repository.name)'
jobs:
- job: 'goautorest'
displayName: 'Run go-autorest CI Checks'
strategy:
matrix:
Linux_Go113:
vm.image: 'ubuntu-18.04'
go.version: '1.13'
Linux_Go114:
vm.image: 'ubuntu-18.04'
go.version: '1.14'
pool:
vmImage: '$(vm.image)'
steps:
- task: GoTool@0
inputs:
version: '$(go.version)'
displayName: "Select Go Version"
- script: |
set -e
mkdir -p '$(GOPATH)/bin'
mkdir -p '$(sdkPath)'
shopt -s extglob
mv !(work) '$(sdkPath)'
echo '##vso[task.prependpath]$(GOPATH)/bin'
displayName: 'Create Go Workspace'
- script: |
set -e
curl -sSL https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure -v
go install ./vendor/golang.org/x/lint/golint
go get github.com/jstemmer/go-junit-report
go get github.com/axw/gocov/gocov
go get github.com/AlekSi/gocov-xml
go get -u github.com/matm/gocov-html
workingDirectory: '$(sdkPath)'
displayName: 'Install Dependencies'
- script: |
go vet ./autorest/...
go vet ./logger/...
go vet ./tracing/...
workingDirectory: '$(sdkPath)'
displayName: 'Vet'
- script: |
go build -v ./autorest/...
go build -v ./logger/...
go build -v ./tracing/...
workingDirectory: '$(sdkPath)'
displayName: 'Build'
- script: |
set -e
go test -race -v -coverprofile=coverage.txt -covermode atomic ./autorest/... ./logger/... ./tracing/... 2>&1 | go-junit-report > report.xml
gocov convert coverage.txt > coverage.json
gocov-xml < coverage.json > coverage.xml
gocov-html < coverage.json > coverage.html
workingDirectory: '$(sdkPath)'
displayName: 'Run Tests'
- script: grep -L -r --include *.go --exclude-dir vendor -P "Copyright (\d{4}|\(c\)) Microsoft" ./ | tee >&2
workingDirectory: '$(sdkPath)'
displayName: 'Copyright Header Check'
failOnStderr: true
condition: succeededOrFailed()
- script: |
gofmt -s -l -w ./autorest/. >&2
gofmt -s -l -w ./logger/. >&2
gofmt -s -l -w ./tracing/. >&2
workingDirectory: '$(sdkPath)'
displayName: 'Format Check'
failOnStderr: true
condition: succeededOrFailed()
- script: |
golint ./autorest/... >&2
golint ./logger/... >&2
golint ./tracing/... >&2
workingDirectory: '$(sdkPath)'
displayName: 'Linter Check'
failOnStderr: true
condition: succeededOrFailed()
- task: PublishTestResults@2
inputs:
testRunner: JUnit
testResultsFiles: $(sdkPath)/report.xml
failTaskOnFailedTests: true
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: $(sdkPath)/coverage.xml
additionalCodeCoverageFiles: $(sdkPath)/coverage.html
/*
Package go-autorest provides an HTTP request client for use with Autorest-generated API client packages.
*/
package go_autorest
// Copyright 2017 Microsoft Corporation
//
// 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.
module github.com/Azure/go-autorest/logger module github.com/Azure/go-autorest/logger
go 1.12 go 1.12
require github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
// +build modhack
package logger
// Copyright 2017 Microsoft Corporation
//
// 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.
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
// the resultant binary.
// Necessary for safely adding multi-module repo.
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
import _ "github.com/Azure/go-autorest"
module github.com/Azure/go-autorest/tracing module github.com/Azure/go-autorest/tracing
go 1.12 go 1.12
require github.com/Azure/go-autorest v14.2.0+incompatible
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
// +build modhack
package tracing
// Copyright 2017 Microsoft Corporation
//
// 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.
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
// the resultant binary.
// Necessary for safely adding multi-module repo.
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
import _ "github.com/Azure/go-autorest"
...@@ -182,13 +182,14 @@ func (s pipeAddress) String() string { ...@@ -182,13 +182,14 @@ func (s pipeAddress) String() string {
} }
// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout. // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout.
func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) { func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) {
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return syscall.Handle(0), ctx.Err() return syscall.Handle(0), ctx.Err()
default: default:
h, err := createFile(*path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
if err == nil { if err == nil {
return h, nil return h, nil
} }
...@@ -197,7 +198,7 @@ func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) { ...@@ -197,7 +198,7 @@ func tryDialPipe(ctx context.Context, path *string) (syscall.Handle, error) {
} }
// Wait 10 msec and try again. This is a rather simplistic // Wait 10 msec and try again. This is a rather simplistic
// view, as we always try each 10 milliseconds. // view, as we always try each 10 milliseconds.
time.Sleep(time.Millisecond * 10) time.Sleep(10 * time.Millisecond)
} }
} }
} }
...@@ -210,7 +211,7 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { ...@@ -210,7 +211,7 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
if timeout != nil { if timeout != nil {
absTimeout = time.Now().Add(*timeout) absTimeout = time.Now().Add(*timeout)
} else { } else {
absTimeout = time.Now().Add(time.Second * 2) absTimeout = time.Now().Add(2 * time.Second)
} }
ctx, _ := context.WithDeadline(context.Background(), absTimeout) ctx, _ := context.WithDeadline(context.Background(), absTimeout)
conn, err := DialPipeContext(ctx, path) conn, err := DialPipeContext(ctx, path)
...@@ -223,9 +224,15 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { ...@@ -223,9 +224,15 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
// DialPipeContext attempts to connect to a named pipe by `path` until `ctx` // DialPipeContext attempts to connect to a named pipe by `path` until `ctx`
// cancellation or timeout. // cancellation or timeout.
func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { func DialPipeContext(ctx context.Context, path string) (net.Conn, error) {
return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE)
}
// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx`
// cancellation or timeout.
func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) {
var err error var err error
var h syscall.Handle var h syscall.Handle
h, err = tryDialPipe(ctx, &path) h, err = tryDialPipe(ctx, &path, access)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
Copyright (c) 2012, Neal van Veen (nealvanveen@gmail.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
Gotty is a library written in Go that determines and reads termcap database
files to produce an interface for interacting with the capabilities of a
terminal.
See the godoc documentation or the source code for more information about
function usage.
gotty.go:// TODO add more concurrency to name lookup, look for more opportunities.
all:// TODO add more documentation, with function usage in a doc.go file.
all:// TODO add more testing/benchmarking with go test.
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Gotty is a Go-package for reading and parsing the terminfo database
package gotty
// TODO add more concurrency to name lookup, look for more opportunities.
import (
"encoding/binary"
"errors"
"fmt"
"os"
"reflect"
"strings"
"sync"
)
// Open a terminfo file by the name given and construct a TermInfo object.
// If something went wrong reading the terminfo database file, an error is
// returned.
func OpenTermInfo(termName string) (*TermInfo, error) {
var term *TermInfo
var err error
// Find the environment variables
termloc := os.Getenv("TERMINFO")
if len(termloc) == 0 {
// Search like ncurses
locations := []string{os.Getenv("HOME") + "/.terminfo/", "/etc/terminfo/",
"/lib/terminfo/", "/usr/share/terminfo/"}
var path string
for _, str := range locations {
// Construct path
path = str + string(termName[0]) + "/" + termName
// Check if path can be opened
file, _ := os.Open(path)
if file != nil {
// Path can open, fall out and use current path
file.Close()
break
}
}
if len(path) > 0 {
term, err = readTermInfo(path)
} else {
err = errors.New(fmt.Sprintf("No terminfo file(-location) found"))
}
}
return term, err
}
// Open a terminfo file from the environment variable containing the current
// terminal name and construct a TermInfo object. If something went wrong
// reading the terminfo database file, an error is returned.
func OpenTermInfoEnv() (*TermInfo, error) {
termenv := os.Getenv("TERM")
return OpenTermInfo(termenv)
}
// Return an attribute by the name attr provided. If none can be found,
// an error is returned.
func (term *TermInfo) GetAttribute(attr string) (stacker, error) {
// Channel to store the main value in.
var value stacker
// Add a blocking WaitGroup
var block sync.WaitGroup
// Keep track of variable being written.
written := false
// Function to put into goroutine.
f := func(ats interface{}) {
var ok bool
var v stacker
// Switch on type of map to use and assign value to it.
switch reflect.TypeOf(ats).Elem().Kind() {
case reflect.Bool:
v, ok = ats.(map[string]bool)[attr]
case reflect.Int16:
v, ok = ats.(map[string]int16)[attr]
case reflect.String:
v, ok = ats.(map[string]string)[attr]
}
// If ok, a value is found, so we can write.
if ok {
value = v
written = true
}
// Goroutine is done
block.Done()
}
block.Add(3)
// Go for all 3 attribute lists.
go f(term.boolAttributes)
go f(term.numAttributes)
go f(term.strAttributes)
// Wait until every goroutine is done.
block.Wait()
// If a value has been written, return it.
if written {
return value, nil
}
// Otherwise, error.
return nil, fmt.Errorf("Erorr finding attribute")
}
// Return an attribute by the name attr provided. If none can be found,
// an error is returned. A name is first converted to its termcap value.
func (term *TermInfo) GetAttributeName(name string) (stacker, error) {
tc := GetTermcapName(name)
return term.GetAttribute(tc)
}
// A utility function that finds and returns the termcap equivalent of a
// variable name.
func GetTermcapName(name string) string {
// Termcap name
var tc string
// Blocking group
var wait sync.WaitGroup
// Function to put into a goroutine
f := func(attrs []string) {
// Find the string corresponding to the name
for i, s := range attrs {
if s == name {
tc = attrs[i+1]
}
}
// Goroutine is finished
wait.Done()
}
wait.Add(3)
// Go for all 3 attribute lists
go f(BoolAttr[:])
go f(NumAttr[:])
go f(StrAttr[:])
// Wait until every goroutine is done
wait.Wait()
// Return the termcap name
return tc
}
// This function takes a path to a terminfo file and reads it in binary
// form to construct the actual TermInfo file.
func readTermInfo(path string) (*TermInfo, error) {
// Open the terminfo file
file, err := os.Open(path)
defer file.Close()
if err != nil {
return nil, err
}
// magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize
// Header is composed of the magic 0432 octal number, size of the name
// section, size of the boolean section, the amount of number values,
// the number of offsets of strings, and the size of the string section.
var header [6]int16
// Byte array is used to read in byte values
var byteArray []byte
// Short array is used to read in short values
var shArray []int16
// TermInfo object to store values
var term TermInfo
// Read in the header
err = binary.Read(file, binary.LittleEndian, &header)
if err != nil {
return nil, err
}
// If magic number isn't there or isn't correct, we have the wrong filetype
if header[0] != 0432 {
return nil, errors.New(fmt.Sprintf("Wrong filetype"))
}
// Read in the names
byteArray = make([]byte, header[1])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.Names = strings.Split(string(byteArray), "|")
// Read in the booleans
byteArray = make([]byte, header[2])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.boolAttributes = make(map[string]bool)
for i, b := range byteArray {
if b == 1 {
term.boolAttributes[BoolAttr[i*2+1]] = true
}
}
// If the number of bytes read is not even, a byte for alignment is added
if len(byteArray)%2 != 0 {
err = binary.Read(file, binary.LittleEndian, make([]byte, 1))
if err != nil {
return nil, err
}
}
// Read in shorts
shArray = make([]int16, header[3])
err = binary.Read(file, binary.LittleEndian, &shArray)
if err != nil {
return nil, err
}
term.numAttributes = make(map[string]int16)
for i, n := range shArray {
if n != 0377 && n > -1 {
term.numAttributes[NumAttr[i*2+1]] = n
}
}
// Read the offsets into the short array
shArray = make([]int16, header[4])
err = binary.Read(file, binary.LittleEndian, &shArray)
if err != nil {
return nil, err
}
// Read the actual strings in the byte array
byteArray = make([]byte, header[5])
err = binary.Read(file, binary.LittleEndian, &byteArray)
if err != nil {
return nil, err
}
term.strAttributes = make(map[string]string)
// We get an offset, and then iterate until the string is null-terminated
for i, offset := range shArray {
if offset > -1 {
r := offset
for ; byteArray[r] != 0; r++ {
}
term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r])
}
}
return &term, nil
}
// Copyright 2012 Neal van Veen. All rights reserved.
// Usage of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package gotty
type TermInfo struct {
boolAttributes map[string]bool
numAttributes map[string]int16
strAttributes map[string]string
// The various names of the TermInfo file.
Names []string
}
type stacker interface {
}
type stack []stacker
type parser struct {
st stack
parameters []stacker
dynamicVar map[byte]stacker
}
...@@ -43,7 +43,7 @@ type Config struct { ...@@ -43,7 +43,7 @@ type Config struct {
// An optional endpoint URL (hostname only or fully qualified URI) // An optional endpoint URL (hostname only or fully qualified URI)
// that overrides the default generated endpoint for a client. Set this // that overrides the default generated endpoint for a client. Set this
// to `""` to use the default generated endpoint. // to `nil` or the value to `""` to use the default generated endpoint.
// //
// Note: You must still provide a `Region` value when specifying an // Note: You must still provide a `Region` value when specifying an
// endpoint for a client. // endpoint for a client.
...@@ -138,7 +138,7 @@ type Config struct { ...@@ -138,7 +138,7 @@ type Config struct {
// `ExpectContinueTimeout` for information on adjusting the continue wait // `ExpectContinueTimeout` for information on adjusting the continue wait
// timeout. https://golang.org/pkg/net/http/#Transport // timeout. https://golang.org/pkg/net/http/#Transport
// //
// You should use this flag to disble 100-Continue if you experience issues // You should use this flag to disable 100-Continue if you experience issues
// with proxies or third party S3 compatible services. // with proxies or third party S3 compatible services.
S3Disable100Continue *bool S3Disable100Continue *bool
...@@ -183,7 +183,7 @@ type Config struct { ...@@ -183,7 +183,7 @@ type Config struct {
// //
// Example: // Example:
// sess := session.Must(session.NewSession(aws.NewConfig() // sess := session.Must(session.NewSession(aws.NewConfig()
// .WithEC2MetadataDiableTimeoutOverride(true))) // .WithEC2MetadataDisableTimeoutOverride(true)))
// //
// svc := s3.New(sess) // svc := s3.New(sess)
// //
...@@ -194,7 +194,7 @@ type Config struct { ...@@ -194,7 +194,7 @@ type Config struct {
// both IPv4 and IPv6 addressing. // both IPv4 and IPv6 addressing.
// //
// Setting this for a service which does not support dual stack will fail // Setting this for a service which does not support dual stack will fail
// to make requets. It is not recommended to set this value on the session // to make requests. It is not recommended to set this value on the session
// as it will apply to all service clients created with the session. Even // as it will apply to all service clients created with the session. Even
// services which don't support dual stack endpoints. // services which don't support dual stack endpoints.
// //
...@@ -238,6 +238,7 @@ type Config struct { ...@@ -238,6 +238,7 @@ type Config struct {
// EnableEndpointDiscovery will allow for endpoint discovery on operations that // EnableEndpointDiscovery will allow for endpoint discovery on operations that
// have the definition in its model. By default, endpoint discovery is off. // have the definition in its model. By default, endpoint discovery is off.
// To use EndpointDiscovery, Endpoint should be unset or set to an empty string.
// //
// Example: // Example:
// sess := session.Must(session.NewSession(&aws.Config{ // sess := session.Must(session.NewSession(&aws.Config{
......
...@@ -2,42 +2,8 @@ ...@@ -2,42 +2,8 @@
package aws package aws
import "time" import (
"github.com/aws/aws-sdk-go/internal/context"
// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to
// provide a 1.6 and 1.5 safe version of context that is compatible with Go
// 1.7's Context.
//
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case backgroundCtx:
return "aws.BackgroundContext"
}
return "unknown empty Context"
}
var (
backgroundCtx = new(emptyCtx)
) )
// BackgroundContext returns a context that will never be canceled, has no // BackgroundContext returns a context that will never be canceled, has no
...@@ -52,5 +18,5 @@ var ( ...@@ -52,5 +18,5 @@ var (
// //
// See https://golang.org/pkg/context for more information on Contexts. // See https://golang.org/pkg/context for more information on Contexts.
func BackgroundContext() Context { func BackgroundContext() Context {
return backgroundCtx return context.BackgroundCtx
} }
...@@ -225,6 +225,8 @@ var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointH ...@@ -225,6 +225,8 @@ var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointH
if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" { if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
r.Error = aws.ErrMissingRegion r.Error = aws.ErrMissingRegion
} else if r.ClientInfo.Endpoint == "" { } else if r.ClientInfo.Endpoint == "" {
// Was any endpoint provided by the user, or one was derived by the
// SDK's endpoint resolver?
r.Error = aws.ErrMissingEndpoint r.Error = aws.ErrMissingEndpoint
} }
}} }}
// +build !go1.7
package credentials
import (
"github.com/aws/aws-sdk-go/internal/context"
)
// backgroundContext returns a context that will never be canceled, has no
// values, and no deadline. This context is used by the SDK to provide
// backwards compatibility with non-context API operations and functionality.
//
// Go 1.6 and before:
// This context function is equivalent to context.Background in the Go stdlib.
//
// Go 1.7 and later:
// The context returned will be the value returned by context.Background()
//
// See https://golang.org/pkg/context for more information on Contexts.
func backgroundContext() Context {
return context.BackgroundCtx
}
// +build go1.7
package credentials
import "context"
// backgroundContext returns a context that will never be canceled, has no
// values, and no deadline. This context is used by the SDK to provide
// backwards compatibility with non-context API operations and functionality.
//
// Go 1.6 and before:
// This context function is equivalent to context.Background in the Go stdlib.
//
// Go 1.7 and later:
// The context returned will be the value returned by context.Background()
//
// See https://golang.org/pkg/context for more information on Contexts.
func backgroundContext() Context {
return context.Background()
}
// +build !go1.9
package credentials
import "time"
// Context is an copy of the Go v1.7 stdlib's context.Context interface.
// It is represented as a SDK interface to enable you to use the "WithContext"
// API methods with Go v1.6 and a Context type such as golang.org/x/net/context.
//
// This type, aws.Context, and context.Context are equivalent.
//
// See https://golang.org/pkg/context on how to use contexts.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
Value(key interface{}) interface{}
}
// +build go1.9
package credentials
import "context"
// Context is an alias of the Go stdlib's context.Context interface.
// It can be used within the SDK's API operation "WithContext" methods.
//
// This type, aws.Context, and context.Context are equivalent.
//
// See https://golang.org/pkg/context on how to use contexts.
type Context = context.Context
...@@ -54,6 +54,7 @@ import ( ...@@ -54,6 +54,7 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/internal/sync/singleflight"
) )
// AnonymousCredentials is an empty Credential object that can be used as // AnonymousCredentials is an empty Credential object that can be used as
...@@ -106,6 +107,13 @@ type Provider interface { ...@@ -106,6 +107,13 @@ type Provider interface {
IsExpired() bool IsExpired() bool
} }
// ProviderWithContext is a Provider that can retrieve credentials with a Context
type ProviderWithContext interface {
Provider
RetrieveWithContext(Context) (Value, error)
}
// An Expirer is an interface that Providers can implement to expose the expiration // An Expirer is an interface that Providers can implement to expose the expiration
// time, if known. If the Provider cannot accurately provide this info, // time, if known. If the Provider cannot accurately provide this info,
// it should not implement this interface. // it should not implement this interface.
...@@ -165,7 +173,9 @@ type Expiry struct { ...@@ -165,7 +173,9 @@ type Expiry struct {
// the expiration time given to ensure no requests are made with expired // the expiration time given to ensure no requests are made with expired
// tokens. // tokens.
func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) { func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
e.expiration = expiration // Passed in expirations should have the monotonic clock values stripped.
// This ensures time comparisons will be based on wall-time.
e.expiration = expiration.Round(0)
if window > 0 { if window > 0 {
e.expiration = e.expiration.Add(-window) e.expiration = e.expiration.Add(-window)
} }
...@@ -197,24 +207,24 @@ func (e *Expiry) ExpiresAt() time.Time { ...@@ -197,24 +207,24 @@ func (e *Expiry) ExpiresAt() time.Time {
// first instance of the credentials Value. All calls to Get() after that // first instance of the credentials Value. All calls to Get() after that
// will return the cached credentials Value until IsExpired() returns true. // will return the cached credentials Value until IsExpired() returns true.
type Credentials struct { type Credentials struct {
creds Value sf singleflight.Group
forceRefresh bool
m sync.RWMutex m sync.RWMutex
creds Value
provider Provider provider Provider
} }
// NewCredentials returns a pointer to a new Credentials with the provider set. // NewCredentials returns a pointer to a new Credentials with the provider set.
func NewCredentials(provider Provider) *Credentials { func NewCredentials(provider Provider) *Credentials {
return &Credentials{ c := &Credentials{
provider: provider, provider: provider,
forceRefresh: true,
} }
return c
} }
// Get returns the credentials value, or error if the credentials Value failed // GetWithContext returns the credentials value, or error if the credentials
// to be retrieved. // Value failed to be retrieved. Will return early if the passed in context is
// canceled.
// //
// Will return the cached credentials Value if it has not expired. If the // Will return the cached credentials Value if it has not expired. If the
// credentials Value has expired the Provider's Retrieve() will be called // credentials Value has expired the Provider's Retrieve() will be called
...@@ -222,31 +232,70 @@ func NewCredentials(provider Provider) *Credentials { ...@@ -222,31 +232,70 @@ func NewCredentials(provider Provider) *Credentials {
// //
// If Credentials.Expire() was called the credentials Value will be force // If Credentials.Expire() was called the credentials Value will be force
// expired, and the next call to Get() will cause them to be refreshed. // expired, and the next call to Get() will cause them to be refreshed.
func (c *Credentials) Get() (Value, error) { //
// Check the cached credentials first with just the read lock. // Passed in Context is equivalent to aws.Context, and context.Context.
c.m.RLock() func (c *Credentials) GetWithContext(ctx Context) (Value, error) {
if !c.isExpired() { // Check if credentials are cached, and not expired.
creds := c.creds select {
c.m.RUnlock() case curCreds, ok := <-c.asyncIsExpired():
return creds, nil // ok will only be true, of the credentials were not expired. ok will
// be false and have no value if the credentials are expired.
if ok {
return curCreds, nil
}
case <-ctx.Done():
return Value{}, awserr.New("RequestCanceled",
"request context canceled", ctx.Err())
} }
c.m.RUnlock()
// Credentials are expired need to retrieve the credentials taking the full // Cannot pass context down to the actual retrieve, because the first
// lock. // context would cancel the whole group when there is not direct
// association of items in the group.
resCh := c.sf.DoChan("", func() (interface{}, error) {
return c.singleRetrieve(&suppressedContext{ctx})
})
select {
case res := <-resCh:
return res.Val.(Value), res.Err
case <-ctx.Done():
return Value{}, awserr.New("RequestCanceled",
"request context canceled", ctx.Err())
}
}
func (c *Credentials) singleRetrieve(ctx Context) (interface{}, error) {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
if c.isExpired() { if curCreds := c.creds; !c.isExpiredLocked(curCreds) {
creds, err := c.provider.Retrieve() return curCreds, nil
if err != nil {
return Value{}, err
} }
var creds Value
var err error
if p, ok := c.provider.(ProviderWithContext); ok {
creds, err = p.RetrieveWithContext(ctx)
} else {
creds, err = c.provider.Retrieve()
}
if err == nil {
c.creds = creds c.creds = creds
c.forceRefresh = false
} }
return c.creds, nil return creds, err
}
// Get returns the credentials value, or error if the credentials Value failed
// to be retrieved.
//
// Will return the cached credentials Value if it has not expired. If the
// credentials Value has expired the Provider's Retrieve() will be called
// to refresh the credentials.
//
// If Credentials.Expire() was called the credentials Value will be force
// expired, and the next call to Get() will cause them to be refreshed.
func (c *Credentials) Get() (Value, error) {
return c.GetWithContext(backgroundContext())
} }
// Expire expires the credentials and forces them to be retrieved on the // Expire expires the credentials and forces them to be retrieved on the
...@@ -258,7 +307,7 @@ func (c *Credentials) Expire() { ...@@ -258,7 +307,7 @@ func (c *Credentials) Expire() {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
c.forceRefresh = true c.creds = Value{}
} }
// IsExpired returns if the credentials are no longer valid, and need // IsExpired returns if the credentials are no longer valid, and need
...@@ -270,12 +319,30 @@ func (c *Credentials) IsExpired() bool { ...@@ -270,12 +319,30 @@ func (c *Credentials) IsExpired() bool {
c.m.RLock() c.m.RLock()
defer c.m.RUnlock() defer c.m.RUnlock()
return c.isExpired() return c.isExpiredLocked(c.creds)
}
// asyncIsExpired returns a channel of credentials Value. If the channel is
// closed the credentials are expired and credentials value are not empty.
func (c *Credentials) asyncIsExpired() <-chan Value {
ch := make(chan Value, 1)
go func() {
c.m.RLock()
defer c.m.RUnlock()
if curCreds := c.creds; !c.isExpiredLocked(curCreds) {
ch <- curCreds
}
close(ch)
}()
return ch
} }
// isExpired helper method wrapping the definition of expired credentials. // isExpiredLocked helper method wrapping the definition of expired credentials.
func (c *Credentials) isExpired() bool { func (c *Credentials) isExpiredLocked(creds interface{}) bool {
return c.forceRefresh || c.provider.IsExpired() return creds == nil || creds.(Value) == Value{} || c.provider.IsExpired()
} }
// ExpiresAt provides access to the functionality of the Expirer interface of // ExpiresAt provides access to the functionality of the Expirer interface of
...@@ -288,12 +355,29 @@ func (c *Credentials) ExpiresAt() (time.Time, error) { ...@@ -288,12 +355,29 @@ func (c *Credentials) ExpiresAt() (time.Time, error) {
expirer, ok := c.provider.(Expirer) expirer, ok := c.provider.(Expirer)
if !ok { if !ok {
return time.Time{}, awserr.New("ProviderNotExpirer", return time.Time{}, awserr.New("ProviderNotExpirer",
fmt.Sprintf("provider %s does not support ExpiresAt()", c.creds.ProviderName), fmt.Sprintf("provider %s does not support ExpiresAt()",
c.creds.ProviderName),
nil) nil)
} }
if c.forceRefresh { if c.creds == (Value{}) {
// set expiration time to the distant past // set expiration time to the distant past
return time.Time{}, nil return time.Time{}, nil
} }
return expirer.ExpiresAt(), nil return expirer.ExpiresAt(), nil
} }
type suppressedContext struct {
Context
}
func (s *suppressedContext) Deadline() (deadline time.Time, ok bool) {
return time.Time{}, false
}
func (s *suppressedContext) Done() <-chan struct{} {
return nil
}
func (s *suppressedContext) Err() error {
return nil
}
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
...@@ -87,7 +88,14 @@ func NewCredentialsWithClient(client *ec2metadata.EC2Metadata, options ...func(* ...@@ -87,7 +88,14 @@ func NewCredentialsWithClient(client *ec2metadata.EC2Metadata, options ...func(*
// Error will be returned if the request fails, or unable to extract // Error will be returned if the request fails, or unable to extract
// the desired credentials. // the desired credentials.
func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) { func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
credsList, err := requestCredList(m.Client) return m.RetrieveWithContext(aws.BackgroundContext())
}
// RetrieveWithContext retrieves credentials from the EC2 service.
// Error will be returned if the request fails, or unable to extract
// the desired credentials.
func (m *EC2RoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
credsList, err := requestCredList(ctx, m.Client)
if err != nil { if err != nil {
return credentials.Value{ProviderName: ProviderName}, err return credentials.Value{ProviderName: ProviderName}, err
} }
...@@ -97,7 +105,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) { ...@@ -97,7 +105,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
} }
credsName := credsList[0] credsName := credsList[0]
roleCreds, err := requestCred(m.Client, credsName) roleCreds, err := requestCred(ctx, m.Client, credsName)
if err != nil { if err != nil {
return credentials.Value{ProviderName: ProviderName}, err return credentials.Value{ProviderName: ProviderName}, err
} }
...@@ -130,8 +138,8 @@ const iamSecurityCredsPath = "iam/security-credentials/" ...@@ -130,8 +138,8 @@ const iamSecurityCredsPath = "iam/security-credentials/"
// requestCredList requests a list of credentials from the EC2 service. // requestCredList requests a list of credentials from the EC2 service.
// If there are no credentials, or there is an error making or receiving the request // If there are no credentials, or there is an error making or receiving the request
func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) { func requestCredList(ctx aws.Context, client *ec2metadata.EC2Metadata) ([]string, error) {
resp, err := client.GetMetadata(iamSecurityCredsPath) resp, err := client.GetMetadataWithContext(ctx, iamSecurityCredsPath)
if err != nil { if err != nil {
return nil, awserr.New("EC2RoleRequestError", "no EC2 instance role found", err) return nil, awserr.New("EC2RoleRequestError", "no EC2 instance role found", err)
} }
...@@ -154,8 +162,8 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) { ...@@ -154,8 +162,8 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
// //
// If the credentials cannot be found, or there is an error reading the response // If the credentials cannot be found, or there is an error reading the response
// and error will be returned. // and error will be returned.
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) { func requestCred(ctx aws.Context, client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName)) resp, err := client.GetMetadataWithContext(ctx, sdkuri.PathJoin(iamSecurityCredsPath, credsName))
if err != nil { if err != nil {
return ec2RoleCredRespBody{}, return ec2RoleCredRespBody{},
awserr.New("EC2RoleRequestError", awserr.New("EC2RoleRequestError",
......
...@@ -116,7 +116,13 @@ func (p *Provider) IsExpired() bool { ...@@ -116,7 +116,13 @@ func (p *Provider) IsExpired() bool {
// Retrieve will attempt to request the credentials from the endpoint the Provider // Retrieve will attempt to request the credentials from the endpoint the Provider
// was configured for. And error will be returned if the retrieval fails. // was configured for. And error will be returned if the retrieval fails.
func (p *Provider) Retrieve() (credentials.Value, error) { func (p *Provider) Retrieve() (credentials.Value, error) {
resp, err := p.getCredentials() return p.RetrieveWithContext(aws.BackgroundContext())
}
// RetrieveWithContext will attempt to request the credentials from the endpoint the Provider
// was configured for. And error will be returned if the retrieval fails.
func (p *Provider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
resp, err := p.getCredentials(ctx)
if err != nil { if err != nil {
return credentials.Value{ProviderName: ProviderName}, return credentials.Value{ProviderName: ProviderName},
awserr.New("CredentialsEndpointError", "failed to load credentials", err) awserr.New("CredentialsEndpointError", "failed to load credentials", err)
...@@ -148,7 +154,7 @@ type errorOutput struct { ...@@ -148,7 +154,7 @@ type errorOutput struct {
Message string `json:"message"` Message string `json:"message"`
} }
func (p *Provider) getCredentials() (*getCredentialsOutput, error) { func (p *Provider) getCredentials(ctx aws.Context) (*getCredentialsOutput, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetCredentials", Name: "GetCredentials",
HTTPMethod: "GET", HTTPMethod: "GET",
...@@ -156,6 +162,7 @@ func (p *Provider) getCredentials() (*getCredentialsOutput, error) { ...@@ -156,6 +162,7 @@ func (p *Provider) getCredentials() (*getCredentialsOutput, error) {
out := &getCredentialsOutput{} out := &getCredentialsOutput{}
req := p.Client.NewRequest(op, nil, out) req := p.Client.NewRequest(op, nil, out)
req.SetContext(ctx)
req.HTTPRequest.Header.Set("Accept", "application/json") req.HTTPRequest.Header.Set("Accept", "application/json")
if authToken := p.AuthorizationToken; len(authToken) != 0 { if authToken := p.AuthorizationToken; len(authToken) != 0 {
req.HTTPRequest.Header.Set("Authorization", authToken) req.HTTPRequest.Header.Set("Authorization", authToken)
......
...@@ -17,8 +17,9 @@ var ( ...@@ -17,8 +17,9 @@ var (
ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil) ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil)
) )
// A SharedCredentialsProvider retrieves credentials from the current user's home // A SharedCredentialsProvider retrieves access key pair (access key ID,
// directory, and keeps track if those credentials are expired. // secret access key, and session token if present) credentials from the current
// user's home directory, and keeps track if those credentials are expired.
// //
// Profile ini file example: $HOME/.aws/credentials // Profile ini file example: $HOME/.aws/credentials
type SharedCredentialsProvider struct { type SharedCredentialsProvider struct {
......
...@@ -19,7 +19,9 @@ type StaticProvider struct { ...@@ -19,7 +19,9 @@ type StaticProvider struct {
} }
// NewStaticCredentials returns a pointer to a new Credentials object // NewStaticCredentials returns a pointer to a new Credentials object
// wrapping a static credentials value provider. // wrapping a static credentials value provider. Token is only required
// for temporary security credentials retrieved via STS, otherwise an empty
// string can be passed for this parameter.
func NewStaticCredentials(id, secret, token string) *Credentials { func NewStaticCredentials(id, secret, token string) *Credentials {
return NewCredentials(&StaticProvider{Value: Value{ return NewCredentials(&StaticProvider{Value: Value{
AccessKeyID: id, AccessKeyID: id,
......
...@@ -87,6 +87,7 @@ import ( ...@@ -87,6 +87,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/internal/sdkrand" "github.com/aws/aws-sdk-go/internal/sdkrand"
"github.com/aws/aws-sdk-go/service/sts" "github.com/aws/aws-sdk-go/service/sts"
) )
...@@ -118,6 +119,10 @@ type AssumeRoler interface { ...@@ -118,6 +119,10 @@ type AssumeRoler interface {
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
} }
type assumeRolerWithContext interface {
AssumeRoleWithContext(aws.Context, *sts.AssumeRoleInput, ...request.Option) (*sts.AssumeRoleOutput, error)
}
// DefaultDuration is the default amount of time in minutes that the credentials // DefaultDuration is the default amount of time in minutes that the credentials
// will be valid for. // will be valid for.
var DefaultDuration = time.Duration(15) * time.Minute var DefaultDuration = time.Duration(15) * time.Minute
...@@ -164,6 +169,29 @@ type AssumeRoleProvider struct { ...@@ -164,6 +169,29 @@ type AssumeRoleProvider struct {
// size. // size.
Policy *string Policy *string
// The ARNs of IAM managed policies you want to use as managed session policies.
// The policies must exist in the same account as the role.
//
// This parameter is optional. You can provide up to 10 managed policy ARNs.
// However, the plain text that you use for both inline and managed session
// policies can't exceed 2,048 characters.
//
// An AWS conversion compresses the passed session policies and session tags
// into a packed binary format that has a separate limit. Your request can fail
// for this limit even if your plain text meets the other requirements. The
// PackedPolicySize response element indicates by percentage how close the policies
// and tags for your request are to the upper size limit.
//
// Passing policies to this operation returns new temporary credentials. The
// resulting session's permissions are the intersection of the role's identity-based
// policy and the session policies. You can use the role's temporary credentials
// in subsequent AWS API calls to access resources in the account that owns
// the role. You cannot use session policies to grant more permissions than
// those allowed by the identity-based policy of the role that is being assumed.
// For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session)
// in the IAM User Guide.
PolicyArns []*sts.PolicyDescriptorType
// The identification number of the MFA device that is associated with the user // The identification number of the MFA device that is associated with the user
// who is making the AssumeRole call. Specify this value if the trust policy // who is making the AssumeRole call. Specify this value if the trust policy
// of the role being assumed includes a condition that requires MFA authentication. // of the role being assumed includes a condition that requires MFA authentication.
...@@ -265,6 +293,11 @@ func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(* ...@@ -265,6 +293,11 @@ func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*
// Retrieve generates a new set of temporary credentials using STS. // Retrieve generates a new set of temporary credentials using STS.
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
return p.RetrieveWithContext(aws.BackgroundContext())
}
// RetrieveWithContext generates a new set of temporary credentials using STS.
func (p *AssumeRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
// Apply defaults where parameters are not set. // Apply defaults where parameters are not set.
if p.RoleSessionName == "" { if p.RoleSessionName == "" {
// Try to work out a role name that will hopefully end up unique. // Try to work out a role name that will hopefully end up unique.
...@@ -281,6 +314,7 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { ...@@ -281,6 +314,7 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
RoleSessionName: aws.String(p.RoleSessionName), RoleSessionName: aws.String(p.RoleSessionName),
ExternalId: p.ExternalID, ExternalId: p.ExternalID,
Tags: p.Tags, Tags: p.Tags,
PolicyArns: p.PolicyArns,
TransitiveTagKeys: p.TransitiveTagKeys, TransitiveTagKeys: p.TransitiveTagKeys,
} }
if p.Policy != nil { if p.Policy != nil {
...@@ -304,7 +338,15 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { ...@@ -304,7 +338,15 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
} }
} }
roleOutput, err := p.Client.AssumeRole(input) var roleOutput *sts.AssumeRoleOutput
var err error
if c, ok := p.Client.(assumeRolerWithContext); ok {
roleOutput, err = c.AssumeRoleWithContext(ctx, input)
} else {
roleOutput, err = p.Client.AssumeRole(input)
}
if err != nil { if err != nil {
return credentials.Value{ProviderName: ProviderName}, err return credentials.Value{ProviderName: ProviderName}, err
} }
......
...@@ -28,15 +28,46 @@ const ( ...@@ -28,15 +28,46 @@ const (
// compare test values. // compare test values.
var now = time.Now var now = time.Now
// TokenFetcher shuold return WebIdentity token bytes or an error
type TokenFetcher interface {
FetchToken(credentials.Context) ([]byte, error)
}
// FetchTokenPath is a path to a WebIdentity token file
type FetchTokenPath string
// FetchToken returns a token by reading from the filesystem
func (f FetchTokenPath) FetchToken(ctx credentials.Context) ([]byte, error) {
data, err := ioutil.ReadFile(string(f))
if err != nil {
errMsg := fmt.Sprintf("unable to read file at %s", f)
return nil, awserr.New(ErrCodeWebIdentity, errMsg, err)
}
return data, nil
}
// WebIdentityRoleProvider is used to retrieve credentials using // WebIdentityRoleProvider is used to retrieve credentials using
// an OIDC token. // an OIDC token.
type WebIdentityRoleProvider struct { type WebIdentityRoleProvider struct {
credentials.Expiry credentials.Expiry
PolicyArns []*sts.PolicyDescriptorType
client stsiface.STSAPI // Duration the STS credentials will be valid for. Truncated to seconds.
// If unset, the assumed role will use AssumeRoleWithWebIdentity's default
// expiry duration. See
// https://docs.aws.amazon.com/sdk-for-go/api/service/sts/#STS.AssumeRoleWithWebIdentity
// for more information.
Duration time.Duration
// The amount of time the credentials will be refreshed before they expire.
// This is useful refresh credentials before they expire to reduce risk of
// using credentials as they expire. If unset, will default to no expiry
// window.
ExpiryWindow time.Duration ExpiryWindow time.Duration
tokenFilePath string client stsiface.STSAPI
tokenFetcher TokenFetcher
roleARN string roleARN string
roleSessionName string roleSessionName string
} }
...@@ -52,9 +83,15 @@ func NewWebIdentityCredentials(c client.ConfigProvider, roleARN, roleSessionName ...@@ -52,9 +83,15 @@ func NewWebIdentityCredentials(c client.ConfigProvider, roleARN, roleSessionName
// NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the // NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the
// provided stsiface.STSAPI // provided stsiface.STSAPI
func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, path string) *WebIdentityRoleProvider { func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, path string) *WebIdentityRoleProvider {
return NewWebIdentityRoleProviderWithToken(svc, roleARN, roleSessionName, FetchTokenPath(path))
}
// NewWebIdentityRoleProviderWithToken will return a new WebIdentityRoleProvider with the
// provided stsiface.STSAPI and a TokenFetcher
func NewWebIdentityRoleProviderWithToken(svc stsiface.STSAPI, roleARN, roleSessionName string, tokenFetcher TokenFetcher) *WebIdentityRoleProvider {
return &WebIdentityRoleProvider{ return &WebIdentityRoleProvider{
client: svc, client: svc,
tokenFilePath: path, tokenFetcher: tokenFetcher,
roleARN: roleARN, roleARN: roleARN,
roleSessionName: roleSessionName, roleSessionName: roleSessionName,
} }
...@@ -64,10 +101,16 @@ func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, p ...@@ -64,10 +101,16 @@ func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, p
// 'WebIdentityTokenFilePath' specified destination and if that is empty an // 'WebIdentityTokenFilePath' specified destination and if that is empty an
// error will be returned. // error will be returned.
func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) { func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) {
b, err := ioutil.ReadFile(p.tokenFilePath) return p.RetrieveWithContext(aws.BackgroundContext())
}
// RetrieveWithContext will attempt to assume a role from a token which is located at
// 'WebIdentityTokenFilePath' specified destination and if that is empty an
// error will be returned.
func (p *WebIdentityRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
b, err := p.tokenFetcher.FetchToken(ctx)
if err != nil { if err != nil {
errMsg := fmt.Sprintf("unable to read file at %s", p.tokenFilePath) return credentials.Value{}, awserr.New(ErrCodeWebIdentity, "failed fetching WebIdentity token: ", err)
return credentials.Value{}, awserr.New(ErrCodeWebIdentity, errMsg, err)
} }
sessionName := p.roleSessionName sessionName := p.roleSessionName
...@@ -76,11 +119,22 @@ func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) { ...@@ -76,11 +119,22 @@ func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) {
// uses unix time in nanoseconds to uniquely identify sessions. // uses unix time in nanoseconds to uniquely identify sessions.
sessionName = strconv.FormatInt(now().UnixNano(), 10) sessionName = strconv.FormatInt(now().UnixNano(), 10)
} }
var duration *int64
if p.Duration != 0 {
duration = aws.Int64(int64(p.Duration / time.Second))
}
req, resp := p.client.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{ req, resp := p.client.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{
PolicyArns: p.PolicyArns,
RoleArn: &p.roleARN, RoleArn: &p.roleARN,
RoleSessionName: &sessionName, RoleSessionName: &sessionName,
WebIdentityToken: aws.String(string(b)), WebIdentityToken: aws.String(string(b)),
DurationSeconds: duration,
}) })
req.SetContext(ctx)
// InvalidIdentityToken error is a temporary error that can occur // InvalidIdentityToken error is a temporary error that can occur
// when assuming an Role with a JWT web identity token. // when assuming an Role with a JWT web identity token.
req.RetryErrorCodes = append(req.RetryErrorCodes, sts.ErrCodeInvalidIdentityTokenException) req.RetryErrorCodes = append(req.RetryErrorCodes, sts.ErrCodeInvalidIdentityTokenException)
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/internal/sdkuri" "github.com/aws/aws-sdk-go/internal/sdkuri"
...@@ -15,15 +16,16 @@ import ( ...@@ -15,15 +16,16 @@ import (
// getToken uses the duration to return a token for EC2 metadata service, // getToken uses the duration to return a token for EC2 metadata service,
// or an error if the request failed. // or an error if the request failed.
func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) { func (c *EC2Metadata) getToken(ctx aws.Context, duration time.Duration) (tokenOutput, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetToken", Name: "GetToken",
HTTPMethod: "PUT", HTTPMethod: "PUT",
HTTPPath: "/api/token", HTTPPath: "/latest/api/token",
} }
var output tokenOutput var output tokenOutput
req := c.NewRequest(op, nil, &output) req := c.NewRequest(op, nil, &output)
req.SetContext(ctx)
// remove the fetch token handler from the request handlers to avoid infinite recursion // remove the fetch token handler from the request handlers to avoid infinite recursion
req.Handlers.Sign.RemoveByName(fetchTokenHandlerName) req.Handlers.Sign.RemoveByName(fetchTokenHandlerName)
...@@ -50,15 +52,24 @@ func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) { ...@@ -50,15 +52,24 @@ func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) {
// instance metadata service. The content will be returned as a string, or // instance metadata service. The content will be returned as a string, or
// error if the request failed. // error if the request failed.
func (c *EC2Metadata) GetMetadata(p string) (string, error) { func (c *EC2Metadata) GetMetadata(p string) (string, error) {
return c.GetMetadataWithContext(aws.BackgroundContext(), p)
}
// GetMetadataWithContext uses the path provided to request information from the EC2
// instance metadata service. The content will be returned as a string, or
// error if the request failed.
func (c *EC2Metadata) GetMetadataWithContext(ctx aws.Context, p string) (string, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetMetadata", Name: "GetMetadata",
HTTPMethod: "GET", HTTPMethod: "GET",
HTTPPath: sdkuri.PathJoin("/meta-data", p), HTTPPath: sdkuri.PathJoin("/latest/meta-data", p),
} }
output := &metadataOutput{} output := &metadataOutput{}
req := c.NewRequest(op, nil, output) req := c.NewRequest(op, nil, output)
req.SetContext(ctx)
err := req.Send() err := req.Send()
return output.Content, err return output.Content, err
} }
...@@ -67,14 +78,22 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) { ...@@ -67,14 +78,22 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
// there is no user-data setup for the EC2 instance a "NotFoundError" error // there is no user-data setup for the EC2 instance a "NotFoundError" error
// code will be returned. // code will be returned.
func (c *EC2Metadata) GetUserData() (string, error) { func (c *EC2Metadata) GetUserData() (string, error) {
return c.GetUserDataWithContext(aws.BackgroundContext())
}
// GetUserDataWithContext returns the userdata that was configured for the service. If
// there is no user-data setup for the EC2 instance a "NotFoundError" error
// code will be returned.
func (c *EC2Metadata) GetUserDataWithContext(ctx aws.Context) (string, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetUserData", Name: "GetUserData",
HTTPMethod: "GET", HTTPMethod: "GET",
HTTPPath: "/user-data", HTTPPath: "/latest/user-data",
} }
output := &metadataOutput{} output := &metadataOutput{}
req := c.NewRequest(op, nil, output) req := c.NewRequest(op, nil, output)
req.SetContext(ctx)
err := req.Send() err := req.Send()
return output.Content, err return output.Content, err
...@@ -84,14 +103,22 @@ func (c *EC2Metadata) GetUserData() (string, error) { ...@@ -84,14 +103,22 @@ func (c *EC2Metadata) GetUserData() (string, error) {
// instance metadata service for dynamic data. The content will be returned // instance metadata service for dynamic data. The content will be returned
// as a string, or error if the request failed. // as a string, or error if the request failed.
func (c *EC2Metadata) GetDynamicData(p string) (string, error) { func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
return c.GetDynamicDataWithContext(aws.BackgroundContext(), p)
}
// GetDynamicDataWithContext uses the path provided to request information from the EC2
// instance metadata service for dynamic data. The content will be returned
// as a string, or error if the request failed.
func (c *EC2Metadata) GetDynamicDataWithContext(ctx aws.Context, p string) (string, error) {
op := &request.Operation{ op := &request.Operation{
Name: "GetDynamicData", Name: "GetDynamicData",
HTTPMethod: "GET", HTTPMethod: "GET",
HTTPPath: sdkuri.PathJoin("/dynamic", p), HTTPPath: sdkuri.PathJoin("/latest/dynamic", p),
} }
output := &metadataOutput{} output := &metadataOutput{}
req := c.NewRequest(op, nil, output) req := c.NewRequest(op, nil, output)
req.SetContext(ctx)
err := req.Send() err := req.Send()
return output.Content, err return output.Content, err
...@@ -101,7 +128,14 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) { ...@@ -101,7 +128,14 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
// instance. Error is returned if the request fails or is unable to parse // instance. Error is returned if the request fails or is unable to parse
// the response. // the response.
func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) { func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) {
resp, err := c.GetDynamicData("instance-identity/document") return c.GetInstanceIdentityDocumentWithContext(aws.BackgroundContext())
}
// GetInstanceIdentityDocumentWithContext retrieves an identity document describing an
// instance. Error is returned if the request fails or is unable to parse
// the response.
func (c *EC2Metadata) GetInstanceIdentityDocumentWithContext(ctx aws.Context) (EC2InstanceIdentityDocument, error) {
resp, err := c.GetDynamicDataWithContext(ctx, "instance-identity/document")
if err != nil { if err != nil {
return EC2InstanceIdentityDocument{}, return EC2InstanceIdentityDocument{},
awserr.New("EC2MetadataRequestError", awserr.New("EC2MetadataRequestError",
...@@ -120,7 +154,12 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument ...@@ -120,7 +154,12 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument
// IAMInfo retrieves IAM info from the metadata API // IAMInfo retrieves IAM info from the metadata API
func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) { func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
resp, err := c.GetMetadata("iam/info") return c.IAMInfoWithContext(aws.BackgroundContext())
}
// IAMInfoWithContext retrieves IAM info from the metadata API
func (c *EC2Metadata) IAMInfoWithContext(ctx aws.Context) (EC2IAMInfo, error) {
resp, err := c.GetMetadataWithContext(ctx, "iam/info")
if err != nil { if err != nil {
return EC2IAMInfo{}, return EC2IAMInfo{},
awserr.New("EC2MetadataRequestError", awserr.New("EC2MetadataRequestError",
...@@ -145,7 +184,12 @@ func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) { ...@@ -145,7 +184,12 @@ func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
// Region returns the region the instance is running in. // Region returns the region the instance is running in.
func (c *EC2Metadata) Region() (string, error) { func (c *EC2Metadata) Region() (string, error) {
ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocument() return c.RegionWithContext(aws.BackgroundContext())
}
// RegionWithContext returns the region the instance is running in.
func (c *EC2Metadata) RegionWithContext(ctx aws.Context) (string, error) {
ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocumentWithContext(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
...@@ -162,7 +206,14 @@ func (c *EC2Metadata) Region() (string, error) { ...@@ -162,7 +206,14 @@ func (c *EC2Metadata) Region() (string, error) {
// Can be used to determine if application is running within an EC2 Instance and // Can be used to determine if application is running within an EC2 Instance and
// the metadata service is available. // the metadata service is available.
func (c *EC2Metadata) Available() bool { func (c *EC2Metadata) Available() bool {
if _, err := c.GetMetadata("instance-id"); err != nil { return c.AvailableWithContext(aws.BackgroundContext())
}
// AvailableWithContext returns if the application has access to the EC2 Metadata service.
// Can be used to determine if application is running within an EC2 Instance and
// the metadata service is available.
func (c *EC2Metadata) AvailableWithContext(ctx aws.Context) bool {
if _, err := c.GetMetadataWithContext(ctx, "instance-id"); err != nil {
return false return false
} }
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
// variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to // variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
// true instructs the SDK to disable the EC2 Metadata client. The client cannot // true instructs the SDK to disable the EC2 Metadata client. The client cannot
// be used while the environment variable is set to true, (case insensitive). // be used while the environment variable is set to true, (case insensitive).
//
// The endpoint of the EC2 IMDS client can be configured via the environment
// variable, AWS_EC2_METADATA_SERVICE_ENDPOINT when creating the client with a
// Session. See aws/session#Options.EC2IMDSEndpoint for more details.
package ec2metadata package ec2metadata
import ( import (
...@@ -12,6 +16,7 @@ import ( ...@@ -12,6 +16,7 @@ import (
"errors" "errors"
"io" "io"
"net/http" "net/http"
"net/url"
"os" "os"
"strconv" "strconv"
"strings" "strings"
...@@ -69,6 +74,9 @@ func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata { ...@@ -69,6 +74,9 @@ func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
// a client when not using a session. Generally using just New with a session // a client when not using a session. Generally using just New with a session
// is preferred. // is preferred.
// //
// Will remove the URL path from the endpoint provided to ensure the EC2 IMDS
// client is able to communicate with the EC2 IMDS API.
//
// If an unmodified HTTP client is provided from the stdlib default, or no client // If an unmodified HTTP client is provided from the stdlib default, or no client
// the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened. // the EC2RoleProvider's EC2Metadata HTTP client's timeout will be shortened.
// To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default. // To disable this set Config.EC2MetadataDisableTimeoutOverride to false. Enabled by default.
...@@ -86,6 +94,15 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio ...@@ -86,6 +94,15 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
cfg.MaxRetries = aws.Int(2) cfg.MaxRetries = aws.Int(2)
} }
if u, err := url.Parse(endpoint); err == nil {
// Remove path from the endpoint since it will be added by requests.
// This is an artifact of the SDK adding `/latest` to the endpoint for
// EC2 IMDS, but this is now moved to the operation definition.
u.Path = ""
u.RawPath = ""
endpoint = u.String()
}
svc := &EC2Metadata{ svc := &EC2Metadata{
Client: client.New( Client: client.New(
cfg, cfg,
......
...@@ -46,7 +46,7 @@ func (t *tokenProvider) fetchTokenHandler(r *request.Request) { ...@@ -46,7 +46,7 @@ func (t *tokenProvider) fetchTokenHandler(r *request.Request) {
return return
} }
output, err := t.client.getToken(t.configuredTTL) output, err := t.client.getToken(r.Context(), t.configuredTTL)
if err != nil { if err != nil {
...@@ -87,6 +87,7 @@ func (t *tokenProvider) enableTokenProviderHandler(r *request.Request) { ...@@ -87,6 +87,7 @@ func (t *tokenProvider) enableTokenProviderHandler(r *request.Request) {
// If the error code status is 401, we enable the token provider // If the error code status is 401, we enable the token provider
if e, ok := r.Error.(awserr.RequestFailure); ok && e != nil && if e, ok := r.Error.(awserr.RequestFailure); ok && e != nil &&
e.StatusCode() == http.StatusUnauthorized { e.StatusCode() == http.StatusUnauthorized {
t.token.Store(ec2Token{})
atomic.StoreUint32(&t.disabled, 0) atomic.StoreUint32(&t.disabled, 0)
} }
} }
...@@ -93,7 +93,7 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol ...@@ -93,7 +93,7 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
} }
func custAddS3DualStack(p *partition) { func custAddS3DualStack(p *partition) {
if p.ID != "aws" { if !(p.ID == "aws" || p.ID == "aws-cn" || p.ID == "aws-us-gov") {
return return
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -7,6 +7,8 @@ import ( ...@@ -7,6 +7,8 @@ import (
"strings" "strings"
) )
var regionValidationRegex = regexp.MustCompile(`^[[:alnum:]]([[:alnum:]\-]*[[:alnum:]])?$`)
type partitions []partition type partitions []partition
func (ps partitions) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) { func (ps partitions) EndpointFor(service, region string, opts ...func(*Options)) (ResolvedEndpoint, error) {
...@@ -124,7 +126,7 @@ func (p partition) EndpointFor(service, region string, opts ...func(*Options)) ( ...@@ -124,7 +126,7 @@ func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (
defs := []endpoint{p.Defaults, s.Defaults} defs := []endpoint{p.Defaults, s.Defaults}
return e.resolve(service, p.ID, region, p.DNSSuffix, defs, opt), nil return e.resolve(service, p.ID, region, p.DNSSuffix, defs, opt)
} }
func serviceList(ss services) []string { func serviceList(ss services) []string {
...@@ -233,7 +235,7 @@ func getByPriority(s []string, p []string, def string) string { ...@@ -233,7 +235,7 @@ func getByPriority(s []string, p []string, def string) string {
return s[0] return s[0]
} }
func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs []endpoint, opts Options) ResolvedEndpoint { func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs []endpoint, opts Options) (ResolvedEndpoint, error) {
var merged endpoint var merged endpoint
for _, def := range defs { for _, def := range defs {
merged.mergeIn(def) merged.mergeIn(def)
...@@ -260,6 +262,10 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [ ...@@ -260,6 +262,10 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [
region = signingRegion region = signingRegion
} }
if !validateInputRegion(region) {
return ResolvedEndpoint{}, fmt.Errorf("invalid region identifier format provided")
}
u := strings.Replace(hostname, "{service}", service, 1) u := strings.Replace(hostname, "{service}", service, 1)
u = strings.Replace(u, "{region}", region, 1) u = strings.Replace(u, "{region}", region, 1)
u = strings.Replace(u, "{dnsSuffix}", dnsSuffix, 1) u = strings.Replace(u, "{dnsSuffix}", dnsSuffix, 1)
...@@ -274,7 +280,7 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [ ...@@ -274,7 +280,7 @@ func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs [
SigningName: signingName, SigningName: signingName,
SigningNameDerived: signingNameDerived, SigningNameDerived: signingNameDerived,
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner), SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
} }, nil
} }
func getEndpointScheme(protocols []string, disableSSL bool) string { func getEndpointScheme(protocols []string, disableSSL bool) string {
...@@ -339,3 +345,7 @@ const ( ...@@ -339,3 +345,7 @@ const (
boxedFalse boxedFalse
boxedTrue boxedTrue
) )
func validateInputRegion(region string) bool {
return regionValidationRegex.MatchString(region)
}
...@@ -9,7 +9,8 @@ func isErrConnectionReset(err error) bool { ...@@ -9,7 +9,8 @@ func isErrConnectionReset(err error) bool {
return false return false
} }
if strings.Contains(err.Error(), "connection reset") || if strings.Contains(err.Error(), "use of closed network connection") ||
strings.Contains(err.Error(), "connection reset") ||
strings.Contains(err.Error(), "broken pipe") { strings.Contains(err.Error(), "broken pipe") {
return true return true
} }
......
...@@ -135,8 +135,6 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers, ...@@ -135,8 +135,6 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err) err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
} }
SanitizeHostForHeader(httpReq)
r := &Request{ r := &Request{
Config: cfg, Config: cfg,
ClientInfo: clientInfo, ClientInfo: clientInfo,
...@@ -426,6 +424,8 @@ func (r *Request) Sign() error { ...@@ -426,6 +424,8 @@ func (r *Request) Sign() error {
return r.Error return r.Error
} }
SanitizeHostForHeader(r.HTTPRequest)
r.Handlers.Sign.Run(r) r.Handlers.Sign.Run(r)
return r.Error return r.Error
} }
...@@ -639,6 +639,10 @@ func getHost(r *http.Request) string { ...@@ -639,6 +639,10 @@ func getHost(r *http.Request) string {
return r.Host return r.Host
} }
if r.URL == nil {
return ""
}
return r.URL.Host return r.URL.Host
} }
......
...@@ -92,6 +92,7 @@ var throttleCodes = map[string]struct{}{ ...@@ -92,6 +92,7 @@ var throttleCodes = map[string]struct{}{
"TooManyRequestsException": {}, // Lambda functions "TooManyRequestsException": {}, // Lambda functions
"PriorRequestNotComplete": {}, // Route53 "PriorRequestNotComplete": {}, // Route53
"TransactionInProgressException": {}, "TransactionInProgressException": {},
"EC2ThrottledException": {}, // EC2
} }
// credsExpiredCodes is a collection of error codes which signify the credentials // credsExpiredCodes is a collection of error codes which signify the credentials
......
...@@ -3,6 +3,7 @@ package session ...@@ -3,6 +3,7 @@ package session
import ( import (
"fmt" "fmt"
"os" "os"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
...@@ -206,7 +207,14 @@ func credsFromAssumeRole(cfg aws.Config, ...@@ -206,7 +207,14 @@ func credsFromAssumeRole(cfg aws.Config,
sharedCfg.RoleARN, sharedCfg.RoleARN,
func(opt *stscreds.AssumeRoleProvider) { func(opt *stscreds.AssumeRoleProvider) {
opt.RoleSessionName = sharedCfg.RoleSessionName opt.RoleSessionName = sharedCfg.RoleSessionName
if sessOpts.AssumeRoleDuration == 0 &&
sharedCfg.AssumeRoleDuration != nil &&
*sharedCfg.AssumeRoleDuration/time.Minute > 15 {
opt.Duration = *sharedCfg.AssumeRoleDuration
} else if sessOpts.AssumeRoleDuration != 0 {
opt.Duration = sessOpts.AssumeRoleDuration opt.Duration = sessOpts.AssumeRoleDuration
}
// Assume role with external ID // Assume role with external ID
if len(sharedCfg.ExternalID) > 0 { if len(sharedCfg.ExternalID) > 0 {
......
...@@ -241,5 +241,22 @@ over the AWS_CA_BUNDLE environment variable, and will be used if both are set. ...@@ -241,5 +241,22 @@ over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
Setting a custom HTTPClient in the aws.Config options will override this setting. Setting a custom HTTPClient in the aws.Config options will override this setting.
To use this option and custom HTTP client, the HTTP client needs to be provided To use this option and custom HTTP client, the HTTP client needs to be provided
when creating the session. Not the service client. when creating the session. Not the service client.
The endpoint of the EC2 IMDS client can be configured via the environment
variable, AWS_EC2_METADATA_SERVICE_ENDPOINT when creating the client with a
Session. See Options.EC2IMDSEndpoint for more details.
AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254
If using an URL with an IPv6 address literal, the IPv6 address
component must be enclosed in square brackets.
AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1]
The custom EC2 IMDS endpoint can also be specified via the Session options.
sess, err := session.NewSessionWithOptions(session.Options{
EC2IMDSEndpoint: "http://[::1]",
})
*/ */
package session package session
...@@ -148,6 +148,11 @@ type envConfig struct { ...@@ -148,6 +148,11 @@ type envConfig struct {
// //
// AWS_S3_USE_ARN_REGION=true // AWS_S3_USE_ARN_REGION=true
S3UseARNRegion bool S3UseARNRegion bool
// Specifies the alternative endpoint to use for EC2 IMDS.
//
// AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1]
EC2IMDSEndpoint string
} }
var ( var (
...@@ -211,6 +216,9 @@ var ( ...@@ -211,6 +216,9 @@ var (
s3UseARNRegionEnvKey = []string{ s3UseARNRegionEnvKey = []string{
"AWS_S3_USE_ARN_REGION", "AWS_S3_USE_ARN_REGION",
} }
ec2IMDSEndpointEnvKey = []string{
"AWS_EC2_METADATA_SERVICE_ENDPOINT",
}
) )
// loadEnvConfig retrieves the SDK's environment configuration. // loadEnvConfig retrieves the SDK's environment configuration.
...@@ -332,6 +340,8 @@ func envConfigLoad(enableSharedConfig bool) (envConfig, error) { ...@@ -332,6 +340,8 @@ func envConfigLoad(enableSharedConfig bool) (envConfig, error) {
} }
} }
setFromEnvVal(&cfg.EC2IMDSEndpoint, ec2IMDSEndpointEnvKey)
return cfg, nil return cfg, nil
} }
......
...@@ -48,6 +48,8 @@ var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credenti ...@@ -48,6 +48,8 @@ var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credenti
type Session struct { type Session struct {
Config *aws.Config Config *aws.Config
Handlers request.Handlers Handlers request.Handlers
options Options
} }
// New creates a new instance of the handlers merging in the provided configs // New creates a new instance of the handlers merging in the provided configs
...@@ -99,7 +101,7 @@ func New(cfgs ...*aws.Config) *Session { ...@@ -99,7 +101,7 @@ func New(cfgs ...*aws.Config) *Session {
return s return s
} }
s := deprecatedNewSession(cfgs...) s := deprecatedNewSession(envCfg, cfgs...)
if envErr != nil { if envErr != nil {
msg := "failed to load env config" msg := "failed to load env config"
s.logDeprecatedNewSessionError(msg, envErr, cfgs) s.logDeprecatedNewSessionError(msg, envErr, cfgs)
...@@ -243,6 +245,23 @@ type Options struct { ...@@ -243,6 +245,23 @@ type Options struct {
// function to initialize this value before changing the handlers to be // function to initialize this value before changing the handlers to be
// used by the SDK. // used by the SDK.
Handlers request.Handlers Handlers request.Handlers
// Allows specifying a custom endpoint to be used by the EC2 IMDS client
// when making requests to the EC2 IMDS API. The must endpoint value must
// include protocol prefix.
//
// If unset, will the EC2 IMDS client will use its default endpoint.
//
// Can also be specified via the environment variable,
// AWS_EC2_METADATA_SERVICE_ENDPOINT.
//
// AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254
//
// If using an URL with an IPv6 address literal, the IPv6 address
// component must be enclosed in square brackets.
//
// AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1]
EC2IMDSEndpoint string
} }
// NewSessionWithOptions returns a new Session created from SDK defaults, config files, // NewSessionWithOptions returns a new Session created from SDK defaults, config files,
...@@ -329,7 +348,25 @@ func Must(sess *Session, err error) *Session { ...@@ -329,7 +348,25 @@ func Must(sess *Session, err error) *Session {
return sess return sess
} }
func deprecatedNewSession(cfgs ...*aws.Config) *Session { // Wraps the endpoint resolver with a resolver that will return a custom
// endpoint for EC2 IMDS.
func wrapEC2IMDSEndpoint(resolver endpoints.Resolver, endpoint string) endpoints.Resolver {
return endpoints.ResolverFunc(
func(service, region string, opts ...func(*endpoints.Options)) (
endpoints.ResolvedEndpoint, error,
) {
if service == ec2MetadataServiceID {
return endpoints.ResolvedEndpoint{
URL: endpoint,
SigningName: ec2MetadataServiceID,
SigningRegion: region,
}, nil
}
return resolver.EndpointFor(service, region)
})
}
func deprecatedNewSession(envCfg envConfig, cfgs ...*aws.Config) *Session {
cfg := defaults.Config() cfg := defaults.Config()
handlers := defaults.Handlers() handlers := defaults.Handlers()
...@@ -341,6 +378,11 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session { ...@@ -341,6 +378,11 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session {
// endpoints for service client configurations. // endpoints for service client configurations.
cfg.EndpointResolver = endpoints.DefaultResolver() cfg.EndpointResolver = endpoints.DefaultResolver()
} }
if len(envCfg.EC2IMDSEndpoint) != 0 {
cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, envCfg.EC2IMDSEndpoint)
}
cfg.Credentials = defaults.CredChain(cfg, handlers) cfg.Credentials = defaults.CredChain(cfg, handlers)
// Reapply any passed in configs to override credentials if set // Reapply any passed in configs to override credentials if set
...@@ -349,6 +391,9 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session { ...@@ -349,6 +391,9 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session {
s := &Session{ s := &Session{
Config: cfg, Config: cfg,
Handlers: handlers, Handlers: handlers,
options: Options{
EC2IMDSEndpoint: envCfg.EC2IMDSEndpoint,
},
} }
initHandlers(s) initHandlers(s)
...@@ -418,6 +463,7 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, ...@@ -418,6 +463,7 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session,
s := &Session{ s := &Session{
Config: cfg, Config: cfg,
Handlers: handlers, Handlers: handlers,
options: opts,
} }
initHandlers(s) initHandlers(s)
...@@ -570,6 +616,14 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config, ...@@ -570,6 +616,14 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config,
endpoints.LegacyS3UsEast1Endpoint, endpoints.LegacyS3UsEast1Endpoint,
}) })
ec2IMDSEndpoint := sessOpts.EC2IMDSEndpoint
if len(ec2IMDSEndpoint) == 0 {
ec2IMDSEndpoint = envCfg.EC2IMDSEndpoint
}
if len(ec2IMDSEndpoint) != 0 {
cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, ec2IMDSEndpoint)
}
// Configure credentials if not already set by the user when creating the // Configure credentials if not already set by the user when creating the
// Session. // Session.
if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil { if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
...@@ -627,6 +681,7 @@ func (s *Session) Copy(cfgs ...*aws.Config) *Session { ...@@ -627,6 +681,7 @@ func (s *Session) Copy(cfgs ...*aws.Config) *Session {
newSession := &Session{ newSession := &Session{
Config: s.Config.Copy(cfgs...), Config: s.Config.Copy(cfgs...),
Handlers: s.Handlers.Copy(), Handlers: s.Handlers.Copy(),
options: s.options,
} }
initHandlers(newSession) initHandlers(newSession)
...@@ -665,6 +720,8 @@ func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Confi ...@@ -665,6 +720,8 @@ func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Confi
} }
} }
const ec2MetadataServiceID = "ec2metadata"
func (s *Session) resolveEndpoint(service, region string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) { func (s *Session) resolveEndpoint(service, region string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) {
if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 { if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 {
......
...@@ -2,6 +2,7 @@ package session ...@@ -2,6 +2,7 @@ package session
import ( import (
"fmt" "fmt"
"time"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
...@@ -22,6 +23,7 @@ const ( ...@@ -22,6 +23,7 @@ const (
externalIDKey = `external_id` // optional externalIDKey = `external_id` // optional
mfaSerialKey = `mfa_serial` // optional mfaSerialKey = `mfa_serial` // optional
roleSessionNameKey = `role_session_name` // optional roleSessionNameKey = `role_session_name` // optional
roleDurationSecondsKey = "duration_seconds" // optional
// CSM options // CSM options
csmEnabledKey = `csm_enabled` csmEnabledKey = `csm_enabled`
...@@ -77,6 +79,7 @@ type sharedConfig struct { ...@@ -77,6 +79,7 @@ type sharedConfig struct {
RoleSessionName string RoleSessionName string
ExternalID string ExternalID string
MFASerial string MFASerial string
AssumeRoleDuration *time.Duration
SourceProfileName string SourceProfileName string
SourceProfile *sharedConfig SourceProfile *sharedConfig
...@@ -274,6 +277,11 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, e ...@@ -274,6 +277,11 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, e
updateString(&cfg.CredentialSource, section, credentialSourceKey) updateString(&cfg.CredentialSource, section, credentialSourceKey)
updateString(&cfg.Region, section, regionKey) updateString(&cfg.Region, section, regionKey)
if section.Has(roleDurationSecondsKey) {
d := time.Duration(section.Int(roleDurationSecondsKey)) * time.Second
cfg.AssumeRoleDuration = &d
}
if v := section.String(stsRegionalEndpointSharedKey); len(v) != 0 { if v := section.String(stsRegionalEndpointSharedKey); len(v) != 0 {
sre, err := endpoints.GetSTSRegionalEndpoint(v) sre, err := endpoints.GetSTSRegionalEndpoint(v)
if err != nil { if err != nil {
......
// +build !go1.7
package v4
import (
"net/http"
"github.com/aws/aws-sdk-go/aws"
)
func requestContext(r *http.Request) aws.Context {
return aws.BackgroundContext()
}
// +build go1.7
package v4
import (
"net/http"
"github.com/aws/aws-sdk-go/aws"
)
func requestContext(r *http.Request) aws.Context {
return r.Context()
}
...@@ -340,7 +340,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi ...@@ -340,7 +340,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
} }
var err error var err error
ctx.credValues, err = v4.Credentials.Get() ctx.credValues, err = v4.Credentials.GetWithContext(requestContext(r))
if err != nil { if err != nil {
return http.Header{}, err return http.Header{}, err
} }
......
...@@ -239,3 +239,26 @@ func (es errors) Error() string { ...@@ -239,3 +239,26 @@ func (es errors) Error() string {
return strings.Join(parts, "\n") return strings.Join(parts, "\n")
} }
// CopySeekableBody copies the seekable body to an io.Writer
func CopySeekableBody(dst io.Writer, src io.ReadSeeker) (int64, error) {
curPos, err := src.Seek(0, sdkio.SeekCurrent)
if err != nil {
return 0, err
}
// copy errors may be assumed to be from the body.
n, err := io.Copy(dst, src)
if err != nil {
return n, err
}
// seek back to the first position after reading to reset
// the body for transmission.
_, err = src.Seek(curPos, sdkio.SeekStart)
if err != nil {
return n, err
}
return n, nil
}
...@@ -5,4 +5,4 @@ package aws ...@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go" const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK // SDKVersion is the version of this SDK
const SDKVersion = "1.28.2" const SDKVersion = "1.35.24"
// +build !go1.7
package context
import "time"
// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to
// provide a 1.6 and 1.5 safe version of context that is compatible with Go
// 1.7's Context.
//
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case BackgroundCtx:
return "aws.BackgroundContext"
}
return "unknown empty Context"
}
// BackgroundCtx is the common base context.
var BackgroundCtx = new(emptyCtx)
...@@ -66,6 +66,7 @@ var parseTable = map[ASTKind]map[TokenType]int{ ...@@ -66,6 +66,7 @@ var parseTable = map[ASTKind]map[TokenType]int{
TokenLit: ValueState, TokenLit: ValueState,
TokenWS: SkipTokenState, TokenWS: SkipTokenState,
TokenNL: SkipState, TokenNL: SkipState,
TokenNone: SkipState,
}, },
ASTKindStatement: map[TokenType]int{ ASTKindStatement: map[TokenType]int{
TokenLit: SectionState, TokenLit: SectionState,
......
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.
// Copyright 2013 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 singleflight provides a duplicate function call suppression
// mechanism.
package singleflight
import "sync"
// call is an in-flight or completed singleflight.Do call
type call struct {
wg sync.WaitGroup
// These fields are written once before the WaitGroup is done
// and are only read after the WaitGroup is done.
val interface{}
err error
// forgotten indicates whether Forget was called with this call's key
// while the call was still in flight.
forgotten bool
// These fields are read and written with the singleflight
// mutex held before the WaitGroup is done, and are read but
// not written after the WaitGroup is done.
dups int
chans []chan<- Result
}
// Group represents a class of work and forms a namespace in
// which units of work can be executed with duplicate suppression.
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
// Result holds the results of Do, so they can be passed
// on a channel.
type Result struct {
Val interface{}
Err error
Shared bool
}
// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
// The return value shared indicates whether v was given to multiple callers.
func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
c.dups++
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err, true
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
g.doCall(c, key, fn)
return c.val, c.err, c.dups > 0
}
// DoChan is like Do but returns a channel that will receive the
// results when they are ready.
func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result {
ch := make(chan Result, 1)
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
c.dups++
c.chans = append(c.chans, ch)
g.mu.Unlock()
return ch
}
c := &call{chans: []chan<- Result{ch}}
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
go g.doCall(c, key, fn)
return ch
}
// doCall handles the single call for a key.
func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) {
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
if !c.forgotten {
delete(g.m, key)
}
for _, ch := range c.chans {
ch <- Result{c.val, c.err, c.dups > 0}
}
g.mu.Unlock()
}
// Forget tells the singleflight to forget about a key. Future calls
// to Do for this key will call the function rather than waiting for
// an earlier call to complete.
func (g *Group) Forget(key string) {
g.mu.Lock()
if c, ok := g.m[key]; ok {
c.forgotten = true
}
delete(g.m, key)
g.mu.Unlock()
}
// Package ec2query provides serialization of AWS EC2 requests and responses. // Package ec2query provides serialization of AWS EC2 requests and responses.
package ec2query package ec2query
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/ec2.json build_test.go //go:generate go run -tags codegen ../../../private/model/cli/gen-protocol-tests ../../../models/protocol_tests/input/ec2.json build_test.go
import ( import (
"net/url" "net/url"
......
package ec2query package ec2query
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/ec2.json unmarshal_test.go //go:generate go run -tags codegen ../../../private/model/cli/gen-protocol-tests ../../../models/protocol_tests/output/ec2.json unmarshal_test.go
import ( import (
"encoding/xml" "encoding/xml"
......
...@@ -6,7 +6,9 @@ import ( ...@@ -6,7 +6,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"math/big"
"reflect" "reflect"
"strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
...@@ -14,6 +16,8 @@ import ( ...@@ -14,6 +16,8 @@ import (
"github.com/aws/aws-sdk-go/private/protocol" "github.com/aws/aws-sdk-go/private/protocol"
) )
var millisecondsFloat = new(big.Float).SetInt64(1e3)
// UnmarshalJSONError unmarshal's the reader's JSON document into the passed in // UnmarshalJSONError unmarshal's the reader's JSON document into the passed in
// type. The value to unmarshal the json document into must be a pointer to the // type. The value to unmarshal the json document into must be a pointer to the
// type. // type.
...@@ -38,17 +42,42 @@ func UnmarshalJSONError(v interface{}, stream io.Reader) error { ...@@ -38,17 +42,42 @@ func UnmarshalJSONError(v interface{}, stream io.Reader) error {
func UnmarshalJSON(v interface{}, stream io.Reader) error { func UnmarshalJSON(v interface{}, stream io.Reader) error {
var out interface{} var out interface{}
err := json.NewDecoder(stream).Decode(&out) decoder := json.NewDecoder(stream)
decoder.UseNumber()
err := decoder.Decode(&out)
if err == io.EOF { if err == io.EOF {
return nil return nil
} else if err != nil { } else if err != nil {
return err return err
} }
return unmarshalAny(reflect.ValueOf(v), out, "") return unmarshaler{}.unmarshalAny(reflect.ValueOf(v), out, "")
}
// UnmarshalJSONCaseInsensitive reads a stream and unmarshals the result into the
// object v. Ignores casing for structure members.
func UnmarshalJSONCaseInsensitive(v interface{}, stream io.Reader) error {
var out interface{}
decoder := json.NewDecoder(stream)
decoder.UseNumber()
err := decoder.Decode(&out)
if err == io.EOF {
return nil
} else if err != nil {
return err
}
return unmarshaler{
caseInsensitive: true,
}.unmarshalAny(reflect.ValueOf(v), out, "")
}
type unmarshaler struct {
caseInsensitive bool
} }
func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error { func (u unmarshaler) unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error {
vtype := value.Type() vtype := value.Type()
if vtype.Kind() == reflect.Ptr { if vtype.Kind() == reflect.Ptr {
vtype = vtype.Elem() // check kind of actual element type vtype = vtype.Elem() // check kind of actual element type
...@@ -80,17 +109,17 @@ func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) ...@@ -80,17 +109,17 @@ func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag)
if field, ok := vtype.FieldByName("_"); ok { if field, ok := vtype.FieldByName("_"); ok {
tag = field.Tag tag = field.Tag
} }
return unmarshalStruct(value, data, tag) return u.unmarshalStruct(value, data, tag)
case "list": case "list":
return unmarshalList(value, data, tag) return u.unmarshalList(value, data, tag)
case "map": case "map":
return unmarshalMap(value, data, tag) return u.unmarshalMap(value, data, tag)
default: default:
return unmarshalScalar(value, data, tag) return u.unmarshalScalar(value, data, tag)
} }
} }
func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error { func (u unmarshaler) unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error {
if data == nil { if data == nil {
return nil return nil
} }
...@@ -114,7 +143,7 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa ...@@ -114,7 +143,7 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa
// unwrap any payloads // unwrap any payloads
if payload := tag.Get("payload"); payload != "" { if payload := tag.Get("payload"); payload != "" {
field, _ := t.FieldByName(payload) field, _ := t.FieldByName(payload)
return unmarshalAny(value.FieldByName(payload), data, field.Tag) return u.unmarshalAny(value.FieldByName(payload), data, field.Tag)
} }
for i := 0; i < t.NumField(); i++ { for i := 0; i < t.NumField(); i++ {
...@@ -128,9 +157,19 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa ...@@ -128,9 +157,19 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa
if locName := field.Tag.Get("locationName"); locName != "" { if locName := field.Tag.Get("locationName"); locName != "" {
name = locName name = locName
} }
if u.caseInsensitive {
if _, ok := mapData[name]; !ok {
// Fallback to uncased name search if the exact name didn't match.
for kn, v := range mapData {
if strings.EqualFold(kn, name) {
mapData[name] = v
}
}
}
}
member := value.FieldByIndex(field.Index) member := value.FieldByIndex(field.Index)
err := unmarshalAny(member, mapData[name], field.Tag) err := u.unmarshalAny(member, mapData[name], field.Tag)
if err != nil { if err != nil {
return err return err
} }
...@@ -138,7 +177,7 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa ...@@ -138,7 +177,7 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa
return nil return nil
} }
func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error { func (u unmarshaler) unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error {
if data == nil { if data == nil {
return nil return nil
} }
...@@ -153,7 +192,7 @@ func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) ...@@ -153,7 +192,7 @@ func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag)
} }
for i, c := range listData { for i, c := range listData {
err := unmarshalAny(value.Index(i), c, "") err := u.unmarshalAny(value.Index(i), c, "")
if err != nil { if err != nil {
return err return err
} }
...@@ -162,7 +201,7 @@ func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) ...@@ -162,7 +201,7 @@ func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag)
return nil return nil
} }
func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error { func (u unmarshaler) unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error {
if data == nil { if data == nil {
return nil return nil
} }
...@@ -179,14 +218,14 @@ func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) ...@@ -179,14 +218,14 @@ func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag)
kvalue := reflect.ValueOf(k) kvalue := reflect.ValueOf(k)
vvalue := reflect.New(value.Type().Elem()).Elem() vvalue := reflect.New(value.Type().Elem()).Elem()
unmarshalAny(vvalue, v, "") u.unmarshalAny(vvalue, v, "")
value.SetMapIndex(kvalue, vvalue) value.SetMapIndex(kvalue, vvalue)
} }
return nil return nil
} }
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error { func (u unmarshaler) unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error {
switch d := data.(type) { switch d := data.(type) {
case nil: case nil:
...@@ -222,16 +261,31 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa ...@@ -222,16 +261,31 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
default: default:
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
} }
case float64: case json.Number:
switch value.Interface().(type) { switch value.Interface().(type) {
case *int64: case *int64:
di := int64(d) // Retain the old behavior where we would just truncate the float64
// calling d.Int64() here could cause an invalid syntax error due to the usage of strconv.ParseInt
f, err := d.Float64()
if err != nil {
return err
}
di := int64(f)
value.Set(reflect.ValueOf(&di)) value.Set(reflect.ValueOf(&di))
case *float64: case *float64:
value.Set(reflect.ValueOf(&d)) f, err := d.Float64()
if err != nil {
return err
}
value.Set(reflect.ValueOf(&f))
case *time.Time: case *time.Time:
// Time unmarshaled from a float64 can only be epoch seconds float, ok := new(big.Float).SetString(d.String())
t := time.Unix(int64(d), 0).UTC() if !ok {
return fmt.Errorf("unsupported float time representation: %v", d.String())
}
float = float.Mul(float, millisecondsFloat)
ms, _ := float.Int64()
t := time.Unix(0, ms*1e6).UTC()
value.Set(reflect.ValueOf(&t)) value.Set(reflect.ValueOf(&t))
default: default:
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// requests and responses. // requests and responses.
package jsonrpc package jsonrpc
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/json.json build_test.go //go:generate go run -tags codegen ../../../private/model/cli/gen-protocol-tests ../../../models/protocol_tests/input/json.json build_test.go
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/json.json unmarshal_test.go //go:generate go run -tags codegen ../../../private/model/cli/gen-protocol-tests ../../../models/protocol_tests/output/json.json unmarshal_test.go
import ( import (
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
......
...@@ -54,7 +54,7 @@ func (u *UnmarshalTypedError) UnmarshalError( ...@@ -54,7 +54,7 @@ func (u *UnmarshalTypedError) UnmarshalError(
// If exception code is know, use associated constructor to get a value // If exception code is know, use associated constructor to get a value
// for the exception that the JSON body can be unmarshaled into. // for the exception that the JSON body can be unmarshaled into.
v := fn(respMeta) v := fn(respMeta)
err := jsonutil.UnmarshalJSON(v, body) err := jsonutil.UnmarshalJSONCaseInsensitive(v, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
// Package query provides serialization of AWS query requests, and responses. // Package query provides serialization of AWS query requests, and responses.
package query package query
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/query.json build_test.go //go:generate go run -tags codegen ../../../private/model/cli/gen-protocol-tests ../../../models/protocol_tests/input/query.json build_test.go
import ( import (
"net/url" "net/url"
......
package query package query
//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/query.json unmarshal_test.go //go:generate go run -tags codegen ../../../private/model/cli/gen-protocol-tests ../../../models/protocol_tests/output/query.json unmarshal_test.go
import ( import (
"encoding/xml" "encoding/xml"
......
...@@ -27,8 +27,8 @@ const ( ...@@ -27,8 +27,8 @@ const (
// RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z // RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z
ISO8601TimeFormat = "2006-01-02T15:04:05.999999999Z" ISO8601TimeFormat = "2006-01-02T15:04:05.999999999Z"
// This format is used for output time without seconds precision // This format is used for output time with fractional second precision up to milliseconds
ISO8601OutputTimeFormat = "2006-01-02T15:04:05Z" ISO8601OutputTimeFormat = "2006-01-02T15:04:05.999999999Z"
) )
// IsKnownTimestampFormat returns if the timestamp format name // IsKnownTimestampFormat returns if the timestamp format name
...@@ -48,7 +48,7 @@ func IsKnownTimestampFormat(name string) bool { ...@@ -48,7 +48,7 @@ func IsKnownTimestampFormat(name string) bool {
// FormatTime returns a string value of the time. // FormatTime returns a string value of the time.
func FormatTime(name string, t time.Time) string { func FormatTime(name string, t time.Time) string {
t = t.UTC() t = t.UTC().Truncate(time.Millisecond)
switch name { switch name {
case RFC822TimeFormatName: case RFC822TimeFormatName:
...@@ -56,7 +56,8 @@ func FormatTime(name string, t time.Time) string { ...@@ -56,7 +56,8 @@ func FormatTime(name string, t time.Time) string {
case ISO8601TimeFormatName: case ISO8601TimeFormatName:
return t.Format(ISO8601OutputTimeFormat) return t.Format(ISO8601OutputTimeFormat)
case UnixTimeFormatName: case UnixTimeFormatName:
return strconv.FormatInt(t.Unix(), 10) ms := t.UnixNano() / int64(time.Millisecond)
return strconv.FormatFloat(float64(ms)/1e3, 'f', -1, 64)
default: default:
panic("unknown timestamp format name, " + name) panic("unknown timestamp format name, " + name)
} }
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"reflect" "reflect"
"sort" "sort"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/aws/aws-sdk-go/private/protocol" "github.com/aws/aws-sdk-go/private/protocol"
...@@ -60,6 +61,14 @@ func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag refle ...@@ -60,6 +61,14 @@ func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag refle
return nil return nil
} }
xml := tag.Get("xml")
if len(xml) != 0 {
name := strings.SplitAfterN(xml, ",", 2)[0]
if name == "-" {
return nil
}
}
t := tag.Get("type") t := tag.Get("type")
if t == "" { if t == "" {
switch value.Kind() { switch value.Kind() {
......
...@@ -64,6 +64,14 @@ func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error { ...@@ -64,6 +64,14 @@ func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
// parse deserializes any value from the XMLNode. The type tag is used to infer the type, or reflect // parse deserializes any value from the XMLNode. The type tag is used to infer the type, or reflect
// will be used to determine the type from r. // will be used to determine the type from r.
func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error { func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
xml := tag.Get("xml")
if len(xml) != 0 {
name := strings.SplitAfterN(xml, ",", 2)[0]
if name == "-" {
return nil
}
}
rtype := r.Type() rtype := r.Type()
if rtype.Kind() == reflect.Ptr { if rtype.Kind() == reflect.Ptr {
rtype = rtype.Elem() // check kind of actual element type rtype = rtype.Elem() // check kind of actual element type
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment