mirror of https://github.com/kubernetes/kops.git
Merge pull request #5922 from spotinst/feature-spotinst-aws
New integration: Spotinst
This commit is contained in:
commit
1fbc6331c6
|
@ -1074,6 +1074,27 @@
|
|||
pruneopts = "UT"
|
||||
revision = "7fb2782df3d83e0036cc89f461ed0422628776f4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:1c5e9c80b7f295cb22373c59300c27c27c122a9c07410a6505b137afacf3583c"
|
||||
name = "github.com/spotinst/spotinst-sdk-go"
|
||||
packages = [
|
||||
"service/elastigroup",
|
||||
"service/elastigroup/providers/aws",
|
||||
"service/elastigroup/providers/azure",
|
||||
"service/elastigroup/providers/gce",
|
||||
"spotinst",
|
||||
"spotinst/client",
|
||||
"spotinst/credentials",
|
||||
"spotinst/log",
|
||||
"spotinst/session",
|
||||
"spotinst/util/jsonutil",
|
||||
"spotinst/util/stringutil",
|
||||
"spotinst/util/uritemplates",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "fed4677dbf8fe026a81e09e66fc38d863d091f9b"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:67ba0f5b63fa937e1e78273904a1fa0f7c2358c4dac967ac16e678f8e50e8aa5"
|
||||
name = "github.com/stretchr/testify"
|
||||
|
@ -1615,7 +1636,7 @@
|
|||
version = "kubernetes-1.11.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b1d67a042544a528d86e37e7ace303dc74313d18c3aab360037db5b797ddde0b"
|
||||
digest = "1:004509a1d109aec0996f8ea494a216f5e74268469661689178ca06134c9d6f02"
|
||||
name = "k8s.io/client-go"
|
||||
packages = [
|
||||
"discovery",
|
||||
|
@ -1794,7 +1815,6 @@
|
|||
"util/integer",
|
||||
"util/jsonpath",
|
||||
"util/retry",
|
||||
"util/workqueue",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "2cefa64ff137e128daeddbd1775cd775708a05bf"
|
||||
|
@ -2178,6 +2198,14 @@
|
|||
"github.com/spf13/cobra/doc",
|
||||
"github.com/spf13/pflag",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup",
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws",
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst",
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/client",
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/credentials",
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/log",
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/session",
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/urfave/cli",
|
||||
"github.com/vmware/govmomi",
|
||||
|
@ -2203,7 +2231,6 @@
|
|||
"google.golang.org/api/storage/v1",
|
||||
"gopkg.in/gcfg.v1",
|
||||
"gopkg.in/yaml.v2",
|
||||
"k8s.io/api/apps/v1",
|
||||
"k8s.io/api/core/v1",
|
||||
"k8s.io/api/extensions/v1beta1",
|
||||
"k8s.io/api/rbac/v1beta1",
|
||||
|
@ -2228,6 +2255,7 @@
|
|||
"k8s.io/apimachinery/pkg/util/validation",
|
||||
"k8s.io/apimachinery/pkg/util/validation/field",
|
||||
"k8s.io/apimachinery/pkg/util/wait",
|
||||
"k8s.io/apimachinery/pkg/util/yaml",
|
||||
"k8s.io/apimachinery/pkg/version",
|
||||
"k8s.io/apimachinery/pkg/watch",
|
||||
"k8s.io/apiserver/pkg/authentication/user",
|
||||
|
@ -2243,23 +2271,17 @@
|
|||
"k8s.io/apiserver/pkg/util/logs",
|
||||
"k8s.io/client-go/discovery",
|
||||
"k8s.io/client-go/discovery/fake",
|
||||
"k8s.io/client-go/informers",
|
||||
"k8s.io/client-go/kubernetes",
|
||||
"k8s.io/client-go/kubernetes/fake",
|
||||
"k8s.io/client-go/kubernetes/scheme",
|
||||
"k8s.io/client-go/kubernetes/typed/core/v1",
|
||||
"k8s.io/client-go/listers/apps/v1",
|
||||
"k8s.io/client-go/plugin/pkg/client/auth",
|
||||
"k8s.io/client-go/rest",
|
||||
"k8s.io/client-go/testing",
|
||||
"k8s.io/client-go/tools/cache",
|
||||
"k8s.io/client-go/tools/clientcmd",
|
||||
"k8s.io/client-go/tools/clientcmd/api",
|
||||
"k8s.io/client-go/tools/clientcmd/api/v1",
|
||||
"k8s.io/client-go/tools/record",
|
||||
"k8s.io/client-go/util/flowcontrol",
|
||||
"k8s.io/client-go/util/homedir",
|
||||
"k8s.io/client-go/util/workqueue",
|
||||
"k8s.io/code-generator/cmd/client-gen",
|
||||
"k8s.io/code-generator/cmd/conversion-gen",
|
||||
"k8s.io/code-generator/cmd/deepcopy-gen",
|
||||
|
|
|
@ -139,6 +139,10 @@ type CreateClusterOptions struct {
|
|||
// We can remove this once we support higher versions.
|
||||
VSphereDatastore string
|
||||
|
||||
// Spotinst options
|
||||
SpotinstProduct string
|
||||
SpotinstOrientation string
|
||||
|
||||
// ConfigBase is the location where we will store the configuration, it defaults to the state store
|
||||
ConfigBase string
|
||||
|
||||
|
@ -353,6 +357,13 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
cmd.Flags().StringVar(&options.VSphereCoreDNSServer, "vsphere-coredns-server", options.VSphereCoreDNSServer, "vsphere-coredns-server is required for vSphere.")
|
||||
cmd.Flags().StringVar(&options.VSphereDatastore, "vsphere-datastore", options.VSphereDatastore, "vsphere-datastore is required for vSphere. Set a valid datastore in which to store dynamic provision volumes.")
|
||||
}
|
||||
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
// Spotinst flags
|
||||
cmd.Flags().StringVar(&options.SpotinstProduct, "spotinst-product", options.SpotinstProduct, "Set the product description (valid values: Linux/UNIX, Linux/UNIX (Amazon VPC), Windows and Windows (Amazon VPC))")
|
||||
cmd.Flags().StringVar(&options.SpotinstOrientation, "spotinst-orientation", options.SpotinstOrientation, "Set the prediction strategy (valid values: balanced, cost, equal-distribution and availability)")
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -840,6 +851,18 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
|
|||
}
|
||||
cluster.Spec.CloudConfig.VSphereDatastore = fi.String(c.VSphereDatastore)
|
||||
}
|
||||
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
if cluster.Spec.CloudConfig == nil {
|
||||
cluster.Spec.CloudConfig = &api.CloudConfiguration{}
|
||||
}
|
||||
if c.SpotinstProduct != "" {
|
||||
cluster.Spec.CloudConfig.SpotinstProduct = fi.String(c.SpotinstProduct)
|
||||
}
|
||||
if c.SpotinstOrientation != "" {
|
||||
cluster.Spec.CloudConfig.SpotinstOrientation = fi.String(c.SpotinstOrientation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate project
|
||||
|
|
|
@ -108,6 +108,7 @@ k8s.io/kops/pkg/model/gcemodel
|
|||
k8s.io/kops/pkg/model/iam
|
||||
k8s.io/kops/pkg/model/openstackmodel
|
||||
k8s.io/kops/pkg/model/resources
|
||||
k8s.io/kops/pkg/model/spotinstmodel
|
||||
k8s.io/kops/pkg/model/vspheremodel
|
||||
k8s.io/kops/pkg/openapi
|
||||
k8s.io/kops/pkg/pki
|
||||
|
@ -120,6 +121,7 @@ k8s.io/kops/pkg/resources/digitalocean/dns
|
|||
k8s.io/kops/pkg/resources/gce
|
||||
k8s.io/kops/pkg/resources/openstack
|
||||
k8s.io/kops/pkg/resources/ops
|
||||
k8s.io/kops/pkg/resources/spotinst
|
||||
k8s.io/kops/pkg/sshcredentials
|
||||
k8s.io/kops/pkg/systemd
|
||||
k8s.io/kops/pkg/templates
|
||||
|
@ -163,6 +165,7 @@ k8s.io/kops/upup/pkg/fi/cloudup/gce
|
|||
k8s.io/kops/upup/pkg/fi/cloudup/gcetasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/openstack
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/spotinsttasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/terraform
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/vsphere
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/vspheretasks
|
||||
|
|
|
@ -493,6 +493,9 @@ type CloudConfiguration struct {
|
|||
VSphereResourcePool *string `json:"vSphereResourcePool,omitempty"`
|
||||
VSphereDatastore *string `json:"vSphereDatastore,omitempty"`
|
||||
VSphereCoreDNSServer *string `json:"vSphereCoreDNSServer,omitempty"`
|
||||
// Spotinst cloud-config specs
|
||||
SpotinstProduct *string `json:"spotinstProduct,omitempty"`
|
||||
SpotinstOrientation *string `json:"spotinstOrientation,omitempty"`
|
||||
}
|
||||
|
||||
// HasAdmissionController checks if a specific admission controller is enabled
|
||||
|
|
|
@ -493,6 +493,9 @@ type CloudConfiguration struct {
|
|||
VSphereResourcePool *string `json:"vSphereResourcePool,omitempty"`
|
||||
VSphereDatastore *string `json:"vSphereDatastore,omitempty"`
|
||||
VSphereCoreDNSServer *string `json:"vSphereCoreDNSServer,omitempty"`
|
||||
// Spotinst cloud-config specs
|
||||
SpotinstProduct *string `json:"spotinstProduct,omitempty"`
|
||||
SpotinstOrientation *string `json:"spotinstOrientation,omitempty"`
|
||||
}
|
||||
|
||||
// HasAdmissionController checks if a specific admission controller is enabled
|
||||
|
|
|
@ -656,6 +656,8 @@ func autoConvert_v1alpha1_CloudConfiguration_To_kops_CloudConfiguration(in *Clou
|
|||
out.VSphereResourcePool = in.VSphereResourcePool
|
||||
out.VSphereDatastore = in.VSphereDatastore
|
||||
out.VSphereCoreDNSServer = in.VSphereCoreDNSServer
|
||||
out.SpotinstProduct = in.SpotinstProduct
|
||||
out.SpotinstOrientation = in.SpotinstOrientation
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -677,6 +679,8 @@ func autoConvert_kops_CloudConfiguration_To_v1alpha1_CloudConfiguration(in *kops
|
|||
out.VSphereResourcePool = in.VSphereResourcePool
|
||||
out.VSphereDatastore = in.VSphereDatastore
|
||||
out.VSphereCoreDNSServer = in.VSphereCoreDNSServer
|
||||
out.SpotinstProduct = in.SpotinstProduct
|
||||
out.SpotinstOrientation = in.SpotinstOrientation
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -525,6 +525,24 @@ func (in *CloudConfiguration) DeepCopyInto(out *CloudConfiguration) {
|
|||
**out = **in
|
||||
}
|
||||
}
|
||||
if in.SpotinstProduct != nil {
|
||||
in, out := &in.SpotinstProduct, &out.SpotinstProduct
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
if in.SpotinstOrientation != nil {
|
||||
in, out := &in.SpotinstOrientation, &out.SpotinstOrientation
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -493,6 +493,9 @@ type CloudConfiguration struct {
|
|||
VSphereResourcePool *string `json:"vSphereResourcePool,omitempty"`
|
||||
VSphereDatastore *string `json:"vSphereDatastore,omitempty"`
|
||||
VSphereCoreDNSServer *string `json:"vSphereCoreDNSServer,omitempty"`
|
||||
// Spotinst cloud-config specs
|
||||
SpotinstProduct *string `json:"spotinstProduct,omitempty"`
|
||||
SpotinstOrientation *string `json:"spotinstOrientation,omitempty"`
|
||||
}
|
||||
|
||||
// HasAdmissionController checks if a specific admission controller is enabled
|
||||
|
|
|
@ -692,6 +692,8 @@ func autoConvert_v1alpha2_CloudConfiguration_To_kops_CloudConfiguration(in *Clou
|
|||
out.VSphereResourcePool = in.VSphereResourcePool
|
||||
out.VSphereDatastore = in.VSphereDatastore
|
||||
out.VSphereCoreDNSServer = in.VSphereCoreDNSServer
|
||||
out.SpotinstProduct = in.SpotinstProduct
|
||||
out.SpotinstOrientation = in.SpotinstOrientation
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -713,6 +715,8 @@ func autoConvert_kops_CloudConfiguration_To_v1alpha2_CloudConfiguration(in *kops
|
|||
out.VSphereResourcePool = in.VSphereResourcePool
|
||||
out.VSphereDatastore = in.VSphereDatastore
|
||||
out.VSphereCoreDNSServer = in.VSphereCoreDNSServer
|
||||
out.SpotinstProduct = in.SpotinstProduct
|
||||
out.SpotinstOrientation = in.SpotinstOrientation
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -498,6 +498,24 @@ func (in *CloudConfiguration) DeepCopyInto(out *CloudConfiguration) {
|
|||
**out = **in
|
||||
}
|
||||
}
|
||||
if in.SpotinstProduct != nil {
|
||||
in, out := &in.SpotinstProduct, &out.SpotinstProduct
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
if in.SpotinstOrientation != nil {
|
||||
in, out := &in.SpotinstOrientation, &out.SpotinstOrientation
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -76,8 +76,10 @@ func awsValidateMachineType(fieldPath *field.Path, machineType string) field.Err
|
|||
allErrs := field.ErrorList{}
|
||||
|
||||
if machineType != "" {
|
||||
if _, err := awsup.GetMachineTypeInfo(machineType); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath, machineType, "machine type specified is invalid"))
|
||||
for _, typ := range strings.Split(machineType, ",") {
|
||||
if _, err := awsup.GetMachineTypeInfo(typ); err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fieldPath, typ, "machine type specified is invalid"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,15 +94,16 @@ func awsValidateAMIforNVMe(fieldPath *field.Path, ig *kops.InstanceGroup) field.
|
|||
allErrs := field.ErrorList{}
|
||||
|
||||
for _, prefix := range NVMe_INSTANCE_PREFIXES {
|
||||
if strings.Contains(strings.ToUpper(ig.Spec.MachineType), strings.ToUpper(prefix)) {
|
||||
glog.V(2).Infof("machineType %s requires an image based on stretch to operate. Trying to check compatibility", ig.Spec.MachineType)
|
||||
if strings.Contains(ig.Spec.Image, "jessie") {
|
||||
errString := fmt.Sprintf("%s cannot use machineType %s with image based on Debian jessie.", ig.Name, ig.Spec.MachineType)
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath, errString))
|
||||
continue
|
||||
for _, machineType := range strings.Split(ig.Spec.MachineType, ",") {
|
||||
if strings.Contains(strings.ToUpper(machineType), strings.ToUpper(prefix)) {
|
||||
glog.V(2).Infof("machineType %s requires an image based on stretch to operate. Trying to check compatibility", machineType)
|
||||
if strings.Contains(ig.Spec.Image, "jessie") {
|
||||
errString := fmt.Sprintf("%s cannot use machineType %s with image based on Debian jessie.", ig.Name, machineType)
|
||||
allErrs = append(allErrs, field.Forbidden(fieldPath, errString))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
|
|
@ -603,6 +603,24 @@ func (in *CloudConfiguration) DeepCopyInto(out *CloudConfiguration) {
|
|||
**out = **in
|
||||
}
|
||||
}
|
||||
if in.SpotinstProduct != nil {
|
||||
in, out := &in.SpotinstProduct, &out.SpotinstProduct
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
if in.SpotinstOrientation != nil {
|
||||
in, out := &in.SpotinstOrientation, &out.SpotinstOrientation
|
||||
if *in == nil {
|
||||
*out = nil
|
||||
} else {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,9 @@ var GoogleCloudBucketAcl = New("GoogleCloudBucketAcl", Bool(false))
|
|||
// EnableNodeAuthorization enables the node authorization features
|
||||
var EnableNodeAuthorization = New("EnableNodeAuthorization", Bool(false))
|
||||
|
||||
// Spotinst toggles the use of Spotinst integration.
|
||||
var Spotinst = New("Spotinst", Bool(false))
|
||||
|
||||
var flags = make(map[string]*FeatureFlag)
|
||||
var flagsMutex sync.Mutex
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package awsmodel
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
|
@ -98,7 +99,7 @@ func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
|||
},
|
||||
IAMInstanceProfile: link,
|
||||
ImageID: s(ig.Spec.Image),
|
||||
InstanceType: s(ig.Spec.MachineType),
|
||||
InstanceType: s(strings.Split(ig.Spec.MachineType, ",")[0]),
|
||||
InstanceMonitoring: ig.Spec.DetailedInstanceMonitoring,
|
||||
|
||||
RootVolumeSize: i64(int64(volumeSize)),
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["elastigroup.go"],
|
||||
importpath = "k8s.io/kops/pkg/model/spotinstmodel",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/kops:go_default_library",
|
||||
"//pkg/model:go_default_library",
|
||||
"//pkg/model/awsmodel:go_default_library",
|
||||
"//pkg/model/defaults:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/awstasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/spotinsttasks:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,340 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package spotinstmodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/model"
|
||||
"k8s.io/kops/pkg/model/awsmodel"
|
||||
"k8s.io/kops/pkg/model/defaults"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/spotinsttasks"
|
||||
)
|
||||
|
||||
const (
|
||||
// InstanceGroupLabelOrientation is the metadata label used on the
|
||||
// instance group to specify which orientation should be used.
|
||||
InstanceGroupLabelOrientation = "spotinst.io/orientation"
|
||||
|
||||
// InstanceGroupLabelUtilizeReservedInstances is the metadata label used
|
||||
// on the instance group to specify whether reserved instances should be
|
||||
// utilized.
|
||||
InstanceGroupLabelUtilizeReservedInstances = "spotinst.io/utilize-reserved-instances"
|
||||
|
||||
// InstanceGroupLabelFallbackToOnDemand is the metadata label used on the
|
||||
// instance group to specify whether fallback to on-demand instances should
|
||||
// be enabled.
|
||||
InstanceGroupLabelFallbackToOnDemand = "spotinst.io/fallback-to-ondemand"
|
||||
|
||||
// InstanceGroupLabelAutoScalerDisabled is the metadata label used on the
|
||||
// instance group to specify whether the auto-scaler should be enabled.
|
||||
InstanceGroupLabelAutoScalerDisabled = "spotinst.io/autoscaler-disabled"
|
||||
|
||||
// InstanceGroupLabelAutoScalerDefaultNodeLabels is the metadata label used on the
|
||||
// instance group to specify whether default node labels should be set for
|
||||
// the auto-scaler.
|
||||
InstanceGroupLabelAutoScalerDefaultNodeLabels = "spotinst.io/autoscaler-default-node-labels"
|
||||
)
|
||||
|
||||
// ElastigroupModelBuilder configures Elastigroup objects
|
||||
type ElastigroupModelBuilder struct {
|
||||
*awsmodel.AWSModelContext
|
||||
|
||||
BootstrapScript *model.BootstrapScript
|
||||
Lifecycle *fi.Lifecycle
|
||||
SecurityLifecycle *fi.Lifecycle
|
||||
}
|
||||
|
||||
var _ fi.ModelBuilder = &ElastigroupModelBuilder{}
|
||||
|
||||
func (b *ElastigroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
||||
for _, ig := range b.InstanceGroups {
|
||||
glog.V(2).Infof("Building instance group %q", b.AutoscalingGroupName(ig))
|
||||
|
||||
group := &spotinsttasks.Elastigroup{
|
||||
Lifecycle: b.Lifecycle,
|
||||
Name: fi.String(b.AutoscalingGroupName(ig)),
|
||||
ImageID: fi.String(ig.Spec.Image),
|
||||
Monitoring: fi.Bool(false),
|
||||
OnDemandInstanceType: fi.String(strings.Split(ig.Spec.MachineType, ",")[0]),
|
||||
SpotInstanceTypes: strings.Split(ig.Spec.MachineType, ","),
|
||||
SecurityGroups: []*awstasks.SecurityGroup{
|
||||
b.LinkToSecurityGroup(ig.Spec.Role),
|
||||
},
|
||||
}
|
||||
|
||||
// Cloud config.
|
||||
if cfg := b.Cluster.Spec.CloudConfig; cfg != nil {
|
||||
group.Product = cfg.SpotinstProduct
|
||||
group.Orientation = cfg.SpotinstOrientation
|
||||
}
|
||||
|
||||
// Strategy.
|
||||
for k, v := range ig.ObjectMeta.Labels {
|
||||
switch k {
|
||||
case InstanceGroupLabelOrientation:
|
||||
group.Orientation = fi.String(v)
|
||||
break
|
||||
|
||||
case InstanceGroupLabelUtilizeReservedInstances:
|
||||
b, err := parseBool(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group.UtilizeReservedInstances = b
|
||||
break
|
||||
|
||||
case InstanceGroupLabelFallbackToOnDemand:
|
||||
b, err := parseBool(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group.FallbackToOnDemand = b
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Instance profile.
|
||||
iprof, err := b.LinkToIAMInstanceProfile(ig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group.IAMInstanceProfile = iprof
|
||||
|
||||
// Root volume.
|
||||
volumeSize := fi.Int32Value(ig.Spec.RootVolumeSize)
|
||||
if volumeSize == 0 {
|
||||
var err error
|
||||
volumeSize, err = defaults.DefaultInstanceGroupVolumeSize(ig.Spec.Role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
volumeType := fi.StringValue(ig.Spec.RootVolumeType)
|
||||
if volumeType == "" {
|
||||
volumeType = awsmodel.DefaultVolumeType
|
||||
}
|
||||
|
||||
group.RootVolumeSize = fi.Int64(int64(volumeSize))
|
||||
group.RootVolumeType = fi.String(volumeType)
|
||||
group.RootVolumeOptimization = ig.Spec.RootVolumeOptimization
|
||||
|
||||
// Tenancy.
|
||||
if ig.Spec.Tenancy != "" {
|
||||
group.Tenancy = fi.String(ig.Spec.Tenancy)
|
||||
}
|
||||
|
||||
// Risk.
|
||||
var risk float64
|
||||
switch ig.Spec.Role {
|
||||
case kops.InstanceGroupRoleMaster:
|
||||
risk = 0
|
||||
case kops.InstanceGroupRoleNode:
|
||||
risk = 100
|
||||
case kops.InstanceGroupRoleBastion:
|
||||
risk = 0
|
||||
default:
|
||||
return fmt.Errorf("spotinst: kops.Role not found %s", ig.Spec.Role)
|
||||
}
|
||||
group.Risk = &risk
|
||||
|
||||
// Security groups.
|
||||
for _, id := range ig.Spec.AdditionalSecurityGroups {
|
||||
sgTask := &awstasks.SecurityGroup{
|
||||
Name: fi.String(id),
|
||||
ID: fi.String(id),
|
||||
Shared: fi.Bool(true),
|
||||
}
|
||||
if err := c.EnsureTask(sgTask); err != nil {
|
||||
return err
|
||||
}
|
||||
group.SecurityGroups = append(group.SecurityGroups, sgTask)
|
||||
}
|
||||
|
||||
// SSH Key.
|
||||
sshKey, err := b.LinkToSSHKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group.SSHKey = sshKey
|
||||
|
||||
// Load balancer.
|
||||
var lb *awstasks.LoadBalancer
|
||||
switch ig.Spec.Role {
|
||||
case kops.InstanceGroupRoleMaster:
|
||||
if b.UseLoadBalancerForAPI() {
|
||||
lb = b.LinkToELB("api")
|
||||
}
|
||||
case kops.InstanceGroupRoleBastion:
|
||||
lb = b.LinkToELB(model.BastionELBSecurityGroupPrefix)
|
||||
}
|
||||
if lb != nil {
|
||||
group.LoadBalancer = lb
|
||||
}
|
||||
|
||||
// User data.
|
||||
userData, err := b.BootstrapScript.ResourceNodeUp(ig, b.Cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group.UserData = userData
|
||||
|
||||
// Public IP.
|
||||
subnetMap := make(map[string]*kops.ClusterSubnetSpec)
|
||||
for i := range b.Cluster.Spec.Subnets {
|
||||
subnet := &b.Cluster.Spec.Subnets[i]
|
||||
subnetMap[subnet.Name] = subnet
|
||||
}
|
||||
|
||||
var subnetType kops.SubnetType
|
||||
for _, subnetName := range ig.Spec.Subnets {
|
||||
subnet := subnetMap[subnetName]
|
||||
if subnet == nil {
|
||||
return fmt.Errorf("spotinst: InstanceGroup %q uses subnet %q that does not exist", ig.ObjectMeta.Name, subnetName)
|
||||
}
|
||||
if subnetType != "" && subnetType != subnet.Type {
|
||||
return fmt.Errorf("spotinst: InstanceGroup %q cannot be in subnets of different Type", ig.ObjectMeta.Name)
|
||||
}
|
||||
subnetType = subnet.Type
|
||||
}
|
||||
|
||||
associatePublicIP := true
|
||||
switch subnetType {
|
||||
case kops.SubnetTypePublic, kops.SubnetTypeUtility:
|
||||
associatePublicIP = true
|
||||
if ig.Spec.AssociatePublicIP != nil {
|
||||
associatePublicIP = *ig.Spec.AssociatePublicIP
|
||||
}
|
||||
case kops.SubnetTypePrivate:
|
||||
associatePublicIP = false
|
||||
if ig.Spec.AssociatePublicIP != nil {
|
||||
if *ig.Spec.AssociatePublicIP {
|
||||
glog.Warningf("Ignoring AssociatePublicIP=true for private InstanceGroup %q", ig.ObjectMeta.Name)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("spotinst: unknown subnet type %q", subnetType)
|
||||
}
|
||||
group.AssociatePublicIP = &associatePublicIP
|
||||
|
||||
// Subnets.
|
||||
subnets, err := b.GatherSubnets(ig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(subnets) == 0 {
|
||||
return fmt.Errorf("spotinst: could not determine any subnets for InstanceGroup %q; subnets was %s", ig.ObjectMeta.Name, ig.Spec.Subnets)
|
||||
}
|
||||
for _, subnet := range subnets {
|
||||
group.Subnets = append(group.Subnets, b.LinkToSubnet(subnet))
|
||||
}
|
||||
|
||||
// Capacity.
|
||||
minSize := int32(1)
|
||||
if ig.Spec.MinSize != nil {
|
||||
minSize = fi.Int32Value(ig.Spec.MinSize)
|
||||
} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
|
||||
minSize = 2
|
||||
}
|
||||
|
||||
maxSize := int32(1)
|
||||
if ig.Spec.MaxSize != nil {
|
||||
maxSize = *ig.Spec.MaxSize
|
||||
} else if ig.Spec.Role == kops.InstanceGroupRoleNode {
|
||||
maxSize = 2
|
||||
}
|
||||
|
||||
group.MinSize = fi.Int64(int64(minSize))
|
||||
group.MaxSize = fi.Int64(int64(maxSize))
|
||||
|
||||
// Tags.
|
||||
tags, err := b.CloudTagsForInstanceGroup(ig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("spotinst: error building cloud tags: %v", err)
|
||||
}
|
||||
tags[awsup.TagClusterName] = b.ClusterName()
|
||||
tags["Name"] = b.AutoscalingGroupName(ig)
|
||||
group.Tags = tags
|
||||
|
||||
// Auto Scaler.
|
||||
if ig.Spec.Role != kops.InstanceGroupRoleBastion {
|
||||
group.AutoScalerClusterID = fi.String(b.ClusterName())
|
||||
|
||||
// Toggle auto scaler's features.
|
||||
var autoScalerDisabled bool
|
||||
var autoScalerNodeLabels bool
|
||||
{
|
||||
for k, v := range ig.ObjectMeta.Labels {
|
||||
switch k {
|
||||
case InstanceGroupLabelAutoScalerDisabled:
|
||||
b, err := parseBool(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
autoScalerDisabled = fi.BoolValue(b)
|
||||
break
|
||||
|
||||
case InstanceGroupLabelAutoScalerDefaultNodeLabels:
|
||||
b, err := parseBool(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
autoScalerNodeLabels = fi.BoolValue(b)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the auto scaler.
|
||||
group.AutoScalerEnabled = fi.Bool(!autoScalerDisabled)
|
||||
|
||||
// Set the node labels.
|
||||
if ig.Spec.Role == kops.InstanceGroupRoleNode {
|
||||
nodeLabels := make(map[string]string)
|
||||
for k, v := range ig.Spec.NodeLabels {
|
||||
if strings.HasPrefix(k, kops.NodeLabelInstanceGroup) && !autoScalerNodeLabels {
|
||||
continue
|
||||
}
|
||||
nodeLabels[k] = v
|
||||
}
|
||||
if len(nodeLabels) > 0 {
|
||||
group.AutoScalerNodeLabels = nodeLabels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.AddTask(group)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseBool(str string) (*bool, error) {
|
||||
b, err := strconv.ParseBool(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("spotinst: unexpected boolean value: %q", str)
|
||||
}
|
||||
return fi.Bool(b), nil
|
||||
}
|
|
@ -18,7 +18,9 @@ go_library(
|
|||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/dns:go_default_library",
|
||||
"//pkg/featureflag:go_default_library",
|
||||
"//pkg/resources:go_default_library",
|
||||
"//pkg/resources/spotinst:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
|
||||
|
|
|
@ -34,7 +34,9 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kops/pkg/dns"
|
||||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/pkg/resources"
|
||||
"k8s.io/kops/pkg/resources/spotinst"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
)
|
||||
|
@ -74,8 +76,6 @@ func ListResourcesAWS(cloud awsup.AWSCloud, clusterName string) (map[string]*res
|
|||
ListELBs,
|
||||
ListELBV2s,
|
||||
ListTargetGroups,
|
||||
// ASG
|
||||
ListAutoScalingGroups,
|
||||
|
||||
// Route 53
|
||||
ListRoute53Records,
|
||||
|
@ -83,6 +83,15 @@ func ListResourcesAWS(cloud awsup.AWSCloud, clusterName string) (map[string]*res
|
|||
ListIAMInstanceProfiles,
|
||||
ListIAMRoles,
|
||||
}
|
||||
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
// Spotinst Elastigroups
|
||||
listFunctions = append(listFunctions, ListSpotinstElastigroups)
|
||||
} else {
|
||||
// AutoScaling Groups
|
||||
listFunctions = append(listFunctions, ListAutoScalingGroups)
|
||||
}
|
||||
|
||||
for _, fn := range listFunctions {
|
||||
rt, err := fn(cloud, clusterName)
|
||||
if err != nil {
|
||||
|
@ -2024,6 +2033,10 @@ func ListIAMInstanceProfiles(cloud fi.Cloud, clusterName string) ([]*resources.R
|
|||
return resourceTrackers, nil
|
||||
}
|
||||
|
||||
func ListSpotinstElastigroups(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) {
|
||||
return spotinst.ListGroups(cloud.(awsup.AWSCloud).Spotinst(), clusterName)
|
||||
}
|
||||
|
||||
func FindName(tags []*ec2.Tag) string {
|
||||
if name, found := awsup.FindEC2Tag(tags, "Name"); found {
|
||||
return name
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"aws.go",
|
||||
"interfaces.go",
|
||||
"resources.go",
|
||||
"spotinst.go",
|
||||
],
|
||||
importpath = "k8s.io/kops/pkg/resources/spotinst",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//:go_default_library",
|
||||
"//pkg/apis/kops:go_default_library",
|
||||
"//pkg/cloudinstances:go_default_library",
|
||||
"//pkg/resources:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/log:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package spotinst
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
type awsService struct {
|
||||
svc aws.Service
|
||||
}
|
||||
|
||||
// List returns a list of Elastigroups.
|
||||
func (s *awsService) List(ctx context.Context) ([]Elastigroup, error) {
|
||||
output, err := s.svc.List(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups := make([]Elastigroup, len(output.Groups))
|
||||
for i, group := range output.Groups {
|
||||
groups[i] = &awsElastigroup{group}
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// Create creates a new Elastigroup and returns its ID.
|
||||
func (s *awsService) Create(ctx context.Context, group Elastigroup) (string, error) {
|
||||
input := &aws.CreateGroupInput{
|
||||
Group: group.Obj().(*aws.Group),
|
||||
}
|
||||
|
||||
output, err := s.svc.Create(ctx, input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fi.StringValue(output.Group.ID), nil
|
||||
}
|
||||
|
||||
// Read returns an existing Elastigroup by ID.
|
||||
func (s *awsService) Read(ctx context.Context, groupID string) (Elastigroup, error) {
|
||||
input := &aws.ReadGroupInput{
|
||||
GroupID: fi.String(groupID),
|
||||
}
|
||||
|
||||
output, err := s.svc.Read(ctx, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &awsElastigroup{output.Group}, nil
|
||||
}
|
||||
|
||||
// Update updates an existing Elastigroup.
|
||||
func (s *awsService) Update(ctx context.Context, group Elastigroup) error {
|
||||
input := &aws.UpdateGroupInput{
|
||||
Group: group.Obj().(*aws.Group),
|
||||
}
|
||||
|
||||
_, err := s.svc.Update(ctx, input)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// Delete deletes an existing Elastigroup by ID.
|
||||
func (s *awsService) Delete(ctx context.Context, groupID string) error {
|
||||
input := &aws.DeleteGroupInput{
|
||||
GroupID: fi.String(groupID),
|
||||
}
|
||||
|
||||
_, err := s.svc.Delete(ctx, input)
|
||||
return err
|
||||
}
|
||||
|
||||
// Detach removes one or more instances from the specified Elastigroup.
|
||||
func (s *awsService) Detach(ctx context.Context, groupID string, instanceIDs []string) error {
|
||||
input := &aws.DetachGroupInput{
|
||||
GroupID: fi.String(groupID),
|
||||
InstanceIDs: instanceIDs,
|
||||
ShouldDecrementTargetCapacity: fi.Bool(false),
|
||||
ShouldTerminateInstances: fi.Bool(true),
|
||||
}
|
||||
|
||||
_, err := s.svc.Detach(ctx, input)
|
||||
return err
|
||||
}
|
||||
|
||||
// Instances returns a list of all instances that belong to specified Elastigroup.
|
||||
func (s *awsService) Instances(ctx context.Context, groupID string) ([]Instance, error) {
|
||||
input := &aws.StatusGroupInput{
|
||||
GroupID: fi.String(groupID),
|
||||
}
|
||||
|
||||
output, err := s.svc.Status(ctx, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instances := make([]Instance, len(output.Instances))
|
||||
for i, instance := range output.Instances {
|
||||
instances[i] = &awsInstance{instance}
|
||||
}
|
||||
|
||||
return instances, err
|
||||
}
|
||||
|
||||
type awsElastigroup struct {
|
||||
obj *aws.Group
|
||||
}
|
||||
|
||||
// Id returns the ID of the Elastigroup.
|
||||
func (e *awsElastigroup) Id() string { return fi.StringValue(e.obj.ID) }
|
||||
|
||||
// Name returns the name of the Elastigroup.
|
||||
func (e *awsElastigroup) Name() string { return fi.StringValue(e.obj.Name) }
|
||||
|
||||
// MinSize returns the minimum size of the Elastigroup.
|
||||
func (e *awsElastigroup) MinSize() int { return fi.IntValue(e.obj.Capacity.Minimum) }
|
||||
|
||||
// MaxSize returns the maximum size of the Elastigroup.
|
||||
func (e *awsElastigroup) MaxSize() int { return fi.IntValue(e.obj.Capacity.Maximum) }
|
||||
|
||||
// Obj returns the raw object which is a cloud-specific implementation.
|
||||
func (e *awsElastigroup) Obj() interface{} { return e.obj }
|
||||
|
||||
type awsInstance struct {
|
||||
obj *aws.Instance
|
||||
}
|
||||
|
||||
// Id returns the ID of the instance.
|
||||
func (i *awsInstance) Id() string { return fi.StringValue(i.obj.ID) }
|
||||
|
||||
// Obj returns the raw object which is a cloud-specific implementation.
|
||||
func (i *awsInstance) Obj() interface{} { return i.obj }
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package spotinst
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
// Elastigroup contains configuration info and functions to control a set
|
||||
// of instances.
|
||||
Elastigroup interface {
|
||||
// Id returns the ID of the Elastigroup.
|
||||
Id() string
|
||||
|
||||
// Name returns the name of the Elastigroup.
|
||||
Name() string
|
||||
|
||||
// MinSize returns the minimum size of the Elastigroup.
|
||||
MinSize() int
|
||||
|
||||
// MaxSize returns the maximum size of the Elastigroup.
|
||||
MaxSize() int
|
||||
|
||||
// Obj returns the raw object which is a cloud-specific implementation.
|
||||
Obj() interface{}
|
||||
}
|
||||
|
||||
// Instance wraps a cloud-specific instance object.
|
||||
Instance interface {
|
||||
// Id returns the ID of the instance.
|
||||
Id() string
|
||||
|
||||
// Obj returns the raw object which is a cloud-specific implementation.
|
||||
Obj() interface{}
|
||||
}
|
||||
|
||||
// Service is an interface that a cloud provider that is supported
|
||||
// by Spotinst MUST implement to manage its Elastigroups.
|
||||
Service interface {
|
||||
// List returns a list of Elastigroups.
|
||||
List(ctx context.Context) ([]Elastigroup, error)
|
||||
|
||||
// Create creates a new Elastigroup and returns its ID.
|
||||
Create(ctx context.Context, group Elastigroup) (string, error)
|
||||
|
||||
// Read returns an existing Elastigroup by ID.
|
||||
Read(ctx context.Context, groupID string) (Elastigroup, error)
|
||||
|
||||
// Update updates an existing Elastigroup.
|
||||
Update(ctx context.Context, group Elastigroup) error
|
||||
|
||||
// Delete deletes an existing Elastigroup by ID.
|
||||
Delete(ctx context.Context, groupID string) error
|
||||
|
||||
// Detach removes one or more instances from the specified Elastigroup.
|
||||
Detach(ctx context.Context, groupID string, instanceIDs []string) error
|
||||
|
||||
// Instances returns a list of all instances that belong to specified Elastigroup.
|
||||
Instances(ctx context.Context, groupID string) ([]Instance, error)
|
||||
}
|
||||
)
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package spotinst
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/cloudinstances"
|
||||
"k8s.io/kops/pkg/resources"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
// ListGroups returns a list of all Elastigroups as Resource objects.
|
||||
func ListGroups(svc Service, clusterName string) ([]*resources.Resource, error) {
|
||||
glog.V(2).Info("Listing all Elastigroups")
|
||||
|
||||
groups, err := svc.List(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resourceTrackers []*resources.Resource
|
||||
for _, group := range groups {
|
||||
if strings.HasSuffix(group.Name(), clusterName) {
|
||||
resource := &resources.Resource{
|
||||
ID: group.Id(),
|
||||
Name: group.Name(),
|
||||
Obj: group,
|
||||
Deleter: deleter(svc, group),
|
||||
Dumper: dumper,
|
||||
}
|
||||
resourceTrackers = append(resourceTrackers, resource)
|
||||
}
|
||||
}
|
||||
|
||||
return resourceTrackers, nil
|
||||
}
|
||||
|
||||
// DeleteGroup deletes an existing Elastigroup.
|
||||
func DeleteGroup(svc Service, group *cloudinstances.CloudInstanceGroup) error {
|
||||
glog.V(2).Infof("Deleting Elastigroup %q", group.HumanName)
|
||||
|
||||
return svc.Delete(
|
||||
context.Background(),
|
||||
group.Raw.(Elastigroup).Id())
|
||||
}
|
||||
|
||||
// DeleteInstance removes an instance from its Elastigroup.
|
||||
func DeleteInstance(svc Service, instance *cloudinstances.CloudInstanceGroupMember) error {
|
||||
glog.V(2).Infof("Detaching instance %q from Elastigroup", instance.ID)
|
||||
|
||||
return svc.Detach(
|
||||
context.Background(),
|
||||
instance.CloudInstanceGroup.Raw.(Elastigroup).Id(),
|
||||
[]string{instance.ID})
|
||||
}
|
||||
|
||||
// GetCloudGroups returns a list of Elastigroups as CloudInstanceGroup objects.
|
||||
func GetCloudGroups(svc Service, cluster *kops.Cluster, instancegroups []*kops.InstanceGroup,
|
||||
warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) {
|
||||
glog.V(2).Info("Listing all Elastigroups")
|
||||
|
||||
groups, err := svc.List(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceGroups := make(map[string]*cloudinstances.CloudInstanceGroup)
|
||||
nodeMap := cloudinstances.GetNodeMap(nodes, cluster)
|
||||
|
||||
for _, group := range groups {
|
||||
// Find matching instance group.
|
||||
var instancegroup *kops.InstanceGroup
|
||||
for _, ig := range instancegroups {
|
||||
name := getGroupNameByRole(cluster, ig)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if name == group.Name() {
|
||||
if instancegroup != nil {
|
||||
return nil, fmt.Errorf("spotinst: found multiple instance groups matching group %q", group.Name())
|
||||
}
|
||||
instancegroup = ig
|
||||
}
|
||||
}
|
||||
|
||||
if instancegroup == nil {
|
||||
if warnUnmatched {
|
||||
glog.Warningf("Found group with no corresponding instance group %q", group.Name())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Build the instance group.
|
||||
ig, err := buildCloudInstanceGroup(svc, instancegroup, group, nodeMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("spotinst: failed to build instance group: %v", err)
|
||||
}
|
||||
|
||||
instanceGroups[instancegroup.ObjectMeta.Name] = ig
|
||||
}
|
||||
|
||||
return instanceGroups, nil
|
||||
}
|
||||
|
||||
func getGroupNameByRole(cluster *kops.Cluster, ig *kops.InstanceGroup) string {
|
||||
var groupName string
|
||||
|
||||
switch ig.Spec.Role {
|
||||
case kops.InstanceGroupRoleMaster:
|
||||
groupName = ig.ObjectMeta.Name + ".masters." + cluster.ObjectMeta.Name
|
||||
case kops.InstanceGroupRoleNode:
|
||||
groupName = ig.ObjectMeta.Name + "." + cluster.ObjectMeta.Name
|
||||
case kops.InstanceGroupRoleBastion:
|
||||
groupName = ig.ObjectMeta.Name + "." + cluster.ObjectMeta.Name
|
||||
default:
|
||||
glog.Warningf("Ignoring InstanceGroup of unknown role %q", ig.Spec.Role)
|
||||
}
|
||||
|
||||
return groupName
|
||||
}
|
||||
|
||||
func buildCloudInstanceGroup(svc Service, ig *kops.InstanceGroup, group Elastigroup,
|
||||
nodeMap map[string]*v1.Node) (*cloudinstances.CloudInstanceGroup, error) {
|
||||
|
||||
instances, err := svc.Instances(context.Background(), group.Id())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceGroup := &cloudinstances.CloudInstanceGroup{
|
||||
HumanName: group.Name(),
|
||||
InstanceGroup: ig,
|
||||
MinSize: group.MinSize(),
|
||||
MaxSize: group.MaxSize(),
|
||||
Raw: group,
|
||||
}
|
||||
|
||||
currentName := group.Name()
|
||||
newName := fmt.Sprintf("%s:%d", group.Name(), time.Now().Nanosecond())
|
||||
|
||||
for _, instance := range instances {
|
||||
if instance.Id() == "" {
|
||||
glog.Warningf("Ignoring instance with no ID: %v", instance)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := instanceGroup.NewCloudInstanceGroupMember(
|
||||
instance.Id(), currentName, newName, nodeMap); err != nil {
|
||||
return nil, fmt.Errorf("spotinst: error creating cloud instance group member: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return instanceGroup, nil
|
||||
}
|
||||
|
||||
func deleter(svc Service, group Elastigroup) func(fi.Cloud, *resources.Resource) error {
|
||||
return func(cloud fi.Cloud, resource *resources.Resource) error {
|
||||
glog.V(2).Infof("Deleting Elastigroup %q", group.Id())
|
||||
return svc.Delete(context.Background(), group.Id())
|
||||
}
|
||||
}
|
||||
|
||||
func dumper(op *resources.DumpOperation, resource *resources.Resource) error {
|
||||
data := make(map[string]interface{})
|
||||
|
||||
data["id"] = resource.ID
|
||||
data["type"] = resource.Type
|
||||
data["raw"] = resource.Obj
|
||||
|
||||
op.Dump.Resources = append(op.Dump.Resources, data)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package spotinst
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup"
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/credentials"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/log"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/session"
|
||||
kopsv "k8s.io/kops"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
)
|
||||
|
||||
// NewService returns a Service interface for the specified cloud provider.
|
||||
func NewService(cloudProviderID kops.CloudProviderID) (Service, error) {
|
||||
svc := elastigroup.New(session.New(NewConfig()))
|
||||
|
||||
switch cloudProviderID {
|
||||
case kops.CloudProviderAWS:
|
||||
return &awsService{svc.CloudProviderAWS()}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("spotinst: unsupported cloud provider: %s", cloudProviderID)
|
||||
}
|
||||
}
|
||||
|
||||
// NewConfig returns a new configuration object.
|
||||
func NewConfig() *spotinst.Config {
|
||||
config := spotinst.DefaultConfig()
|
||||
|
||||
config.WithCredentials(NewCredentials())
|
||||
config.WithLogger(NewStdLogger())
|
||||
config.WithUserAgent("kubernetes-kops/" + kopsv.Version)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// NewCredentials returns a new chain-credentials object.
|
||||
func NewCredentials() *credentials.Credentials {
|
||||
return credentials.NewChainCredentials(
|
||||
new(credentials.EnvProvider),
|
||||
new(credentials.FileProvider),
|
||||
)
|
||||
}
|
||||
|
||||
// NewStdLogger returns a new Logger.
|
||||
func NewStdLogger() log.Logger {
|
||||
return log.LoggerFunc(func(format string, args ...interface{}) {
|
||||
glog.V(2).Infof(format, args...)
|
||||
})
|
||||
}
|
||||
|
||||
// NewElastigroup returns an Elastigroup wrapper for the specified cloud provider.
|
||||
func NewElastigroup(cloudProviderID kops.CloudProviderID,
|
||||
obj interface{}) (Elastigroup, error) {
|
||||
|
||||
switch cloudProviderID {
|
||||
case kops.CloudProviderAWS:
|
||||
return &awsElastigroup{obj.(*aws.Group)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("spotinst: unsupported cloud provider: %s", cloudProviderID)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadCredentials attempts to load credentials using the default chain.
|
||||
func LoadCredentials() (credentials.Value, error) {
|
||||
var (
|
||||
chain = NewCredentials()
|
||||
creds credentials.Value
|
||||
err error
|
||||
)
|
||||
|
||||
// Attempt to load the credentials.
|
||||
creds, err = chain.Get()
|
||||
if err != nil {
|
||||
return creds, fmt.Errorf("spotinst: unable to load credentials: %v", err)
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
# ------------------------------------------
|
||||
# Config Map
|
||||
# ------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
namespace: kube-system
|
||||
data:
|
||||
spotinst.token: {{ SpotinstToken }}
|
||||
spotinst.account: {{ SpotinstAccount }}
|
||||
spotinst.cluster-identifier: {{ ClusterName }}
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Secret
|
||||
# ------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller-certs
|
||||
namespace: kube-system
|
||||
type: Opaque
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Service Account
|
||||
# ------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Cluster Role
|
||||
# ------------------------------------------
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "nodes", "replicationcontrollers", "events", "limitranges", "services", "persistentvolumes", "persistentvolumeclaims"]
|
||||
verbs: ["get", "delete", "list", "patch", "update"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get","list","patch"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["replicasets"]
|
||||
verbs: ["get","list"]
|
||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||
resources: ["clusterroles"]
|
||||
verbs: ["patch"]
|
||||
- apiGroups: ["policy"]
|
||||
resources: ["poddisruptionbudgets"]
|
||||
verbs: ["list"]
|
||||
- nonResourceURLs: ["/version/", "/version"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Cluster Role Binding
|
||||
# ------------------------------------------
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Deployment
|
||||
# ------------------------------------------
|
||||
apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
k8s-addon: spotinst-kubernetes-cluster-controller.addons.k8s.io
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-addon: spotinst-kubernetes-cluster-controller.addons.k8s.io
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-addon: spotinst-kubernetes-cluster-controller.addons.k8s.io
|
||||
spec:
|
||||
containers:
|
||||
- name: spotinst-kubernetes-cluster-controller
|
||||
image: spotinst/kubernetes-cluster-controller:1.0.16
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: SPOTINST_TOKEN
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
key: spotinst.token
|
||||
- name: SPOTINST_ACCOUNT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
key: spotinst.account
|
||||
- name: CLUSTER_IDENTIFIER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
key: spotinst.cluster-identifier
|
||||
volumeMounts:
|
||||
- name: spotinst-kubernetes-cluster-controller-certs
|
||||
mountPath: /certs
|
||||
volumes:
|
||||
- name: spotinst-kubernetes-cluster-controller-certs
|
||||
secret:
|
||||
secretName: spotinst-kubernetes-cluster-controller-certs
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect: NoSchedule
|
||||
serviceAccountName: spotinst-kubernetes-cluster-controller
|
||||
---
|
|
@ -0,0 +1,129 @@
|
|||
# ------------------------------------------
|
||||
# Config Map
|
||||
# ------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
namespace: kube-system
|
||||
data:
|
||||
spotinst.token: {{ SpotinstToken }}
|
||||
spotinst.account: {{ SpotinstAccount }}
|
||||
spotinst.cluster-identifier: {{ ClusterName }}
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Secret
|
||||
# ------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller-certs
|
||||
namespace: kube-system
|
||||
type: Opaque
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Service Account
|
||||
# ------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Cluster Role
|
||||
# ------------------------------------------
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "nodes", "replicationcontrollers", "events", "limitranges", "services", "persistentvolumes", "persistentvolumeclaims"]
|
||||
verbs: ["get", "delete", "list", "patch", "update"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get","list","patch"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["replicasets"]
|
||||
verbs: ["get","list"]
|
||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||
resources: ["clusterroles"]
|
||||
verbs: ["patch"]
|
||||
- apiGroups: ["policy"]
|
||||
resources: ["poddisruptionbudgets"]
|
||||
verbs: ["list"]
|
||||
- nonResourceURLs: ["/version/", "/version"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Cluster Role Binding
|
||||
# ------------------------------------------
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
# ------------------------------------------
|
||||
# Deployment
|
||||
# ------------------------------------------
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
k8s-addon: spotinst-kubernetes-cluster-controller.addons.k8s.io
|
||||
name: spotinst-kubernetes-cluster-controller
|
||||
namespace: kube-system
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-addon: spotinst-kubernetes-cluster-controller.addons.k8s.io
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-addon: spotinst-kubernetes-cluster-controller.addons.k8s.io
|
||||
spec:
|
||||
containers:
|
||||
- name: spotinst-kubernetes-cluster-controller
|
||||
image: spotinst/kubernetes-cluster-controller:1.0.16
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: SPOTINST_TOKEN
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
key: spotinst.token
|
||||
- name: SPOTINST_ACCOUNT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
key: spotinst.account
|
||||
- name: CLUSTER_IDENTIFIER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: spotinst-kubernetes-cluster-controller-config
|
||||
key: spotinst.cluster-identifier
|
||||
volumeMounts:
|
||||
- name: spotinst-kubernetes-cluster-controller-certs
|
||||
mountPath: /certs
|
||||
volumes:
|
||||
- name: spotinst-kubernetes-cluster-controller-certs
|
||||
secret:
|
||||
secretName: spotinst-kubernetes-cluster-controller-certs
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect: NoSchedule
|
||||
serviceAccountName: spotinst-kubernetes-cluster-controller
|
||||
---
|
|
@ -48,8 +48,10 @@ go_library(
|
|||
"//pkg/model/domodel:go_default_library",
|
||||
"//pkg/model/gcemodel:go_default_library",
|
||||
"//pkg/model/openstackmodel:go_default_library",
|
||||
"//pkg/model/spotinstmodel:go_default_library",
|
||||
"//pkg/model/vspheremodel:go_default_library",
|
||||
"//pkg/resources/digitalocean:go_default_library",
|
||||
"//pkg/resources/spotinst:go_default_library",
|
||||
"//pkg/templates:go_default_library",
|
||||
"//upup/models:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
|
@ -66,6 +68,7 @@ go_library(
|
|||
"//upup/pkg/fi/cloudup/gcetasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/openstack:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/openstacktasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/spotinsttasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/terraform:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/vsphere:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/vspheretasks:go_default_library",
|
||||
|
|
|
@ -45,6 +45,7 @@ import (
|
|||
"k8s.io/kops/pkg/model/domodel"
|
||||
"k8s.io/kops/pkg/model/gcemodel"
|
||||
"k8s.io/kops/pkg/model/openstackmodel"
|
||||
"k8s.io/kops/pkg/model/spotinstmodel"
|
||||
"k8s.io/kops/pkg/model/vspheremodel"
|
||||
"k8s.io/kops/pkg/resources/digitalocean"
|
||||
"k8s.io/kops/pkg/templates"
|
||||
|
@ -62,6 +63,7 @@ import (
|
|||
"k8s.io/kops/upup/pkg/fi/cloudup/gcetasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/spotinsttasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/vsphere"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/vspheretasks"
|
||||
|
@ -399,6 +401,9 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
// Autoscaling
|
||||
"autoscalingGroup": &awstasks.AutoscalingGroup{},
|
||||
"launchConfiguration": &awstasks.LaunchConfiguration{},
|
||||
|
||||
// Spotinst
|
||||
"spotinstElastigroup": &spotinsttasks.Elastigroup{},
|
||||
})
|
||||
|
||||
if len(sshPublicKeys) == 0 {
|
||||
|
@ -673,13 +678,21 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
KopsModelContext: modelContext,
|
||||
}
|
||||
|
||||
l.Builders = append(l.Builders, &awsmodel.AutoscalingGroupModelBuilder{
|
||||
AWSModelContext: awsModelContext,
|
||||
BootstrapScript: bootstrapScriptBuilder,
|
||||
Lifecycle: &clusterLifecycle,
|
||||
|
||||
SecurityLifecycle: &securityLifecycle,
|
||||
})
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
l.Builders = append(l.Builders, &spotinstmodel.ElastigroupModelBuilder{
|
||||
AWSModelContext: awsModelContext,
|
||||
BootstrapScript: bootstrapScriptBuilder,
|
||||
Lifecycle: &clusterLifecycle,
|
||||
SecurityLifecycle: &securityLifecycle,
|
||||
})
|
||||
} else {
|
||||
l.Builders = append(l.Builders, &awsmodel.AutoscalingGroupModelBuilder{
|
||||
AWSModelContext: awsModelContext,
|
||||
BootstrapScript: bootstrapScriptBuilder,
|
||||
Lifecycle: &clusterLifecycle,
|
||||
SecurityLifecycle: &securityLifecycle,
|
||||
})
|
||||
}
|
||||
case kops.CloudProviderDO:
|
||||
doModelContext := &domodel.DOModelContext{
|
||||
KopsModelContext: modelContext,
|
||||
|
|
|
@ -21,6 +21,8 @@ go_library(
|
|||
"//pkg/apis/kops:go_default_library",
|
||||
"//pkg/apis/kops/model:go_default_library",
|
||||
"//pkg/cloudinstances:go_default_library",
|
||||
"//pkg/featureflag:go_default_library",
|
||||
"//pkg/resources/spotinst:go_default_library",
|
||||
"//protokube/pkg/etcd:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
|
||||
|
|
|
@ -48,6 +48,8 @@ import (
|
|||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/apis/kops/model"
|
||||
"k8s.io/kops/pkg/cloudinstances"
|
||||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/pkg/resources/spotinst"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
k8s_aws "k8s.io/kubernetes/pkg/cloudprovider/providers/aws"
|
||||
)
|
||||
|
@ -102,6 +104,7 @@ type AWSCloud interface {
|
|||
ELBV2() elbv2iface.ELBV2API
|
||||
Autoscaling() autoscalingiface.AutoScalingAPI
|
||||
Route53() route53iface.Route53API
|
||||
Spotinst() spotinst.Service
|
||||
|
||||
// TODO: Document and rationalize these tags/filters methods
|
||||
AddTags(name *string, tags map[string]string)
|
||||
|
@ -157,6 +160,7 @@ type awsCloudImplementation struct {
|
|||
elbv2 *elbv2.ELBV2
|
||||
autoscaling *autoscaling.AutoScaling
|
||||
route53 *route53.Route53
|
||||
spotinst spotinst.Service
|
||||
|
||||
region string
|
||||
|
||||
|
@ -265,6 +269,13 @@ func NewAWSCloud(region string, tags map[string]string) (AWSCloud, error) {
|
|||
c.route53.Handlers.Send.PushFront(requestLogger)
|
||||
c.addHandlers(region, &c.route53.Handlers)
|
||||
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
c.spotinst, err = spotinst.NewService(kops.CloudProviderAWS)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
}
|
||||
|
||||
awsCloudInstances[region] = c
|
||||
raw = c
|
||||
}
|
||||
|
@ -325,6 +336,10 @@ func NewEC2Filter(name string, values ...string) *ec2.Filter {
|
|||
|
||||
// DeleteGroup deletes an aws autoscaling group
|
||||
func (c *awsCloudImplementation) DeleteGroup(g *cloudinstances.CloudInstanceGroup) error {
|
||||
if c.spotinst != nil {
|
||||
return spotinst.DeleteGroup(c.spotinst, g)
|
||||
}
|
||||
|
||||
return deleteGroup(c, g)
|
||||
}
|
||||
|
||||
|
@ -366,6 +381,10 @@ func deleteGroup(c AWSCloud, g *cloudinstances.CloudInstanceGroup) error {
|
|||
|
||||
// DeleteInstance deletes an aws instance
|
||||
func (c *awsCloudImplementation) DeleteInstance(i *cloudinstances.CloudInstanceGroupMember) error {
|
||||
if c.spotinst != nil {
|
||||
return spotinst.DeleteInstance(c.spotinst, i)
|
||||
}
|
||||
|
||||
return deleteInstance(c, i)
|
||||
}
|
||||
|
||||
|
@ -393,6 +412,11 @@ func deleteInstance(c AWSCloud, i *cloudinstances.CloudInstanceGroupMember) erro
|
|||
|
||||
// GetCloudGroups returns a groups of instances that back a kops instance groups
|
||||
func (c *awsCloudImplementation) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) {
|
||||
if c.spotinst != nil {
|
||||
return spotinst.GetCloudGroups(c.spotinst, cluster,
|
||||
instancegroups, warnUnmatched, nodes)
|
||||
}
|
||||
|
||||
return getCloudGroups(c, cluster, instancegroups, warnUnmatched, nodes)
|
||||
}
|
||||
|
||||
|
@ -1140,6 +1164,10 @@ func (c *awsCloudImplementation) Route53() route53iface.Route53API {
|
|||
return c.route53
|
||||
}
|
||||
|
||||
func (c *awsCloudImplementation) Spotinst() spotinst.Service {
|
||||
return c.spotinst
|
||||
}
|
||||
|
||||
func (c *awsCloudImplementation) FindVPCInfo(vpcID string) (*fi.VPCInfo, error) {
|
||||
return findVPCInfo(c, vpcID)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import (
|
|||
dnsproviderroute53 "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/aws/route53"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/cloudinstances"
|
||||
"k8s.io/kops/pkg/resources/spotinst"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
|
@ -78,6 +79,7 @@ type MockCloud struct {
|
|||
MockRoute53 route53iface.Route53API
|
||||
MockELB elbiface.ELBAPI
|
||||
MockELBV2 elbv2iface.ELBV2API
|
||||
MockSpotinst spotinst.Service
|
||||
}
|
||||
|
||||
func (c *MockAWSCloud) DeleteGroup(g *cloudinstances.CloudInstanceGroup) error {
|
||||
|
@ -236,6 +238,13 @@ func (c *MockAWSCloud) Route53() route53iface.Route53API {
|
|||
return c.MockRoute53
|
||||
}
|
||||
|
||||
func (c *MockAWSCloud) Spotinst() spotinst.Service {
|
||||
if c.MockSpotinst == nil {
|
||||
glog.Fatalf("MockSpotinst not set")
|
||||
}
|
||||
return c.MockSpotinst
|
||||
}
|
||||
|
||||
func (c *MockAWSCloud) FindVPCInfo(id string) (*fi.VPCInfo, error) {
|
||||
return findVPCInfo(c, id)
|
||||
}
|
||||
|
|
|
@ -445,6 +445,41 @@ func (b *BootstrapChannelBuilder) buildManifest() (*channelsapi.Addons, map[stri
|
|||
}
|
||||
}
|
||||
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
key := "spotinst-kubernetes-cluster-controller.addons.k8s.io"
|
||||
version := "1.0.16"
|
||||
|
||||
{
|
||||
id := "v1.8.0"
|
||||
location := key + "/" + id + ".yaml"
|
||||
|
||||
addons.Spec.Addons = append(addons.Spec.Addons, &channelsapi.AddonSpec{
|
||||
Name: fi.String(key),
|
||||
Version: fi.String(version),
|
||||
Selector: map[string]string{"k8s-addon": key},
|
||||
Manifest: fi.String(location),
|
||||
KubernetesVersion: "<1.9.0",
|
||||
Id: id,
|
||||
})
|
||||
manifests[key+"-"+id] = "addons/" + location
|
||||
}
|
||||
|
||||
{
|
||||
id := "v1.9.0"
|
||||
location := key + "/" + id + ".yaml"
|
||||
|
||||
addons.Spec.Addons = append(addons.Spec.Addons, &channelsapi.AddonSpec{
|
||||
Name: fi.String(key),
|
||||
Version: fi.String(version),
|
||||
Selector: map[string]string{"k8s-addon": key},
|
||||
Manifest: fi.String(location),
|
||||
KubernetesVersion: ">=1.9.0",
|
||||
Id: id,
|
||||
})
|
||||
manifests[key+"-"+id] = "addons/" + location
|
||||
}
|
||||
}
|
||||
|
||||
// The role.kubernetes.io/networking is used to label anything related to a networking addin,
|
||||
// so that if we switch networking plugins (e.g. calico -> weave or vice-versa), we'll replace the
|
||||
// old networking plugin, and there won't be old pods "floating around".
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"elastigroup.go",
|
||||
"elastigroup_fitask.go",
|
||||
],
|
||||
importpath = "k8s.io/kops/upup/pkg/fi/cloudup/spotinsttasks",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/resources/spotinst:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/awstasks:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/awsup:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/terraform:go_default_library",
|
||||
"//upup/pkg/fi/utils:go_default_library",
|
||||
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by ""fitask" -type=Elastigroup"; DO NOT EDIT
|
||||
|
||||
package spotinsttasks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
// Elastigroup
|
||||
|
||||
// JSON marshalling boilerplate
|
||||
type realElastigroup Elastigroup
|
||||
|
||||
// UnmarshalJSON implements conversion to JSON, supporitng an alternate specification of the object as a string
|
||||
func (o *Elastigroup) UnmarshalJSON(data []byte) error {
|
||||
var jsonName string
|
||||
if err := json.Unmarshal(data, &jsonName); err == nil {
|
||||
o.Name = &jsonName
|
||||
return nil
|
||||
}
|
||||
|
||||
var r realElastigroup
|
||||
if err := json.Unmarshal(data, &r); err != nil {
|
||||
return err
|
||||
}
|
||||
*o = Elastigroup(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ fi.HasLifecycle = &Elastigroup{}
|
||||
|
||||
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
|
||||
func (o *Elastigroup) GetLifecycle() *fi.Lifecycle {
|
||||
return o.Lifecycle
|
||||
}
|
||||
|
||||
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
|
||||
func (o *Elastigroup) SetLifecycle(lifecycle fi.Lifecycle) {
|
||||
o.Lifecycle = &lifecycle
|
||||
}
|
||||
|
||||
var _ fi.HasName = &Elastigroup{}
|
||||
|
||||
// GetName returns the Name of the object, implementing fi.HasName
|
||||
func (o *Elastigroup) GetName() *string {
|
||||
return o.Name
|
||||
}
|
||||
|
||||
// SetName sets the Name of the object, implementing fi.SetName
|
||||
func (o *Elastigroup) SetName(name string) {
|
||||
o.Name = &name
|
||||
}
|
||||
|
||||
// String is the stringer function for the task, producing readable output using fi.TaskAsString
|
||||
func (o *Elastigroup) String() string {
|
||||
return fi.TaskAsString(o)
|
||||
}
|
|
@ -37,7 +37,9 @@ import (
|
|||
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/dns"
|
||||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/pkg/model"
|
||||
"k8s.io/kops/pkg/resources/spotinst"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
|
||||
|
||||
|
@ -102,6 +104,13 @@ func (tf *TemplateFunctions) AddTo(dest template.FuncMap, secretStore fi.SecretS
|
|||
return os.Getenv("DIGITALOCEAN_ACCESS_TOKEN")
|
||||
}
|
||||
|
||||
if featureflag.Spotinst.Enabled() {
|
||||
if creds, err := spotinst.LoadCredentials(); err == nil {
|
||||
dest["SpotinstToken"] = func() string { return creds.Token }
|
||||
dest["SpotinstAccount"] = func() string { return creds.Account }
|
||||
}
|
||||
}
|
||||
|
||||
if tf.cluster.Spec.Networking != nil && tf.cluster.Spec.Networking.Flannel != nil {
|
||||
flannelBackendType := tf.cluster.Spec.Networking.Flannel.Backend
|
||||
if flannelBackendType == "" {
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
17
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/BUILD.bazel
generated
vendored
Normal file
17
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["elastigroup.go"],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/service/elastigroup",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session:go_default_library",
|
||||
],
|
||||
)
|
47
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/elastigroup.go
generated
vendored
Normal file
47
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/elastigroup.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package elastigroup
|
||||
|
||||
import (
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws"
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure"
|
||||
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/client"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/session"
|
||||
)
|
||||
|
||||
// Service provides the API operation methods for making requests to
|
||||
// endpoints of the Spotinst API. See this package's package overview docs
|
||||
// for details on the service.
|
||||
type Service interface {
|
||||
CloudProviderAWS() aws.Service
|
||||
CloudProviderAzure() azure.Service
|
||||
CloudProviderGCE() gce.Service
|
||||
}
|
||||
|
||||
type ServiceOp struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
var _ Service = &ServiceOp{}
|
||||
|
||||
func New(sess *session.Session, cfgs ...*spotinst.Config) *ServiceOp {
|
||||
cfg := &spotinst.Config{}
|
||||
cfg.Merge(sess.Config)
|
||||
cfg.Merge(cfgs...)
|
||||
|
||||
return &ServiceOp{
|
||||
Client: client.New(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceOp) CloudProviderAWS() aws.Service {
|
||||
return &aws.ServiceOp{s.Client}
|
||||
}
|
||||
|
||||
func (s *ServiceOp) CloudProviderAzure() azure.Service {
|
||||
return &azure.ServiceOp{s.Client}
|
||||
}
|
||||
|
||||
func (s *ServiceOp) CloudProviderGCE() gce.Service {
|
||||
return &gce.ServiceOp{s.Client}
|
||||
}
|
20
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/BUILD.bazel
generated
vendored
Normal file
20
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"aws.go",
|
||||
"service.go",
|
||||
"tag.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates:go_default_library",
|
||||
],
|
||||
)
|
3226
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/aws.go
generated
vendored
Normal file
3226
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/aws.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
44
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/service.go
generated
vendored
Normal file
44
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/service.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/client"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/session"
|
||||
)
|
||||
|
||||
// Service provides the API operation methods for making requests to
|
||||
// endpoints of the Spotinst API. See this package's package overview docs
|
||||
// for details on the service.
|
||||
type Service interface {
|
||||
List(context.Context, *ListGroupsInput) (*ListGroupsOutput, error)
|
||||
Create(context.Context, *CreateGroupInput) (*CreateGroupOutput, error)
|
||||
Read(context.Context, *ReadGroupInput) (*ReadGroupOutput, error)
|
||||
Update(context.Context, *UpdateGroupInput) (*UpdateGroupOutput, error)
|
||||
Delete(context.Context, *DeleteGroupInput) (*DeleteGroupOutput, error)
|
||||
Status(context.Context, *StatusGroupInput) (*StatusGroupOutput, error)
|
||||
Detach(context.Context, *DetachGroupInput) (*DetachGroupOutput, error)
|
||||
Roll(context.Context, *RollGroupInput) (*RollGroupOutput, error)
|
||||
Scale(context.Context, *ScaleGroupInput) (*ScaleGroupOutput, error)
|
||||
ImportBeanstalkEnv(context.Context, *ImportBeanstalkInput) (*ImportBeanstalkOutput, error)
|
||||
StartBeanstalkMaintenance(context.Context, *BeanstalkMaintenanceInput) (*BeanstalkMaintenanceOutput, error)
|
||||
FinishBeanstalkMaintenance(context.Context, *BeanstalkMaintenanceInput) (*BeanstalkMaintenanceOutput, error)
|
||||
GetBeanstalkMaintenanceStatus(context.Context, *BeanstalkMaintenanceInput) (*string, error)
|
||||
}
|
||||
|
||||
type ServiceOp struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
var _ Service = &ServiceOp{}
|
||||
|
||||
func New(sess *session.Session, cfgs ...*spotinst.Config) *ServiceOp {
|
||||
cfg := &spotinst.Config{}
|
||||
cfg.Merge(sess.Config)
|
||||
cfg.Merge(cfgs...)
|
||||
|
||||
return &ServiceOp{
|
||||
Client: client.New(sess.Config),
|
||||
}
|
||||
}
|
31
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/tag.go
generated
vendored
Normal file
31
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws/tag.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package aws
|
||||
|
||||
import "github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil"
|
||||
|
||||
type Tag struct {
|
||||
Key *string `json:"tagKey,omitempty"`
|
||||
Value *string `json:"tagValue,omitempty"`
|
||||
|
||||
forceSendFields []string `json:"-"`
|
||||
nullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (o *Tag) MarshalJSON() ([]byte, error) {
|
||||
type noMethod Tag
|
||||
raw := noMethod(*o)
|
||||
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
|
||||
}
|
||||
|
||||
func (o *Tag) SetKey(v *string) *Tag {
|
||||
if o.Key = v; o.Key == nil {
|
||||
o.nullFields = append(o.nullFields, "Key")
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *Tag) SetValue(v *string) *Tag {
|
||||
if o.Value = v; o.Value == nil {
|
||||
o.nullFields = append(o.nullFields, "Value")
|
||||
}
|
||||
return o
|
||||
}
|
20
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/BUILD.bazel
generated
vendored
Normal file
20
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"azure.go",
|
||||
"service.go",
|
||||
"tag.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates:go_default_library",
|
||||
],
|
||||
)
|
1486
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/azure.go
generated
vendored
Normal file
1486
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/azure.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
39
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/service.go
generated
vendored
Normal file
39
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/service.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/client"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/session"
|
||||
)
|
||||
|
||||
// Service provides the API operation methods for making requests to
|
||||
// endpoints of the Spotinst API. See this package's package overview docs
|
||||
// for details on the service.
|
||||
type Service interface {
|
||||
List(context.Context, *ListGroupsInput) (*ListGroupsOutput, error)
|
||||
Create(context.Context, *CreateGroupInput) (*CreateGroupOutput, error)
|
||||
Read(context.Context, *ReadGroupInput) (*ReadGroupOutput, error)
|
||||
Update(context.Context, *UpdateGroupInput) (*UpdateGroupOutput, error)
|
||||
Delete(context.Context, *DeleteGroupInput) (*DeleteGroupOutput, error)
|
||||
Status(context.Context, *StatusGroupInput) (*StatusGroupOutput, error)
|
||||
Detach(context.Context, *DetachGroupInput) (*DetachGroupOutput, error)
|
||||
Roll(context.Context, *RollGroupInput) (*RollGroupOutput, error)
|
||||
}
|
||||
|
||||
type ServiceOp struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
var _ Service = &ServiceOp{}
|
||||
|
||||
func New(sess *session.Session, cfgs ...*spotinst.Config) *ServiceOp {
|
||||
cfg := &spotinst.Config{}
|
||||
cfg.Merge(sess.Config)
|
||||
cfg.Merge(cfgs...)
|
||||
|
||||
return &ServiceOp{
|
||||
Client: client.New(sess.Config),
|
||||
}
|
||||
}
|
31
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/tag.go
generated
vendored
Normal file
31
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/tag.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package azure
|
||||
|
||||
import "github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil"
|
||||
|
||||
type Tag struct {
|
||||
Key *string `json:"tagKey,omitempty"`
|
||||
Value *string `json:"tagValue,omitempty"`
|
||||
|
||||
forceSendFields []string `json:"-"`
|
||||
nullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (o *Tag) MarshalJSON() ([]byte, error) {
|
||||
type noMethod Tag
|
||||
raw := noMethod(*o)
|
||||
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
|
||||
}
|
||||
|
||||
func (o *Tag) SetKey(v *string) *Tag {
|
||||
if o.Key = v; o.Key == nil {
|
||||
o.nullFields = append(o.nullFields, "Key")
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *Tag) SetValue(v *string) *Tag {
|
||||
if o.Value = v; o.Value == nil {
|
||||
o.nullFields = append(o.nullFields, "Value")
|
||||
}
|
||||
return o
|
||||
}
|
18
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce/BUILD.bazel
generated
vendored
Normal file
18
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"service.go",
|
||||
"tag.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil:go_default_library",
|
||||
],
|
||||
)
|
28
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce/service.go
generated
vendored
Normal file
28
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce/service.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
package gce
|
||||
|
||||
import (
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/client"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/session"
|
||||
)
|
||||
|
||||
// Service provides the API operation methods for making requests to
|
||||
// endpoints of the Spotinst API. See this package's package overview docs
|
||||
// for details on the service.
|
||||
type Service interface{}
|
||||
|
||||
type ServiceOp struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
var _ Service = &ServiceOp{}
|
||||
|
||||
func New(sess *session.Session, cfgs ...*spotinst.Config) *ServiceOp {
|
||||
cfg := &spotinst.Config{}
|
||||
cfg.Merge(sess.Config)
|
||||
cfg.Merge(cfgs...)
|
||||
|
||||
return &ServiceOp{
|
||||
Client: client.New(sess.Config),
|
||||
}
|
||||
}
|
31
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce/tag.go
generated
vendored
Normal file
31
vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gce/tag.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package gce
|
||||
|
||||
import "github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil"
|
||||
|
||||
type Tag struct {
|
||||
Key *string `json:"tagKey,omitempty"`
|
||||
Value *string `json:"tagValue,omitempty"`
|
||||
|
||||
forceSendFields []string `json:"-"`
|
||||
nullFields []string `json:"-"`
|
||||
}
|
||||
|
||||
func (o *Tag) MarshalJSON() ([]byte, error) {
|
||||
type noMethod Tag
|
||||
raw := noMethod(*o)
|
||||
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
|
||||
}
|
||||
|
||||
func (o *Tag) SetKey(v *string) *Tag {
|
||||
if o.Key = v; o.Key == nil {
|
||||
o.nullFields = append(o.nullFields, "Key")
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *Tag) SetValue(v *string) *Tag {
|
||||
if o.Value = v; o.Value == nil {
|
||||
o.nullFields = append(o.nullFields, "Value")
|
||||
}
|
||||
return o
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"types.go",
|
||||
"version.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials:go_default_library",
|
||||
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/log:go_default_library",
|
||||
],
|
||||
)
|
14
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/BUILD.bazel
generated
vendored
Normal file
14
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"client.go",
|
||||
"request.go",
|
||||
"response.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/client",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library"],
|
||||
)
|
81
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/client.go
generated
vendored
Normal file
81
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/client.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
)
|
||||
|
||||
// Client provides a client to the API.
|
||||
type Client struct {
|
||||
config *spotinst.Config
|
||||
}
|
||||
|
||||
// New returns a new client.
|
||||
func New(cfg *spotinst.Config) *Client {
|
||||
if cfg == nil {
|
||||
cfg = spotinst.DefaultConfig()
|
||||
}
|
||||
return &Client{cfg}
|
||||
}
|
||||
|
||||
// NewRequest is used to create a new request.
|
||||
func NewRequest(method, path string) *Request {
|
||||
return &Request{
|
||||
method: method,
|
||||
url: &url.URL{
|
||||
Path: path,
|
||||
},
|
||||
header: make(http.Header),
|
||||
Params: make(url.Values),
|
||||
}
|
||||
}
|
||||
|
||||
// Do runs a request with our client.
|
||||
func (c *Client) Do(ctx context.Context, r *Request) (*http.Response, error) {
|
||||
req, err := r.toHTTP(ctx, c.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.logRequest(req)
|
||||
resp, err := c.config.HTTPClient.Do(req)
|
||||
c.logResponse(resp)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (c *Client) logf(format string, args ...interface{}) {
|
||||
if c.config.Logger != nil {
|
||||
c.config.Logger.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
const logReqMsg = `SPOTINST: Request "%s %s" details:
|
||||
---[ REQUEST ]---------------------------------------
|
||||
%s
|
||||
-----------------------------------------------------`
|
||||
|
||||
func (c *Client) logRequest(req *http.Request) {
|
||||
if c.config.Logger != nil && req != nil {
|
||||
out, err := httputil.DumpRequestOut(req, true)
|
||||
if err == nil {
|
||||
c.logf(logReqMsg, req.Method, req.URL, string(out))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const logRespMsg = `SPOTINST: Response "%s %s" details:
|
||||
---[ RESPONSE ]----------------------------------------
|
||||
%s
|
||||
-------------------------------------------------------`
|
||||
|
||||
func (c *Client) logResponse(resp *http.Response) {
|
||||
if c.config.Logger != nil && resp != nil {
|
||||
out, err := httputil.DumpResponse(resp, true)
|
||||
if err == nil {
|
||||
c.logf(logRespMsg, resp.Request.Method, resp.Request.URL, string(out))
|
||||
}
|
||||
}
|
||||
}
|
76
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/request.go
generated
vendored
Normal file
76
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/request.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Obj interface{}
|
||||
Params url.Values
|
||||
url *url.URL
|
||||
method string
|
||||
body io.Reader
|
||||
header http.Header
|
||||
}
|
||||
|
||||
// toHTTP converts the request to an HTTP request.
|
||||
func (r *Request) toHTTP(ctx context.Context, cfg *spotinst.Config) (*http.Request, error) {
|
||||
// Set the user credentials.
|
||||
creds, err := cfg.Credentials.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if creds.Token != "" {
|
||||
r.header.Set("Authorization", "Bearer "+creds.Token)
|
||||
}
|
||||
if creds.Account != "" {
|
||||
r.Params.Set("accountId", creds.Account)
|
||||
}
|
||||
|
||||
// Encode the query parameters.
|
||||
r.url.RawQuery = r.Params.Encode()
|
||||
|
||||
// Check if we should encode the body.
|
||||
if r.body == nil && r.Obj != nil {
|
||||
if b, err := EncodeBody(r.Obj); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
r.body = b
|
||||
}
|
||||
}
|
||||
|
||||
// Create the HTTP request.
|
||||
req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set request base URL.
|
||||
req.URL.Host = cfg.BaseURL.Host
|
||||
req.URL.Scheme = cfg.BaseURL.Scheme
|
||||
|
||||
// Set request headers.
|
||||
req.Host = cfg.BaseURL.Host
|
||||
req.Header = r.header
|
||||
req.Header.Set("Content-Type", cfg.ContentType)
|
||||
req.Header.Add("Accept", cfg.ContentType)
|
||||
req.Header.Add("User-Agent", cfg.UserAgent)
|
||||
|
||||
return req.WithContext(ctx), nil
|
||||
}
|
||||
|
||||
// EncodeBody is used to encode a request body
|
||||
func EncodeBody(obj interface{}) (io.Reader, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if err := json.NewEncoder(buf).Encode(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
110
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/response.go
generated
vendored
Normal file
110
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client/response.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Request struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"request"`
|
||||
Response struct {
|
||||
Errors []responseError `json:"errors"`
|
||||
Items []json.RawMessage `json:"items"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
type responseError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Response *http.Response `json:"-"`
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Field string `json:"field"`
|
||||
RequestID string `json:"requestId"`
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
msg := fmt.Sprintf("%v %v: %d (request: %q) %v: %v",
|
||||
e.Response.Request.Method, e.Response.Request.URL,
|
||||
e.Response.StatusCode, e.RequestID, e.Code, e.Message)
|
||||
|
||||
if e.Field != "" {
|
||||
msg = fmt.Sprintf("%s (field: %v)", msg, e.Field)
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
type Errors []Error
|
||||
|
||||
func (es Errors) Error() string {
|
||||
var stack string
|
||||
for _, e := range es {
|
||||
stack += e.Error() + "\n"
|
||||
}
|
||||
return stack
|
||||
}
|
||||
|
||||
// DecodeBody is used to JSON decode a body
|
||||
func DecodeBody(resp *http.Response, out interface{}) error {
|
||||
return json.NewDecoder(resp.Body).Decode(out)
|
||||
}
|
||||
|
||||
// RequireOK is used to verify response status code is a successful one (200 OK)
|
||||
func RequireOK(resp *http.Response, err error) (*http.Response, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, extractError(resp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// extractError is used to extract inner/logical errors from the response
|
||||
func extractError(resp *http.Response) error {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// TeeReader returns a Reader that writes to b what it reads from r.Body.
|
||||
reader := io.TeeReader(resp.Body, buf)
|
||||
defer resp.Body.Close()
|
||||
resp.Body = ioutil.NopCloser(buf)
|
||||
|
||||
var out Response
|
||||
if err := json.NewDecoder(reader).Decode(&out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errors Errors
|
||||
if errs := out.Response.Errors; len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
errors = append(errors, Error{
|
||||
Response: resp,
|
||||
RequestID: out.Request.ID,
|
||||
Code: err.Code,
|
||||
Message: err.Message,
|
||||
Field: err.Field,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
errors = append(errors, Error{
|
||||
Response: resp,
|
||||
RequestID: out.Request.ID,
|
||||
Code: strconv.Itoa(resp.StatusCode),
|
||||
Message: http.StatusText(resp.StatusCode),
|
||||
})
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package spotinst
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/credentials"
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultBaseURL is the default base URL of the Spotinst API.
|
||||
// It is used e.g. when initializing a new Client without a specific address.
|
||||
defaultBaseURL = "https://api.spotinst.io"
|
||||
|
||||
// defaultContentType is the default content type to use when making HTTP
|
||||
// calls.
|
||||
defaultContentType = "application/json"
|
||||
|
||||
// defaultUserAgent is the default user agent to use when making HTTP
|
||||
// calls.
|
||||
defaultUserAgent = SDKName + "/" + SDKVersion
|
||||
|
||||
// defaultMaxRetries is the number of retries for a single request after
|
||||
// the client will give up and return an error. It is zero by default, so
|
||||
// retry is disabled by default.
|
||||
defaultMaxRetries = 0
|
||||
|
||||
// defaultGzipEnabled specifies if gzip compression is enabled by default.
|
||||
defaultGzipEnabled = false
|
||||
)
|
||||
|
||||
// A Config provides Configuration to a service client instance.
|
||||
type Config struct {
|
||||
BaseURL *url.URL
|
||||
HTTPClient *http.Client
|
||||
Credentials *credentials.Credentials
|
||||
Logger log.Logger
|
||||
UserAgent string
|
||||
ContentType string
|
||||
}
|
||||
|
||||
func DefaultBaseURL() *url.URL {
|
||||
baseURL, _ := url.Parse(defaultBaseURL)
|
||||
return baseURL
|
||||
}
|
||||
|
||||
// DefaultTransport returns a new http.Transport with similar default
|
||||
// values to http.DefaultTransport. Do not use this for transient transports as
|
||||
// it can leak file descriptors over time. Only use this for transports that
|
||||
// will be re-used for the same host(s).
|
||||
func DefaultTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
DisableKeepAlives: false,
|
||||
MaxIdleConnsPerHost: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultHTTPClient returns a new http.Client with similar default values to
|
||||
// http.Client, but with a non-shared Transport, idle connections disabled, and
|
||||
// KeepAlives disabled.
|
||||
func DefaultHTTPClient() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: DefaultTransport(),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration for the client. By default this
|
||||
// will pool and reuse idle connections to API. If you have a long-lived
|
||||
// client object, this is the desired behavior and should make the most efficient
|
||||
// use of the connections to API.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
BaseURL: DefaultBaseURL(),
|
||||
HTTPClient: DefaultHTTPClient(),
|
||||
UserAgent: defaultUserAgent,
|
||||
ContentType: defaultContentType,
|
||||
Credentials: credentials.NewChainCredentials(
|
||||
new(credentials.EnvProvider),
|
||||
new(credentials.FileProvider),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaseURL defines the base URL of the Spotinst API.
|
||||
func (c *Config) WithBaseURL(rawurl string) *Config {
|
||||
baseURL, _ := url.Parse(rawurl)
|
||||
c.BaseURL = baseURL
|
||||
return c
|
||||
}
|
||||
|
||||
// WithHTTPClient defines the HTTP client.
|
||||
func (c *Config) WithHTTPClient(client *http.Client) *Config {
|
||||
c.HTTPClient = client
|
||||
return c
|
||||
}
|
||||
|
||||
// WithCredentials defines the credentials.
|
||||
func (c *Config) WithCredentials(creds *credentials.Credentials) *Config {
|
||||
c.Credentials = creds
|
||||
return c
|
||||
}
|
||||
|
||||
// WithUserAgent defines the user agent.
|
||||
func (c *Config) WithUserAgent(ua string) *Config {
|
||||
c.UserAgent = fmt.Sprintf("%s,%s", ua, c.UserAgent)
|
||||
return c
|
||||
}
|
||||
|
||||
// WithContentType defines the content type.
|
||||
func (c *Config) WithContentType(ct string) *Config {
|
||||
c.ContentType = ct
|
||||
return c
|
||||
}
|
||||
|
||||
// WithLogger defines the logger for informational messages, e.g. requests
|
||||
// and their response times. It is nil by default.
|
||||
func (c *Config) WithLogger(logger log.Logger) *Config {
|
||||
c.Logger = logger
|
||||
return c
|
||||
}
|
||||
|
||||
// Merge merges the passed in configs into the existing config object.
|
||||
func (c *Config) Merge(cfgs ...*Config) {
|
||||
for _, other := range cfgs {
|
||||
mergeConfig(c, other)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeConfig(dst *Config, other *Config) {
|
||||
if other == nil {
|
||||
return
|
||||
}
|
||||
if other.BaseURL != nil {
|
||||
dst.BaseURL = other.BaseURL
|
||||
}
|
||||
if other.Credentials != nil {
|
||||
dst.Credentials = other.Credentials
|
||||
}
|
||||
if other.HTTPClient != nil {
|
||||
dst.HTTPClient = other.HTTPClient
|
||||
}
|
||||
if other.UserAgent != "" {
|
||||
dst.UserAgent = other.UserAgent
|
||||
}
|
||||
if other.ContentType != "" {
|
||||
dst.ContentType = other.ContentType
|
||||
}
|
||||
if other.Logger != nil {
|
||||
dst.Logger = other.Logger
|
||||
}
|
||||
}
|
15
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/BUILD.bazel
generated
vendored
Normal file
15
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"credentials.go",
|
||||
"provider_chain.go",
|
||||
"provider_env.go",
|
||||
"provider_file.go",
|
||||
"provider_static.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/credentials",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
86
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/credentials.go
generated
vendored
Normal file
86
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/credentials.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package credentials
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A Value is the Spotinst credentials value for individual credential fields.
|
||||
type Value struct {
|
||||
// Spotinst API token.
|
||||
Token string `json:"token"`
|
||||
|
||||
// Spotinst account ID.
|
||||
Account string `json:"account"`
|
||||
|
||||
// Provider used to get credentials.
|
||||
ProviderName string `json:"-"`
|
||||
}
|
||||
|
||||
// A Provider is the interface for any component which will provide credentials
|
||||
// Value.
|
||||
//
|
||||
// The Provider should not need to implement its own mutexes, because
|
||||
// that will be managed by Credentials.
|
||||
type Provider interface {
|
||||
fmt.Stringer
|
||||
|
||||
// Refresh returns nil if it successfully retrieved the value.
|
||||
// Error is returned if the value were not obtainable, or empty.
|
||||
Retrieve() (Value, error)
|
||||
}
|
||||
|
||||
// A Credentials provides synchronous safe retrieval of Spotinst credentials.
|
||||
// Credentials will cache the credentials value.
|
||||
//
|
||||
// Credentials is safe to use across multiple goroutines and will manage the
|
||||
// synchronous state so the Providers do not need to implement their own
|
||||
// synchronization.
|
||||
//
|
||||
// The first Credentials.Get() will always call Provider.Retrieve() to get the
|
||||
// first instance of the credentials Value. All calls to Get() after that
|
||||
// will return the cached credentials Value.
|
||||
type Credentials struct {
|
||||
provider Provider
|
||||
mu sync.Mutex
|
||||
forceRefresh bool
|
||||
creds Value
|
||||
}
|
||||
|
||||
// NewCredentials returns a pointer to a new Credentials with the provider set.
|
||||
func NewCredentials(provider Provider) *Credentials {
|
||||
return &Credentials{
|
||||
provider: provider,
|
||||
forceRefresh: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the credentials value, or error if the credentials Value failed
|
||||
// to be retrieved.
|
||||
//
|
||||
// Will return the cached credentials Value. If the credentials Value is empty
|
||||
// the Provider's Retrieve() will be called to refresh the credentials.
|
||||
func (c *Credentials) Get() (Value, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.creds.Token == "" || c.forceRefresh {
|
||||
creds, err := c.provider.Retrieve()
|
||||
if err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
c.creds = creds
|
||||
c.forceRefresh = false
|
||||
}
|
||||
|
||||
return c.creds, nil
|
||||
}
|
||||
|
||||
// Refresh refreshes the credentials and forces them to be retrieved on the
|
||||
// next call to Get().
|
||||
func (c *Credentials) Refresh() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.forceRefresh = true
|
||||
}
|
102
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_chain.go
generated
vendored
Normal file
102
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_chain.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
package credentials
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrNoValidProvidersFoundInChain Is returned when there are no valid
|
||||
// credentials providers in the ChainProvider.
|
||||
var ErrNoValidProvidersFoundInChain = errors.New("spotinst: no valid credentials providers in chain")
|
||||
|
||||
// A ChainProvider will search for a provider which returns credentials
|
||||
// and cache that provider until Retrieve is called again.
|
||||
//
|
||||
// The ChainProvider provides a way of chaining multiple providers together
|
||||
// which will pick the first available using priority order of the Providers
|
||||
// in the list.
|
||||
//
|
||||
// If none of the Providers retrieve valid credentials Value, ChainProvider's
|
||||
// Retrieve() will return the error ErrNoValidProvidersFoundInChain.
|
||||
//
|
||||
// If a Provider is found which returns valid credentials Value ChainProvider
|
||||
// will cache that Provider for all calls until Retrieve is called again.
|
||||
//
|
||||
// Example of ChainProvider to be used with an EnvCredentialsProvider and
|
||||
// FileCredentialsProvider. In this example EnvProvider will first check if
|
||||
// any credentials are available via the environment variables. If there are
|
||||
// none ChainProvider will check the next Provider in the list, FileProvider
|
||||
// in this case. If FileCredentialsProvider does not return any credentials
|
||||
// ChainProvider will return the error ErrNoValidProvidersFoundInChain.
|
||||
//
|
||||
// creds := credentials.NewChainCredentials(
|
||||
// new(credentials.EnvProvider),
|
||||
// new(credentials.FileProvider),
|
||||
// )
|
||||
type ChainProvider struct {
|
||||
Providers []Provider
|
||||
active Provider
|
||||
}
|
||||
|
||||
// NewChainCredentials returns a pointer to a new Credentials object
|
||||
// wrapping a chain of providers.
|
||||
func NewChainCredentials(providers ...Provider) *Credentials {
|
||||
return NewCredentials(&ChainProvider{
|
||||
Providers: providers,
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieve returns the credentials value or error if no provider returned
|
||||
// without error. If a provider is found it will be cached.
|
||||
func (c *ChainProvider) Retrieve() (Value, error) {
|
||||
var errs errorList
|
||||
for _, p := range c.Providers {
|
||||
value, err := p.Retrieve()
|
||||
if err == nil {
|
||||
c.active = p
|
||||
return value, nil
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
c.active = nil
|
||||
|
||||
err := ErrNoValidProvidersFoundInChain
|
||||
if len(errs) > 0 {
|
||||
err = errs
|
||||
}
|
||||
|
||||
return Value{}, err
|
||||
}
|
||||
|
||||
func (c *ChainProvider) String() string {
|
||||
var out string
|
||||
for i, provider := range c.Providers {
|
||||
out += provider.String()
|
||||
if i < len(c.Providers)-1 {
|
||||
out += " "
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// An error list that satisfies the error interface.
|
||||
type errorList []error
|
||||
|
||||
// Error returns the string representation of the error.
|
||||
//
|
||||
// Satisfies the error interface.
|
||||
func (e errorList) Error() string {
|
||||
msg := ""
|
||||
if size := len(e); size > 0 {
|
||||
for i := 0; i < size; i++ {
|
||||
msg += fmt.Sprintf("%s", e[i].Error())
|
||||
// We check the next index to see if it is within the slice.
|
||||
// If it is, then we append a newline. We do this, because unit tests
|
||||
// could be broken with the additional '\n'.
|
||||
if i+1 < size {
|
||||
msg += "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
return msg
|
||||
}
|
62
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_env.go
generated
vendored
Normal file
62
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_env.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package credentials
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// EnvCredentialsProviderName provides a name of Env provider.
|
||||
EnvCredentialsProviderName = "EnvCredentialsProvider"
|
||||
|
||||
// EnvCredentialsVarToken specifies the name of the environment variable
|
||||
// points to the Spotinst Token.
|
||||
EnvCredentialsVarToken = "SPOTINST_TOKEN"
|
||||
|
||||
// EnvCredentialsVarAccount specifies the name of the environment variable
|
||||
// points to the Spotinst account ID.
|
||||
EnvCredentialsVarAccount = "SPOTINST_ACCOUNT"
|
||||
)
|
||||
|
||||
// ErrEnvCredentialsTokenNotFound is returned when the Spotinst Token can't be
|
||||
// found in the process's environment.
|
||||
var ErrEnvCredentialsTokenNotFound = fmt.Errorf("spotinst: %s not found in environment", EnvCredentialsVarToken)
|
||||
|
||||
// A EnvProvider retrieves credentials from the environment variables of the
|
||||
// running process.
|
||||
//
|
||||
// Environment variables used:
|
||||
// * Token: SPOTINST_TOKEN
|
||||
type EnvProvider struct {
|
||||
retrieved bool
|
||||
}
|
||||
|
||||
// NewEnvCredentials returns a pointer to a new Credentials object
|
||||
// wrapping the environment variable provider.
|
||||
func NewEnvCredentials() *Credentials {
|
||||
return NewCredentials(&EnvProvider{})
|
||||
}
|
||||
|
||||
// Retrieve retrieves the keys from the environment.
|
||||
func (e *EnvProvider) Retrieve() (Value, error) {
|
||||
e.retrieved = false
|
||||
|
||||
token := os.Getenv(EnvCredentialsVarToken)
|
||||
if token == "" {
|
||||
return Value{ProviderName: EnvCredentialsProviderName},
|
||||
ErrEnvCredentialsTokenNotFound
|
||||
}
|
||||
|
||||
e.retrieved = true
|
||||
value := Value{
|
||||
Token: token,
|
||||
Account: os.Getenv(EnvCredentialsVarAccount),
|
||||
ProviderName: EnvCredentialsProviderName,
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (e *EnvProvider) String() string {
|
||||
return EnvCredentialsProviderName
|
||||
}
|
129
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_file.go
generated
vendored
Normal file
129
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_file.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
package credentials
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
// FileCredentialsProviderName provides a name of File provider.
|
||||
FileCredentialsProviderName = "FileCredentialsProvider"
|
||||
|
||||
// FileCredentialsEnvVarFile specifies the name of the environment variable
|
||||
// points to the location of the credentials file.
|
||||
FileCredentialsEnvVarFile = "SPOTINST_CREDENTIALS_FILE"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrFileCredentialsHomeNotFound is emitted when the user directory
|
||||
// cannot be found.
|
||||
ErrFileCredentialsHomeNotFound = errors.New("spotinst: user home directory not found")
|
||||
|
||||
// ErrFileCredentialsLoadFailed is emitted when the provider is unable to
|
||||
// load credentials from the credentials file.
|
||||
ErrFileCredentialsLoadFailed = errors.New("spotinst: failed to load credentials file")
|
||||
|
||||
// ErrFileCredentialsTokenNotFound is emitted when the loaded credentials
|
||||
// did not contain a valid token.
|
||||
ErrFileCredentialsTokenNotFound = errors.New("spotinst: credentials did not contain token")
|
||||
)
|
||||
|
||||
// A FileProvider retrieves credentials from the current user's home
|
||||
// directory.
|
||||
type FileProvider struct {
|
||||
// Path to the credentials file.
|
||||
//
|
||||
// If empty will look for FileCredentialsEnvVarFile env variable. If the
|
||||
// env value is empty will default to current user's home directory.
|
||||
// Linux/OSX: "$HOME/.spotinst/credentials.json"
|
||||
// Windows: "%USERPROFILE%\.spotinst\credentials.json"
|
||||
Filename string
|
||||
|
||||
// retrieved states if the credentials have been successfully retrieved.
|
||||
retrieved bool
|
||||
}
|
||||
|
||||
// NewFileCredentials returns a pointer to a new Credentials object
|
||||
// wrapping the file provider.
|
||||
func NewFileCredentials(filename string) *Credentials {
|
||||
return NewCredentials(&FileProvider{
|
||||
Filename: filename,
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieve reads and extracts the shared credentials from the current
|
||||
// users home directory.
|
||||
func (p *FileProvider) Retrieve() (Value, error) {
|
||||
p.retrieved = false
|
||||
|
||||
filename, err := p.filename()
|
||||
if err != nil {
|
||||
return Value{ProviderName: FileCredentialsProviderName}, err
|
||||
}
|
||||
|
||||
creds, err := p.loadCredentials(filename)
|
||||
if err != nil {
|
||||
return Value{ProviderName: FileCredentialsProviderName}, err
|
||||
}
|
||||
|
||||
if len(creds.ProviderName) == 0 {
|
||||
creds.ProviderName = FileCredentialsProviderName
|
||||
}
|
||||
|
||||
p.retrieved = true
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func (p *FileProvider) String() string {
|
||||
return FileCredentialsProviderName
|
||||
}
|
||||
|
||||
// filename returns the filename to use to read Spotinst credentials.
|
||||
//
|
||||
// Will return an error if the user's home directory path cannot be found.
|
||||
func (p *FileProvider) filename() (string, error) {
|
||||
if p.Filename == "" {
|
||||
if p.Filename = os.Getenv(FileCredentialsEnvVarFile); p.Filename != "" {
|
||||
return p.Filename, nil
|
||||
}
|
||||
|
||||
homeDir := os.Getenv("HOME") // *nix
|
||||
if homeDir == "" { // Windows
|
||||
homeDir = os.Getenv("USERPROFILE")
|
||||
}
|
||||
if homeDir == "" {
|
||||
return "", ErrFileCredentialsHomeNotFound
|
||||
}
|
||||
|
||||
p.Filename = filepath.Join(homeDir, ".spotinst", "credentials")
|
||||
}
|
||||
|
||||
return p.Filename, nil
|
||||
}
|
||||
|
||||
// loadCredentials loads the credentials from the file pointed to by filename.
|
||||
// The credentials retrieved from the profile will be returned or error. Error will be
|
||||
// returned if it fails to read from the file, or the data is invalid.
|
||||
func (p *FileProvider) loadCredentials(filename string) (Value, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return Value{ProviderName: FileCredentialsProviderName},
|
||||
fmt.Errorf("%s: %s", ErrFileCredentialsLoadFailed.Error(), err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var value Value
|
||||
if err := json.NewDecoder(f).Decode(&value); err != nil {
|
||||
return Value{ProviderName: FileCredentialsProviderName},
|
||||
fmt.Errorf("%s: %s", ErrFileCredentialsLoadFailed.Error(), err)
|
||||
}
|
||||
if token := value.Token; len(token) == 0 {
|
||||
return Value{ProviderName: FileCredentialsProviderName},
|
||||
ErrFileCredentialsTokenNotFound
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
41
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_static.go
generated
vendored
Normal file
41
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials/provider_static.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
package credentials
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// StaticCredentialsProviderName provides a name of Static provider.
|
||||
const StaticCredentialsProviderName = "StaticProvider"
|
||||
|
||||
// ErrStaticCredentialsEmpty is emitted when static credentials are empty.
|
||||
var ErrStaticCredentialsEmpty = errors.New("spotinst: static credentials are empty")
|
||||
|
||||
// A StaticProvider is a set of credentials which are set programmatically.
|
||||
type StaticProvider struct {
|
||||
Value
|
||||
}
|
||||
|
||||
// NewStaticCredentials returns a pointer to a new Credentials object
|
||||
// wrapping a static credentials value provider.
|
||||
func NewStaticCredentials(token, account string) *Credentials {
|
||||
return NewCredentials(&StaticProvider{Value: Value{
|
||||
Token: token,
|
||||
Account: account,
|
||||
}})
|
||||
}
|
||||
|
||||
// Retrieve returns the credentials or error if the credentials are invalid.
|
||||
func (s *StaticProvider) Retrieve() (Value, error) {
|
||||
if s.Token == "" {
|
||||
return Value{ProviderName: StaticCredentialsProviderName},
|
||||
ErrStaticCredentialsEmpty
|
||||
}
|
||||
if len(s.Value.ProviderName) == 0 {
|
||||
s.Value.ProviderName = StaticCredentialsProviderName
|
||||
}
|
||||
return s.Value, nil
|
||||
}
|
||||
|
||||
func (s *StaticProvider) String() string {
|
||||
return StaticCredentialsProviderName
|
||||
}
|
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/log/BUILD.bazel
generated
vendored
Normal file
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/log/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["log.go"],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/log",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/log",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -0,0 +1,26 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DefaultStdLogger represents the default logging object that
|
||||
// generates lines of output to os.Stderr.
|
||||
var DefaultStdLogger Logger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
||||
// Logger specifies the interface for all log operations.
|
||||
type Logger interface {
|
||||
Printf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// The LoggerFunc type is an adapter to allow the use of
|
||||
// ordinary functions as Logger. If f is a function
|
||||
// with the appropriate signature, LoggerFunc(f) is a
|
||||
// Logger that calls f.
|
||||
type LoggerFunc func(format string, args ...interface{})
|
||||
|
||||
// Printf calls f(format, args).
|
||||
func (f LoggerFunc) Printf(format string, args ...interface{}) {
|
||||
f(format, args...)
|
||||
}
|
10
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session/BUILD.bazel
generated
vendored
Normal file
10
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["session.go"],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/session",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library"],
|
||||
)
|
22
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session/session.go
generated
vendored
Normal file
22
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session/session.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"github.com/spotinst/spotinst-sdk-go/spotinst"
|
||||
)
|
||||
|
||||
// A Session provides a central location to create service clients.
|
||||
//
|
||||
// Sessions are safe to create service clients concurrently, but it is not safe
|
||||
// to mutate the Session concurrently.
|
||||
type Session struct {
|
||||
Config *spotinst.Config
|
||||
}
|
||||
|
||||
// New creates a new instance of Session. Once the Session is created it
|
||||
// can be mutated to modify the Config. The Session is safe to be read
|
||||
// concurrently, but it should not be written to concurrently.
|
||||
func New(cfgs ...*spotinst.Config) *Session {
|
||||
s := &Session{Config: spotinst.DefaultConfig()}
|
||||
s.Config.Merge(cfgs...)
|
||||
return s
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
package spotinst
|
||||
|
||||
import "time"
|
||||
|
||||
// String returns a pointer to of the string value passed in.
|
||||
func String(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
// StringValue returns the value of the string pointer passed in or
|
||||
// "" if the pointer is nil.
|
||||
func StringValue(v *string) string {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// StringSlice converts a slice of string values into a slice of
|
||||
// string pointers
|
||||
func StringSlice(src []string) []*string {
|
||||
dst := make([]*string, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringValueSlice converts a slice of string pointers into a slice of
|
||||
// string values
|
||||
func StringValueSlice(src []*string) []string {
|
||||
dst := make([]string, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringMap converts a string map of string values into a string
|
||||
// map of string pointers
|
||||
func StringMap(src map[string]string) map[string]*string {
|
||||
dst := make(map[string]*string)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// StringValueMap converts a string map of string pointers into a string
|
||||
// map of string values
|
||||
func StringValueMap(src map[string]*string) map[string]string {
|
||||
dst := make(map[string]string)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Bool returns a pointer to of the bool value passed in.
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
// BoolValue returns the value of the bool pointer passed in or
|
||||
// false if the pointer is nil.
|
||||
func BoolValue(v *bool) bool {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BoolSlice converts a slice of bool values into a slice of
|
||||
// bool pointers
|
||||
func BoolSlice(src []bool) []*bool {
|
||||
dst := make([]*bool, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolValueSlice converts a slice of bool pointers into a slice of
|
||||
// bool values
|
||||
func BoolValueSlice(src []*bool) []bool {
|
||||
dst := make([]bool, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolMap converts a string map of bool values into a string
|
||||
// map of bool pointers
|
||||
func BoolMap(src map[string]bool) map[string]*bool {
|
||||
dst := make(map[string]*bool)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// BoolValueMap converts a string map of bool pointers into a string
|
||||
// map of bool values
|
||||
func BoolValueMap(src map[string]*bool) map[string]bool {
|
||||
dst := make(map[string]bool)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int returns a pointer to of the int value passed in.
|
||||
func Int(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
// IntValue returns the value of the int pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func IntValue(v *int) int {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntSlice converts a slice of int values into a slice of
|
||||
// int pointers.
|
||||
func IntSlice(src []int) []*int {
|
||||
dst := make([]*int, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntValueSlice converts a slice of int pointers into a slice of
|
||||
// int values.
|
||||
func IntValueSlice(src []*int) []int {
|
||||
dst := make([]int, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntMap converts a string map of int values into a string
|
||||
// map of int pointers.
|
||||
func IntMap(src map[string]int) map[string]*int {
|
||||
dst := make(map[string]*int)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// IntValueMap converts a string map of int pointers into a string
|
||||
// map of int values.
|
||||
func IntValueMap(src map[string]*int) map[string]int {
|
||||
dst := make(map[string]int)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64 returns a pointer to of the int64 value passed in.
|
||||
func Int64(v int64) *int64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int64Value returns the value of the int64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Int64Value(v *int64) int64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Int64Slice converts a slice of int64 values into a slice of
|
||||
// int64 pointers.
|
||||
func Int64Slice(src []int64) []*int64 {
|
||||
dst := make([]*int64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64ValueSlice converts a slice of int64 pointers into a slice of
|
||||
// int64 values.
|
||||
func Int64ValueSlice(src []*int64) []int64 {
|
||||
dst := make([]int64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64Map converts a string map of int64 values into a string
|
||||
// map of int64 pointers.
|
||||
func Int64Map(src map[string]int64) map[string]*int64 {
|
||||
dst := make(map[string]*int64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Int64ValueMap converts a string map of int64 pointers into a string
|
||||
// map of int64 values.
|
||||
func Int64ValueMap(src map[string]*int64) map[string]int64 {
|
||||
dst := make(map[string]int64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64 returns a pointer to of the float64 value passed in.
|
||||
func Float64(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
// Float64Value returns the value of the float64 pointer passed in or
|
||||
// 0 if the pointer is nil.
|
||||
func Float64Value(v *float64) float64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Float64Slice converts a slice of float64 values into a slice of
|
||||
// float64 pointers.
|
||||
func Float64Slice(src []float64) []*float64 {
|
||||
dst := make([]*float64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64ValueSlice converts a slice of float64 pointers into a slice of
|
||||
// float64 values.
|
||||
func Float64ValueSlice(src []*float64) []float64 {
|
||||
dst := make([]float64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64Map converts a string map of float64 values into a string
|
||||
// map of float64 pointers.
|
||||
func Float64Map(src map[string]float64) map[string]*float64 {
|
||||
dst := make(map[string]*float64)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float64ValueMap converts a string map of float64 pointers into a string
|
||||
// map of float64 values.
|
||||
func Float64ValueMap(src map[string]*float64) map[string]float64 {
|
||||
dst := make(map[string]float64)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Time returns a pointer to of the time.Time value passed in.
|
||||
func Time(v time.Time) *time.Time {
|
||||
return &v
|
||||
}
|
||||
|
||||
// TimeValue returns the value of the time.Time pointer passed in or
|
||||
// time.Time{} if the pointer is nil.
|
||||
func TimeValue(v *time.Time) time.Time {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// TimeSlice converts a slice of time.Time values into a slice of
|
||||
// time.Time pointers.
|
||||
func TimeSlice(src []time.Time) []*time.Time {
|
||||
dst := make([]*time.Time, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeValueSlice converts a slice of time.Time pointers into a slice of
|
||||
// time.Time values.
|
||||
func TimeValueSlice(src []*time.Time) []time.Time {
|
||||
dst := make([]time.Time, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeMap converts a string map of time.Time values into a string
|
||||
// map of time.Time pointers.
|
||||
func TimeMap(src map[string]time.Time) map[string]*time.Time {
|
||||
dst := make(map[string]*time.Time)
|
||||
for k, val := range src {
|
||||
v := val
|
||||
dst[k] = &v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// TimeValueMap converts a string map of time.Time pointers into a string
|
||||
// map of time.Time values.
|
||||
func TimeValueMap(src map[string]*time.Time) map[string]time.Time {
|
||||
dst := make(map[string]time.Time)
|
||||
for k, val := range src {
|
||||
if val != nil {
|
||||
dst[k] = *val
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil/BUILD.bazel
generated
vendored
Normal file
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["json.go"],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
184
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil/json.go
generated
vendored
Normal file
184
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil/json.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jsonutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MarshalJSON returns a JSON encoding of schema containing only selected fields.
|
||||
// A field is selected if any of the following is true:
|
||||
// * it has a non-empty value
|
||||
// * its field name is present in forceSendFields and it is not a nil pointer or nil interface
|
||||
// * its field name is present in nullFields.
|
||||
// The JSON key for each selected field is taken from the field's json: struct tag.
|
||||
func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) {
|
||||
if len(forceSendFields) == 0 && len(nullFields) == 0 {
|
||||
return json.Marshal(schema)
|
||||
}
|
||||
|
||||
mustInclude := make(map[string]struct{})
|
||||
for _, f := range forceSendFields {
|
||||
mustInclude[f] = struct{}{}
|
||||
}
|
||||
useNull := make(map[string]struct{})
|
||||
for _, f := range nullFields {
|
||||
useNull[f] = struct{}{}
|
||||
}
|
||||
|
||||
dataMap, err := schemaToMap(schema, mustInclude, useNull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(dataMap)
|
||||
}
|
||||
|
||||
func schemaToMap(schema interface{}, mustInclude, useNull map[string]struct{}) (map[string]interface{}, error) {
|
||||
m := make(map[string]interface{})
|
||||
s := reflect.ValueOf(schema)
|
||||
st := s.Type()
|
||||
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
jsonTag := st.Field(i).Tag.Get("json")
|
||||
if jsonTag == "" {
|
||||
continue
|
||||
}
|
||||
tag, err := parseJSONTag(jsonTag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tag.ignore {
|
||||
continue
|
||||
}
|
||||
|
||||
v := s.Field(i)
|
||||
f := st.Field(i)
|
||||
|
||||
if _, ok := useNull[f.Name]; ok {
|
||||
if !isEmptyValue(v) {
|
||||
return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name)
|
||||
}
|
||||
m[tag.apiName] = nil
|
||||
continue
|
||||
}
|
||||
if !includeField(v, f, mustInclude) {
|
||||
continue
|
||||
}
|
||||
|
||||
// nil maps are treated as empty maps.
|
||||
if f.Type.Kind() == reflect.Map && v.IsNil() {
|
||||
m[tag.apiName] = map[string]string{}
|
||||
continue
|
||||
}
|
||||
|
||||
// nil slices are treated as empty slices.
|
||||
if f.Type.Kind() == reflect.Slice && v.IsNil() {
|
||||
m[tag.apiName] = []bool{}
|
||||
continue
|
||||
}
|
||||
|
||||
if tag.stringFormat {
|
||||
m[tag.apiName] = formatAsString(v, f.Type.Kind())
|
||||
} else {
|
||||
m[tag.apiName] = v.Interface()
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// formatAsString returns a string representation of v, dereferencing it first if possible.
|
||||
func formatAsString(v reflect.Value, kind reflect.Kind) string {
|
||||
if kind == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", v.Interface())
|
||||
}
|
||||
|
||||
// jsonTag represents a restricted version of the struct tag format used by encoding/json.
|
||||
// It is used to describe the JSON encoding of fields in a Schema struct.
|
||||
type jsonTag struct {
|
||||
apiName string
|
||||
stringFormat bool
|
||||
ignore bool
|
||||
}
|
||||
|
||||
// parseJSONTag parses a restricted version of the struct tag format used by encoding/json.
|
||||
// The format of the tag must match that generated by the Schema.writeSchemaStruct method
|
||||
// in the api generator.
|
||||
func parseJSONTag(val string) (jsonTag, error) {
|
||||
if val == "-" {
|
||||
return jsonTag{ignore: true}, nil
|
||||
}
|
||||
|
||||
var tag jsonTag
|
||||
|
||||
i := strings.Index(val, ",")
|
||||
if i == -1 || val[:i] == "" {
|
||||
return tag, fmt.Errorf("malformed json tag: %s", val)
|
||||
}
|
||||
|
||||
tag = jsonTag{
|
||||
apiName: val[:i],
|
||||
}
|
||||
|
||||
switch val[i+1:] {
|
||||
case "omitempty":
|
||||
case "omitempty,string":
|
||||
tag.stringFormat = true
|
||||
default:
|
||||
return tag, fmt.Errorf("malformed json tag: %s", val)
|
||||
}
|
||||
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// Reports whether the struct field "f" with value "v" should be included in JSON output.
|
||||
func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]struct{}) bool {
|
||||
// The regular JSON encoding of a nil pointer is "null", which means "delete this field".
|
||||
// Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set.
|
||||
// However, many fields are not pointers, so there would be no way to delete these fields.
|
||||
// Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields.
|
||||
// Deletion will be handled by a separate mechanism.
|
||||
if f.Type.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
// The "any" type is represented as an interface{}. If this interface
|
||||
// is nil, there is no reasonable representation to send. We ignore
|
||||
// these fields, for the same reasons as given above for pointers.
|
||||
if f.Type.Kind() == reflect.Interface && v.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := mustInclude[f.Name]
|
||||
return ok || !isEmptyValue(v)
|
||||
}
|
||||
|
||||
// isEmptyValue reports whether v is the empty value for its type. This
|
||||
// implementation is based on that of the encoding/json package, but its
|
||||
// correctness does not depend on it being identical. What's important is that
|
||||
// this function return false in situations where v should not be sent as part
|
||||
// of a PATCH operation.
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil/BUILD.bazel
generated
vendored
Normal file
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["stringutil.go"],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
69
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil/stringutil.go
generated
vendored
Normal file
69
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil/stringutil.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
package stringutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Stringify attempts to create a reasonable string representation of types.
|
||||
// It does things like resolve pointers to their values and omits struct
|
||||
// fields with nil values.
|
||||
func Stringify(message interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
v := reflect.ValueOf(message)
|
||||
stringifyValue(&buf, v)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// stringifyValue was heavily inspired by the goprotobuf library.
|
||||
func stringifyValue(w io.Writer, val reflect.Value) {
|
||||
if val.Kind() == reflect.Ptr && val.IsNil() {
|
||||
w.Write([]byte("<nil>"))
|
||||
return
|
||||
}
|
||||
v := reflect.Indirect(val)
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
fmt.Fprintf(w, `"%s"`, v)
|
||||
case reflect.Slice:
|
||||
w.Write([]byte{'['})
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
w.Write([]byte{' '})
|
||||
}
|
||||
stringifyValue(w, v.Index(i))
|
||||
}
|
||||
w.Write([]byte{']'})
|
||||
return
|
||||
case reflect.Struct:
|
||||
if v.Type().Name() != "" {
|
||||
w.Write([]byte(v.Type().String()))
|
||||
}
|
||||
w.Write([]byte{'{'})
|
||||
var sep bool
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fv := v.Field(i)
|
||||
if fv.Kind() == reflect.Ptr && fv.IsNil() {
|
||||
continue
|
||||
}
|
||||
if fv.Kind() == reflect.Slice && fv.IsNil() {
|
||||
continue
|
||||
}
|
||||
if sep {
|
||||
w.Write([]byte(", "))
|
||||
} else {
|
||||
sep = true
|
||||
}
|
||||
w.Write([]byte(v.Type().Field(i).Name))
|
||||
w.Write([]byte{':'})
|
||||
stringifyValue(w, fv)
|
||||
}
|
||||
w.Write([]byte{'}'})
|
||||
default:
|
||||
if v.CanInterface() {
|
||||
fmt.Fprint(w, v.Interface())
|
||||
}
|
||||
}
|
||||
}
|
12
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/BUILD.bazel
generated
vendored
Normal file
12
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/BUILD.bazel
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"uritemplates.go",
|
||||
"utils.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates",
|
||||
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
18
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/LICENSE
generated
vendored
Normal file
18
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
Copyright (c) 2013 Joshua Tacoma
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
361
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/uritemplates.go
generated
vendored
Normal file
361
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/uritemplates.go
generated
vendored
Normal file
|
@ -0,0 +1,361 @@
|
|||
// Copyright 2013 Joshua Tacoma. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uritemplates is a level 4 implementation of RFC 6570 (URI
|
||||
// Template, http://tools.ietf.org/html/rfc6570).
|
||||
//
|
||||
// To use uritemplates, parse a template string and expand it with a value
|
||||
// map:
|
||||
//
|
||||
// template, _ := uritemplates.Parse("https://api.github.com/repos{/user,repo}")
|
||||
// values := make(map[string]interface{})
|
||||
// values["user"] = "jtacoma"
|
||||
// values["repo"] = "uritemplates"
|
||||
// expanded, _ := template.ExpandString(values)
|
||||
// fmt.Printf(expanded)
|
||||
//
|
||||
package uritemplates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
|
||||
reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
|
||||
validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
|
||||
hex = []byte("0123456789ABCDEF")
|
||||
)
|
||||
|
||||
func pctEncode(src []byte) []byte {
|
||||
dst := make([]byte, len(src)*3)
|
||||
for i, b := range src {
|
||||
buf := dst[i*3 : i*3+3]
|
||||
buf[0] = 0x25
|
||||
buf[1] = hex[b/16]
|
||||
buf[2] = hex[b%16]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func escape(s string, allowReserved bool) (escaped string) {
|
||||
if allowReserved {
|
||||
escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
} else {
|
||||
escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
|
||||
}
|
||||
return escaped
|
||||
}
|
||||
|
||||
type Values map[string]interface{}
|
||||
|
||||
// A Template is a parsed representation of a URI template.
|
||||
type Template struct {
|
||||
raw string
|
||||
parts []templatePart
|
||||
}
|
||||
|
||||
// Parse parses a URI template string into a Template object.
|
||||
func Parse(rawtemplate string) (template *Template, err error) {
|
||||
template = new(Template)
|
||||
template.raw = rawtemplate
|
||||
split := strings.Split(rawtemplate, "{")
|
||||
template.parts = make([]templatePart, len(split)*2-1)
|
||||
for i, s := range split {
|
||||
if i == 0 {
|
||||
if strings.Contains(s, "}") {
|
||||
err = errors.New("unexpected }")
|
||||
break
|
||||
}
|
||||
template.parts[i].raw = s
|
||||
} else {
|
||||
subsplit := strings.Split(s, "}")
|
||||
if len(subsplit) != 2 {
|
||||
err = errors.New("malformed template")
|
||||
break
|
||||
}
|
||||
expression := subsplit[0]
|
||||
template.parts[i*2-1], err = parseExpression(expression)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
template.parts[i*2].raw = subsplit[1]
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
template = nil
|
||||
}
|
||||
return template, err
|
||||
}
|
||||
|
||||
type templatePart struct {
|
||||
raw string
|
||||
terms []templateTerm
|
||||
first string
|
||||
sep string
|
||||
named bool
|
||||
ifemp string
|
||||
allowReserved bool
|
||||
}
|
||||
|
||||
type templateTerm struct {
|
||||
name string
|
||||
explode bool
|
||||
truncate int
|
||||
}
|
||||
|
||||
func parseExpression(expression string) (result templatePart, err error) {
|
||||
switch expression[0] {
|
||||
case '+':
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
case '.':
|
||||
result.first = "."
|
||||
result.sep = "."
|
||||
expression = expression[1:]
|
||||
case '/':
|
||||
result.first = "/"
|
||||
result.sep = "/"
|
||||
expression = expression[1:]
|
||||
case ';':
|
||||
result.first = ";"
|
||||
result.sep = ";"
|
||||
result.named = true
|
||||
expression = expression[1:]
|
||||
case '?':
|
||||
result.first = "?"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '&':
|
||||
result.first = "&"
|
||||
result.sep = "&"
|
||||
result.named = true
|
||||
result.ifemp = "="
|
||||
expression = expression[1:]
|
||||
case '#':
|
||||
result.first = "#"
|
||||
result.sep = ","
|
||||
result.allowReserved = true
|
||||
expression = expression[1:]
|
||||
default:
|
||||
result.sep = ","
|
||||
}
|
||||
rawterms := strings.Split(expression, ",")
|
||||
result.terms = make([]templateTerm, len(rawterms))
|
||||
for i, raw := range rawterms {
|
||||
result.terms[i], err = parseTerm(raw)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func parseTerm(term string) (result templateTerm, err error) {
|
||||
if strings.HasSuffix(term, "*") {
|
||||
result.explode = true
|
||||
term = term[:len(term)-1]
|
||||
}
|
||||
split := strings.Split(term, ":")
|
||||
if len(split) == 1 {
|
||||
result.name = term
|
||||
} else if len(split) == 2 {
|
||||
result.name = split[0]
|
||||
var parsed int64
|
||||
parsed, err = strconv.ParseInt(split[1], 10, 0)
|
||||
result.truncate = int(parsed)
|
||||
} else {
|
||||
err = errors.New("multiple colons in same term")
|
||||
}
|
||||
if !validname.MatchString(result.name) {
|
||||
err = errors.New("not a valid name: " + result.name)
|
||||
}
|
||||
if result.explode && result.truncate > 0 {
|
||||
err = errors.New("both explode and prefix modifers on same term")
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Expand expands a URI template with a set of values to produce a string.
|
||||
func (self *Template) Expand(value interface{}) (string, error) {
|
||||
values, ismap := value.(Values)
|
||||
if !ismap {
|
||||
if m, ismap := struct2map(value); !ismap {
|
||||
return "", errors.New("expected Values, struct, or pointer to struct")
|
||||
} else {
|
||||
return self.Expand(m)
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for _, p := range self.parts {
|
||||
err := p.expand(&buf, values)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (self *templatePart) expand(buf *bytes.Buffer, values Values) error {
|
||||
if len(self.raw) > 0 {
|
||||
buf.WriteString(self.raw)
|
||||
return nil
|
||||
}
|
||||
var zeroLen = buf.Len()
|
||||
buf.WriteString(self.first)
|
||||
var firstLen = buf.Len()
|
||||
for _, term := range self.terms {
|
||||
value, exists := values[term.name]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if buf.Len() != firstLen {
|
||||
buf.WriteString(self.sep)
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
self.expandString(buf, term, v)
|
||||
case []interface{}:
|
||||
self.expandArray(buf, term, v)
|
||||
case map[string]interface{}:
|
||||
if term.truncate > 0 {
|
||||
return errors.New("cannot truncate a map expansion")
|
||||
}
|
||||
self.expandMap(buf, term, v)
|
||||
default:
|
||||
if m, ismap := struct2map(value); ismap {
|
||||
if term.truncate > 0 {
|
||||
return errors.New("cannot truncate a map expansion")
|
||||
}
|
||||
self.expandMap(buf, term, m)
|
||||
} else {
|
||||
str := fmt.Sprintf("%v", value)
|
||||
self.expandString(buf, term, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
if buf.Len() == firstLen {
|
||||
original := buf.Bytes()[:zeroLen]
|
||||
buf.Reset()
|
||||
buf.Write(original)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
|
||||
if self.named {
|
||||
buf.WriteString(name)
|
||||
if empty {
|
||||
buf.WriteString(self.ifemp)
|
||||
} else {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
self.expandName(buf, t.name, len(s) == 0)
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
|
||||
func (self *templatePart) expandArray(buf *bytes.Buffer, t templateTerm, a []interface{}) {
|
||||
if len(a) == 0 {
|
||||
return
|
||||
} else if !t.explode {
|
||||
self.expandName(buf, t.name, false)
|
||||
}
|
||||
for i, value := range a {
|
||||
if t.explode && i > 0 {
|
||||
buf.WriteString(self.sep)
|
||||
} else if i > 0 {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if len(s) > t.truncate && t.truncate > 0 {
|
||||
s = s[:t.truncate]
|
||||
}
|
||||
if self.named && t.explode {
|
||||
self.expandName(buf, t.name, len(s) == 0)
|
||||
}
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *templatePart) expandMap(buf *bytes.Buffer, t templateTerm, m map[string]interface{}) {
|
||||
if len(m) == 0 {
|
||||
return
|
||||
}
|
||||
if !t.explode {
|
||||
self.expandName(buf, t.name, len(m) == 0)
|
||||
}
|
||||
var firstLen = buf.Len()
|
||||
for k, value := range m {
|
||||
if firstLen != buf.Len() {
|
||||
if t.explode {
|
||||
buf.WriteString(self.sep)
|
||||
} else {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
}
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
default:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if t.explode {
|
||||
buf.WriteString(escape(k, self.allowReserved))
|
||||
buf.WriteRune('=')
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
} else {
|
||||
buf.WriteString(escape(k, self.allowReserved))
|
||||
buf.WriteRune(',')
|
||||
buf.WriteString(escape(s, self.allowReserved))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func struct2map(v interface{}) (map[string]interface{}, bool) {
|
||||
value := reflect.ValueOf(v)
|
||||
switch value.Type().Kind() {
|
||||
case reflect.Ptr:
|
||||
return struct2map(value.Elem().Interface())
|
||||
case reflect.Struct:
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
tag := value.Type().Field(i).Tag
|
||||
var name string
|
||||
if strings.Contains(string(tag), ":") {
|
||||
name = tag.Get("uri")
|
||||
} else {
|
||||
name = strings.TrimSpace(string(tag))
|
||||
}
|
||||
if len(name) == 0 {
|
||||
name = value.Type().Field(i).Name
|
||||
}
|
||||
m[name] = value.Field(i).Interface()
|
||||
}
|
||||
return m, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/utils.go
generated
vendored
Normal file
9
vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
package uritemplates
|
||||
|
||||
func Expand(path string, values Values) (string, error) {
|
||||
template, err := Parse(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return template.Expand(values)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package spotinst
|
||||
|
||||
// SDKVersion is the current version of the SDK.
|
||||
const SDKVersion = "3.2.24"
|
||||
|
||||
// SDKName is the name of the SDK.
|
||||
const SDKName = "spotinst-sdk-go"
|
|
@ -1,22 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"default_rate_limiters.go",
|
||||
"delaying_queue.go",
|
||||
"doc.go",
|
||||
"metrics.go",
|
||||
"parallelizer.go",
|
||||
"queue.go",
|
||||
"rate_limitting_queue.go",
|
||||
],
|
||||
importmap = "k8s.io/kops/vendor/k8s.io/client-go/util/workqueue",
|
||||
importpath = "k8s.io/client-go/util/workqueue",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/golang.org/x/time/rate:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/clock:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
],
|
||||
)
|
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type RateLimiter interface {
|
||||
// When gets an item and gets to decide how long that item should wait
|
||||
When(item interface{}) time.Duration
|
||||
// Forget indicates that an item is finished being retried. Doesn't matter whether its for perm failing
|
||||
// or for success, we'll stop tracking it
|
||||
Forget(item interface{})
|
||||
// NumRequeues returns back how many failures the item has had
|
||||
NumRequeues(item interface{}) int
|
||||
}
|
||||
|
||||
// DefaultControllerRateLimiter is a no-arg constructor for a default rate limiter for a workqueue. It has
|
||||
// both overall and per-item rate limitting. The overall is a token bucket and the per-item is exponential
|
||||
func DefaultControllerRateLimiter() RateLimiter {
|
||||
return NewMaxOfRateLimiter(
|
||||
NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
|
||||
// 10 qps, 100 bucket size. This is only for retry speed and its only the overall factor (not per item)
|
||||
&BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
|
||||
)
|
||||
}
|
||||
|
||||
// BucketRateLimiter adapts a standard bucket to the workqueue ratelimiter API
|
||||
type BucketRateLimiter struct {
|
||||
*rate.Limiter
|
||||
}
|
||||
|
||||
var _ RateLimiter = &BucketRateLimiter{}
|
||||
|
||||
func (r *BucketRateLimiter) When(item interface{}) time.Duration {
|
||||
return r.Limiter.Reserve().Delay()
|
||||
}
|
||||
|
||||
func (r *BucketRateLimiter) NumRequeues(item interface{}) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *BucketRateLimiter) Forget(item interface{}) {
|
||||
}
|
||||
|
||||
// ItemExponentialFailureRateLimiter does a simple baseDelay*10^<num-failures> limit
|
||||
// dealing with max failures and expiration are up to the caller
|
||||
type ItemExponentialFailureRateLimiter struct {
|
||||
failuresLock sync.Mutex
|
||||
failures map[interface{}]int
|
||||
|
||||
baseDelay time.Duration
|
||||
maxDelay time.Duration
|
||||
}
|
||||
|
||||
var _ RateLimiter = &ItemExponentialFailureRateLimiter{}
|
||||
|
||||
func NewItemExponentialFailureRateLimiter(baseDelay time.Duration, maxDelay time.Duration) RateLimiter {
|
||||
return &ItemExponentialFailureRateLimiter{
|
||||
failures: map[interface{}]int{},
|
||||
baseDelay: baseDelay,
|
||||
maxDelay: maxDelay,
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultItemBasedRateLimiter() RateLimiter {
|
||||
return NewItemExponentialFailureRateLimiter(time.Millisecond, 1000*time.Second)
|
||||
}
|
||||
|
||||
func (r *ItemExponentialFailureRateLimiter) When(item interface{}) time.Duration {
|
||||
r.failuresLock.Lock()
|
||||
defer r.failuresLock.Unlock()
|
||||
|
||||
exp := r.failures[item]
|
||||
r.failures[item] = r.failures[item] + 1
|
||||
|
||||
// The backoff is capped such that 'calculated' value never overflows.
|
||||
backoff := float64(r.baseDelay.Nanoseconds()) * math.Pow(2, float64(exp))
|
||||
if backoff > math.MaxInt64 {
|
||||
return r.maxDelay
|
||||
}
|
||||
|
||||
calculated := time.Duration(backoff)
|
||||
if calculated > r.maxDelay {
|
||||
return r.maxDelay
|
||||
}
|
||||
|
||||
return calculated
|
||||
}
|
||||
|
||||
func (r *ItemExponentialFailureRateLimiter) NumRequeues(item interface{}) int {
|
||||
r.failuresLock.Lock()
|
||||
defer r.failuresLock.Unlock()
|
||||
|
||||
return r.failures[item]
|
||||
}
|
||||
|
||||
func (r *ItemExponentialFailureRateLimiter) Forget(item interface{}) {
|
||||
r.failuresLock.Lock()
|
||||
defer r.failuresLock.Unlock()
|
||||
|
||||
delete(r.failures, item)
|
||||
}
|
||||
|
||||
// ItemFastSlowRateLimiter does a quick retry for a certain number of attempts, then a slow retry after that
|
||||
type ItemFastSlowRateLimiter struct {
|
||||
failuresLock sync.Mutex
|
||||
failures map[interface{}]int
|
||||
|
||||
maxFastAttempts int
|
||||
fastDelay time.Duration
|
||||
slowDelay time.Duration
|
||||
}
|
||||
|
||||
var _ RateLimiter = &ItemFastSlowRateLimiter{}
|
||||
|
||||
func NewItemFastSlowRateLimiter(fastDelay, slowDelay time.Duration, maxFastAttempts int) RateLimiter {
|
||||
return &ItemFastSlowRateLimiter{
|
||||
failures: map[interface{}]int{},
|
||||
fastDelay: fastDelay,
|
||||
slowDelay: slowDelay,
|
||||
maxFastAttempts: maxFastAttempts,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ItemFastSlowRateLimiter) When(item interface{}) time.Duration {
|
||||
r.failuresLock.Lock()
|
||||
defer r.failuresLock.Unlock()
|
||||
|
||||
r.failures[item] = r.failures[item] + 1
|
||||
|
||||
if r.failures[item] <= r.maxFastAttempts {
|
||||
return r.fastDelay
|
||||
}
|
||||
|
||||
return r.slowDelay
|
||||
}
|
||||
|
||||
func (r *ItemFastSlowRateLimiter) NumRequeues(item interface{}) int {
|
||||
r.failuresLock.Lock()
|
||||
defer r.failuresLock.Unlock()
|
||||
|
||||
return r.failures[item]
|
||||
}
|
||||
|
||||
func (r *ItemFastSlowRateLimiter) Forget(item interface{}) {
|
||||
r.failuresLock.Lock()
|
||||
defer r.failuresLock.Unlock()
|
||||
|
||||
delete(r.failures, item)
|
||||
}
|
||||
|
||||
// MaxOfRateLimiter calls every RateLimiter and returns the worst case response
|
||||
// When used with a token bucket limiter, the burst could be apparently exceeded in cases where particular items
|
||||
// were separately delayed a longer time.
|
||||
type MaxOfRateLimiter struct {
|
||||
limiters []RateLimiter
|
||||
}
|
||||
|
||||
func (r *MaxOfRateLimiter) When(item interface{}) time.Duration {
|
||||
ret := time.Duration(0)
|
||||
for _, limiter := range r.limiters {
|
||||
curr := limiter.When(item)
|
||||
if curr > ret {
|
||||
ret = curr
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func NewMaxOfRateLimiter(limiters ...RateLimiter) RateLimiter {
|
||||
return &MaxOfRateLimiter{limiters: limiters}
|
||||
}
|
||||
|
||||
func (r *MaxOfRateLimiter) NumRequeues(item interface{}) int {
|
||||
ret := 0
|
||||
for _, limiter := range r.limiters {
|
||||
curr := limiter.NumRequeues(item)
|
||||
if curr > ret {
|
||||
ret = curr
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *MaxOfRateLimiter) Forget(item interface{}) {
|
||||
for _, limiter := range r.limiters {
|
||||
limiter.Forget(item)
|
||||
}
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
)
|
||||
|
||||
// DelayingInterface is an Interface that can Add an item at a later time. This makes it easier to
|
||||
// requeue items after failures without ending up in a hot-loop.
|
||||
type DelayingInterface interface {
|
||||
Interface
|
||||
// AddAfter adds an item to the workqueue after the indicated duration has passed
|
||||
AddAfter(item interface{}, duration time.Duration)
|
||||
}
|
||||
|
||||
// NewDelayingQueue constructs a new workqueue with delayed queuing ability
|
||||
func NewDelayingQueue() DelayingInterface {
|
||||
return newDelayingQueue(clock.RealClock{}, "")
|
||||
}
|
||||
|
||||
func NewNamedDelayingQueue(name string) DelayingInterface {
|
||||
return newDelayingQueue(clock.RealClock{}, name)
|
||||
}
|
||||
|
||||
func newDelayingQueue(clock clock.Clock, name string) DelayingInterface {
|
||||
ret := &delayingType{
|
||||
Interface: NewNamed(name),
|
||||
clock: clock,
|
||||
heartbeat: clock.NewTicker(maxWait),
|
||||
stopCh: make(chan struct{}),
|
||||
waitingForAddCh: make(chan *waitFor, 1000),
|
||||
metrics: newRetryMetrics(name),
|
||||
}
|
||||
|
||||
go ret.waitingLoop()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// delayingType wraps an Interface and provides delayed re-enquing
|
||||
type delayingType struct {
|
||||
Interface
|
||||
|
||||
// clock tracks time for delayed firing
|
||||
clock clock.Clock
|
||||
|
||||
// stopCh lets us signal a shutdown to the waiting loop
|
||||
stopCh chan struct{}
|
||||
|
||||
// heartbeat ensures we wait no more than maxWait before firing
|
||||
heartbeat clock.Ticker
|
||||
|
||||
// waitingForAddCh is a buffered channel that feeds waitingForAdd
|
||||
waitingForAddCh chan *waitFor
|
||||
|
||||
// metrics counts the number of retries
|
||||
metrics retryMetrics
|
||||
}
|
||||
|
||||
// waitFor holds the data to add and the time it should be added
|
||||
type waitFor struct {
|
||||
data t
|
||||
readyAt time.Time
|
||||
// index in the priority queue (heap)
|
||||
index int
|
||||
}
|
||||
|
||||
// waitForPriorityQueue implements a priority queue for waitFor items.
|
||||
//
|
||||
// waitForPriorityQueue implements heap.Interface. The item occurring next in
|
||||
// time (i.e., the item with the smallest readyAt) is at the root (index 0).
|
||||
// Peek returns this minimum item at index 0. Pop returns the minimum item after
|
||||
// it has been removed from the queue and placed at index Len()-1 by
|
||||
// container/heap. Push adds an item at index Len(), and container/heap
|
||||
// percolates it into the correct location.
|
||||
type waitForPriorityQueue []*waitFor
|
||||
|
||||
func (pq waitForPriorityQueue) Len() int {
|
||||
return len(pq)
|
||||
}
|
||||
func (pq waitForPriorityQueue) Less(i, j int) bool {
|
||||
return pq[i].readyAt.Before(pq[j].readyAt)
|
||||
}
|
||||
func (pq waitForPriorityQueue) Swap(i, j int) {
|
||||
pq[i], pq[j] = pq[j], pq[i]
|
||||
pq[i].index = i
|
||||
pq[j].index = j
|
||||
}
|
||||
|
||||
// Push adds an item to the queue. Push should not be called directly; instead,
|
||||
// use `heap.Push`.
|
||||
func (pq *waitForPriorityQueue) Push(x interface{}) {
|
||||
n := len(*pq)
|
||||
item := x.(*waitFor)
|
||||
item.index = n
|
||||
*pq = append(*pq, item)
|
||||
}
|
||||
|
||||
// Pop removes an item from the queue. Pop should not be called directly;
|
||||
// instead, use `heap.Pop`.
|
||||
func (pq *waitForPriorityQueue) Pop() interface{} {
|
||||
n := len(*pq)
|
||||
item := (*pq)[n-1]
|
||||
item.index = -1
|
||||
*pq = (*pq)[0:(n - 1)]
|
||||
return item
|
||||
}
|
||||
|
||||
// Peek returns the item at the beginning of the queue, without removing the
|
||||
// item or otherwise mutating the queue. It is safe to call directly.
|
||||
func (pq waitForPriorityQueue) Peek() interface{} {
|
||||
return pq[0]
|
||||
}
|
||||
|
||||
// ShutDown gives a way to shut off this queue
|
||||
func (q *delayingType) ShutDown() {
|
||||
q.Interface.ShutDown()
|
||||
close(q.stopCh)
|
||||
q.heartbeat.Stop()
|
||||
}
|
||||
|
||||
// AddAfter adds the given item to the work queue after the given delay
|
||||
func (q *delayingType) AddAfter(item interface{}, duration time.Duration) {
|
||||
// don't add if we're already shutting down
|
||||
if q.ShuttingDown() {
|
||||
return
|
||||
}
|
||||
|
||||
q.metrics.retry()
|
||||
|
||||
// immediately add things with no delay
|
||||
if duration <= 0 {
|
||||
q.Add(item)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-q.stopCh:
|
||||
// unblock if ShutDown() is called
|
||||
case q.waitingForAddCh <- &waitFor{data: item, readyAt: q.clock.Now().Add(duration)}:
|
||||
}
|
||||
}
|
||||
|
||||
// maxWait keeps a max bound on the wait time. It's just insurance against weird things happening.
|
||||
// Checking the queue every 10 seconds isn't expensive and we know that we'll never end up with an
|
||||
// expired item sitting for more than 10 seconds.
|
||||
const maxWait = 10 * time.Second
|
||||
|
||||
// waitingLoop runs until the workqueue is shutdown and keeps a check on the list of items to be added.
|
||||
func (q *delayingType) waitingLoop() {
|
||||
defer utilruntime.HandleCrash()
|
||||
|
||||
// Make a placeholder channel to use when there are no items in our list
|
||||
never := make(<-chan time.Time)
|
||||
|
||||
waitingForQueue := &waitForPriorityQueue{}
|
||||
heap.Init(waitingForQueue)
|
||||
|
||||
waitingEntryByData := map[t]*waitFor{}
|
||||
|
||||
for {
|
||||
if q.Interface.ShuttingDown() {
|
||||
return
|
||||
}
|
||||
|
||||
now := q.clock.Now()
|
||||
|
||||
// Add ready entries
|
||||
for waitingForQueue.Len() > 0 {
|
||||
entry := waitingForQueue.Peek().(*waitFor)
|
||||
if entry.readyAt.After(now) {
|
||||
break
|
||||
}
|
||||
|
||||
entry = heap.Pop(waitingForQueue).(*waitFor)
|
||||
q.Add(entry.data)
|
||||
delete(waitingEntryByData, entry.data)
|
||||
}
|
||||
|
||||
// Set up a wait for the first item's readyAt (if one exists)
|
||||
nextReadyAt := never
|
||||
if waitingForQueue.Len() > 0 {
|
||||
entry := waitingForQueue.Peek().(*waitFor)
|
||||
nextReadyAt = q.clock.After(entry.readyAt.Sub(now))
|
||||
}
|
||||
|
||||
select {
|
||||
case <-q.stopCh:
|
||||
return
|
||||
|
||||
case <-q.heartbeat.C():
|
||||
// continue the loop, which will add ready items
|
||||
|
||||
case <-nextReadyAt:
|
||||
// continue the loop, which will add ready items
|
||||
|
||||
case waitEntry := <-q.waitingForAddCh:
|
||||
if waitEntry.readyAt.After(q.clock.Now()) {
|
||||
insert(waitingForQueue, waitingEntryByData, waitEntry)
|
||||
} else {
|
||||
q.Add(waitEntry.data)
|
||||
}
|
||||
|
||||
drained := false
|
||||
for !drained {
|
||||
select {
|
||||
case waitEntry := <-q.waitingForAddCh:
|
||||
if waitEntry.readyAt.After(q.clock.Now()) {
|
||||
insert(waitingForQueue, waitingEntryByData, waitEntry)
|
||||
} else {
|
||||
q.Add(waitEntry.data)
|
||||
}
|
||||
default:
|
||||
drained = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// insert adds the entry to the priority queue, or updates the readyAt if it already exists in the queue
|
||||
func insert(q *waitForPriorityQueue, knownEntries map[t]*waitFor, entry *waitFor) {
|
||||
// if the entry already exists, update the time only if it would cause the item to be queued sooner
|
||||
existing, exists := knownEntries[entry.data]
|
||||
if exists {
|
||||
if existing.readyAt.After(entry.readyAt) {
|
||||
existing.readyAt = entry.readyAt
|
||||
heap.Fix(q, existing.index)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
heap.Push(q, entry)
|
||||
knownEntries[entry.data] = entry
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package workqueue provides a simple queue that supports the following
|
||||
// features:
|
||||
// * Fair: items processed in the order in which they are added.
|
||||
// * Stingy: a single item will not be processed multiple times concurrently,
|
||||
// and if an item is added multiple times before it can be processed, it
|
||||
// will only be processed once.
|
||||
// * Multiple consumers and producers. In particular, it is allowed for an
|
||||
// item to be reenqueued while it is being processed.
|
||||
// * Shutdown notifications.
|
||||
package workqueue
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This file provides abstractions for setting the provider (e.g., prometheus)
|
||||
// of metrics.
|
||||
|
||||
type queueMetrics interface {
|
||||
add(item t)
|
||||
get(item t)
|
||||
done(item t)
|
||||
}
|
||||
|
||||
// GaugeMetric represents a single numerical value that can arbitrarily go up
|
||||
// and down.
|
||||
type GaugeMetric interface {
|
||||
Inc()
|
||||
Dec()
|
||||
}
|
||||
|
||||
// CounterMetric represents a single numerical value that only ever
|
||||
// goes up.
|
||||
type CounterMetric interface {
|
||||
Inc()
|
||||
}
|
||||
|
||||
// SummaryMetric captures individual observations.
|
||||
type SummaryMetric interface {
|
||||
Observe(float64)
|
||||
}
|
||||
|
||||
type noopMetric struct{}
|
||||
|
||||
func (noopMetric) Inc() {}
|
||||
func (noopMetric) Dec() {}
|
||||
func (noopMetric) Observe(float64) {}
|
||||
|
||||
type defaultQueueMetrics struct {
|
||||
// current depth of a workqueue
|
||||
depth GaugeMetric
|
||||
// total number of adds handled by a workqueue
|
||||
adds CounterMetric
|
||||
// how long an item stays in a workqueue
|
||||
latency SummaryMetric
|
||||
// how long processing an item from a workqueue takes
|
||||
workDuration SummaryMetric
|
||||
addTimes map[t]time.Time
|
||||
processingStartTimes map[t]time.Time
|
||||
}
|
||||
|
||||
func (m *defaultQueueMetrics) add(item t) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.adds.Inc()
|
||||
m.depth.Inc()
|
||||
if _, exists := m.addTimes[item]; !exists {
|
||||
m.addTimes[item] = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueueMetrics) get(item t) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.depth.Dec()
|
||||
m.processingStartTimes[item] = time.Now()
|
||||
if startTime, exists := m.addTimes[item]; exists {
|
||||
m.latency.Observe(sinceInMicroseconds(startTime))
|
||||
delete(m.addTimes, item)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultQueueMetrics) done(item t) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if startTime, exists := m.processingStartTimes[item]; exists {
|
||||
m.workDuration.Observe(sinceInMicroseconds(startTime))
|
||||
delete(m.processingStartTimes, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the time since the specified start in microseconds.
|
||||
func sinceInMicroseconds(start time.Time) float64 {
|
||||
return float64(time.Since(start).Nanoseconds() / time.Microsecond.Nanoseconds())
|
||||
}
|
||||
|
||||
type retryMetrics interface {
|
||||
retry()
|
||||
}
|
||||
|
||||
type defaultRetryMetrics struct {
|
||||
retries CounterMetric
|
||||
}
|
||||
|
||||
func (m *defaultRetryMetrics) retry() {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.retries.Inc()
|
||||
}
|
||||
|
||||
// MetricsProvider generates various metrics used by the queue.
|
||||
type MetricsProvider interface {
|
||||
NewDepthMetric(name string) GaugeMetric
|
||||
NewAddsMetric(name string) CounterMetric
|
||||
NewLatencyMetric(name string) SummaryMetric
|
||||
NewWorkDurationMetric(name string) SummaryMetric
|
||||
NewRetriesMetric(name string) CounterMetric
|
||||
}
|
||||
|
||||
type noopMetricsProvider struct{}
|
||||
|
||||
func (_ noopMetricsProvider) NewDepthMetric(name string) GaugeMetric {
|
||||
return noopMetric{}
|
||||
}
|
||||
|
||||
func (_ noopMetricsProvider) NewAddsMetric(name string) CounterMetric {
|
||||
return noopMetric{}
|
||||
}
|
||||
|
||||
func (_ noopMetricsProvider) NewLatencyMetric(name string) SummaryMetric {
|
||||
return noopMetric{}
|
||||
}
|
||||
|
||||
func (_ noopMetricsProvider) NewWorkDurationMetric(name string) SummaryMetric {
|
||||
return noopMetric{}
|
||||
}
|
||||
|
||||
func (_ noopMetricsProvider) NewRetriesMetric(name string) CounterMetric {
|
||||
return noopMetric{}
|
||||
}
|
||||
|
||||
var metricsFactory = struct {
|
||||
metricsProvider MetricsProvider
|
||||
setProviders sync.Once
|
||||
}{
|
||||
metricsProvider: noopMetricsProvider{},
|
||||
}
|
||||
|
||||
func newQueueMetrics(name string) queueMetrics {
|
||||
var ret *defaultQueueMetrics
|
||||
if len(name) == 0 {
|
||||
return ret
|
||||
}
|
||||
return &defaultQueueMetrics{
|
||||
depth: metricsFactory.metricsProvider.NewDepthMetric(name),
|
||||
adds: metricsFactory.metricsProvider.NewAddsMetric(name),
|
||||
latency: metricsFactory.metricsProvider.NewLatencyMetric(name),
|
||||
workDuration: metricsFactory.metricsProvider.NewWorkDurationMetric(name),
|
||||
addTimes: map[t]time.Time{},
|
||||
processingStartTimes: map[t]time.Time{},
|
||||
}
|
||||
}
|
||||
|
||||
func newRetryMetrics(name string) retryMetrics {
|
||||
var ret *defaultRetryMetrics
|
||||
if len(name) == 0 {
|
||||
return ret
|
||||
}
|
||||
return &defaultRetryMetrics{
|
||||
retries: metricsFactory.metricsProvider.NewRetriesMetric(name),
|
||||
}
|
||||
}
|
||||
|
||||
// SetProvider sets the metrics provider of the metricsFactory.
|
||||
func SetProvider(metricsProvider MetricsProvider) {
|
||||
metricsFactory.setProviders.Do(func() {
|
||||
metricsFactory.metricsProvider = metricsProvider
|
||||
})
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
)
|
||||
|
||||
type DoWorkPieceFunc func(piece int)
|
||||
|
||||
// Parallelize is a very simple framework that allow for parallelizing
|
||||
// N independent pieces of work.
|
||||
func Parallelize(workers, pieces int, doWorkPiece DoWorkPieceFunc) {
|
||||
toProcess := make(chan int, pieces)
|
||||
for i := 0; i < pieces; i++ {
|
||||
toProcess <- i
|
||||
}
|
||||
close(toProcess)
|
||||
|
||||
if pieces < workers {
|
||||
workers = pieces
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(workers)
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer wg.Done()
|
||||
for piece := range toProcess {
|
||||
doWorkPiece(piece)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Add(item interface{})
|
||||
Len() int
|
||||
Get() (item interface{}, shutdown bool)
|
||||
Done(item interface{})
|
||||
ShutDown()
|
||||
ShuttingDown() bool
|
||||
}
|
||||
|
||||
// New constructs a new work queue (see the package comment).
|
||||
func New() *Type {
|
||||
return NewNamed("")
|
||||
}
|
||||
|
||||
func NewNamed(name string) *Type {
|
||||
return &Type{
|
||||
dirty: set{},
|
||||
processing: set{},
|
||||
cond: sync.NewCond(&sync.Mutex{}),
|
||||
metrics: newQueueMetrics(name),
|
||||
}
|
||||
}
|
||||
|
||||
// Type is a work queue (see the package comment).
|
||||
type Type struct {
|
||||
// queue defines the order in which we will work on items. Every
|
||||
// element of queue should be in the dirty set and not in the
|
||||
// processing set.
|
||||
queue []t
|
||||
|
||||
// dirty defines all of the items that need to be processed.
|
||||
dirty set
|
||||
|
||||
// Things that are currently being processed are in the processing set.
|
||||
// These things may be simultaneously in the dirty set. When we finish
|
||||
// processing something and remove it from this set, we'll check if
|
||||
// it's in the dirty set, and if so, add it to the queue.
|
||||
processing set
|
||||
|
||||
cond *sync.Cond
|
||||
|
||||
shuttingDown bool
|
||||
|
||||
metrics queueMetrics
|
||||
}
|
||||
|
||||
type empty struct{}
|
||||
type t interface{}
|
||||
type set map[t]empty
|
||||
|
||||
func (s set) has(item t) bool {
|
||||
_, exists := s[item]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s set) insert(item t) {
|
||||
s[item] = empty{}
|
||||
}
|
||||
|
||||
func (s set) delete(item t) {
|
||||
delete(s, item)
|
||||
}
|
||||
|
||||
// Add marks item as needing processing.
|
||||
func (q *Type) Add(item interface{}) {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
if q.shuttingDown {
|
||||
return
|
||||
}
|
||||
if q.dirty.has(item) {
|
||||
return
|
||||
}
|
||||
|
||||
q.metrics.add(item)
|
||||
|
||||
q.dirty.insert(item)
|
||||
if q.processing.has(item) {
|
||||
return
|
||||
}
|
||||
|
||||
q.queue = append(q.queue, item)
|
||||
q.cond.Signal()
|
||||
}
|
||||
|
||||
// Len returns the current queue length, for informational purposes only. You
|
||||
// shouldn't e.g. gate a call to Add() or Get() on Len() being a particular
|
||||
// value, that can't be synchronized properly.
|
||||
func (q *Type) Len() int {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
return len(q.queue)
|
||||
}
|
||||
|
||||
// Get blocks until it can return an item to be processed. If shutdown = true,
|
||||
// the caller should end their goroutine. You must call Done with item when you
|
||||
// have finished processing it.
|
||||
func (q *Type) Get() (item interface{}, shutdown bool) {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
for len(q.queue) == 0 && !q.shuttingDown {
|
||||
q.cond.Wait()
|
||||
}
|
||||
if len(q.queue) == 0 {
|
||||
// We must be shutting down.
|
||||
return nil, true
|
||||
}
|
||||
|
||||
item, q.queue = q.queue[0], q.queue[1:]
|
||||
|
||||
q.metrics.get(item)
|
||||
|
||||
q.processing.insert(item)
|
||||
q.dirty.delete(item)
|
||||
|
||||
return item, false
|
||||
}
|
||||
|
||||
// Done marks item as done processing, and if it has been marked as dirty again
|
||||
// while it was being processed, it will be re-added to the queue for
|
||||
// re-processing.
|
||||
func (q *Type) Done(item interface{}) {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
q.metrics.done(item)
|
||||
|
||||
q.processing.delete(item)
|
||||
if q.dirty.has(item) {
|
||||
q.queue = append(q.queue, item)
|
||||
q.cond.Signal()
|
||||
}
|
||||
}
|
||||
|
||||
// ShutDown will cause q to ignore all new items added to it. As soon as the
|
||||
// worker goroutines have drained the existing items in the queue, they will be
|
||||
// instructed to exit.
|
||||
func (q *Type) ShutDown() {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
q.shuttingDown = true
|
||||
q.cond.Broadcast()
|
||||
}
|
||||
|
||||
func (q *Type) ShuttingDown() bool {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
return q.shuttingDown
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
// RateLimitingInterface is an interface that rate limits items being added to the queue.
|
||||
type RateLimitingInterface interface {
|
||||
DelayingInterface
|
||||
|
||||
// AddRateLimited adds an item to the workqueue after the rate limiter says its ok
|
||||
AddRateLimited(item interface{})
|
||||
|
||||
// Forget indicates that an item is finished being retried. Doesn't matter whether its for perm failing
|
||||
// or for success, we'll stop the rate limiter from tracking it. This only clears the `rateLimiter`, you
|
||||
// still have to call `Done` on the queue.
|
||||
Forget(item interface{})
|
||||
|
||||
// NumRequeues returns back how many times the item was requeued
|
||||
NumRequeues(item interface{}) int
|
||||
}
|
||||
|
||||
// NewRateLimitingQueue constructs a new workqueue with rateLimited queuing ability
|
||||
// Remember to call Forget! If you don't, you may end up tracking failures forever.
|
||||
func NewRateLimitingQueue(rateLimiter RateLimiter) RateLimitingInterface {
|
||||
return &rateLimitingType{
|
||||
DelayingInterface: NewDelayingQueue(),
|
||||
rateLimiter: rateLimiter,
|
||||
}
|
||||
}
|
||||
|
||||
func NewNamedRateLimitingQueue(rateLimiter RateLimiter, name string) RateLimitingInterface {
|
||||
return &rateLimitingType{
|
||||
DelayingInterface: NewNamedDelayingQueue(name),
|
||||
rateLimiter: rateLimiter,
|
||||
}
|
||||
}
|
||||
|
||||
// rateLimitingType wraps an Interface and provides rateLimited re-enquing
|
||||
type rateLimitingType struct {
|
||||
DelayingInterface
|
||||
|
||||
rateLimiter RateLimiter
|
||||
}
|
||||
|
||||
// AddRateLimited AddAfter's the item based on the time when the rate limiter says its ok
|
||||
func (q *rateLimitingType) AddRateLimited(item interface{}) {
|
||||
q.DelayingInterface.AddAfter(item, q.rateLimiter.When(item))
|
||||
}
|
||||
|
||||
func (q *rateLimitingType) NumRequeues(item interface{}) int {
|
||||
return q.rateLimiter.NumRequeues(item)
|
||||
}
|
||||
|
||||
func (q *rateLimitingType) Forget(item interface{}) {
|
||||
q.rateLimiter.Forget(item)
|
||||
}
|
Loading…
Reference in New Issue