fix(spot): change `ScaleDown.MaxPercentage` from int to float64

This commit is contained in:
liranp 2020-08-04 23:39:02 +03:00
parent 6ae2bf85f8
commit 4d8866824f
No known key found for this signature in database
GPG Key ID: D5F03857002C1A93
22 changed files with 951 additions and 128 deletions

2
go.mod
View File

@ -91,7 +91,7 @@ require (
github.com/spf13/cobra v0.0.5 github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.4.0 github.com/spf13/viper v1.4.0
github.com/spotinst/spotinst-sdk-go v1.49.0 github.com/spotinst/spotinst-sdk-go v1.56.0
github.com/stretchr/testify v1.5.1 github.com/stretchr/testify v1.5.1
github.com/urfave/cli v1.20.0 github.com/urfave/cli v1.20.0
github.com/weaveworks/mesh v0.0.0-20170419100114-1f158d31de55 github.com/weaveworks/mesh v0.0.0-20170419100114-1f158d31de55

4
go.sum
View File

@ -778,8 +778,8 @@ github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spotinst/spotinst-sdk-go v1.49.0 h1:JmsLlsgd/cCKpcn04HrParo5r34owDzIC12IW1SpSEs= github.com/spotinst/spotinst-sdk-go v1.56.0 h1:w7OnAWXWPxvOvqjYv4i0kN70KWGTNcyoNB72suIA30g=
github.com/spotinst/spotinst-sdk-go v1.49.0/go.mod h1:nWi2DyjUi1WUZclpsqZFXvImsU0T39ppqqHwC4/T5mw= github.com/spotinst/spotinst-sdk-go v1.56.0/go.mod h1:nWi2DyjUi1WUZclpsqZFXvImsU0T39ppqqHwC4/T5mw=
github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY= github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=

View File

@ -815,14 +815,14 @@ func (b *InstanceGroupModelBuilder) buildAutoScalerOpts(clusterID string, ig *ko
case InstanceGroupLabelAutoScalerScaleDownMaxPercentage: case InstanceGroupLabelAutoScalerScaleDownMaxPercentage:
{ {
v, err := parseInt(v) v, err := parseFloat(v)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if opts.Down == nil { if opts.Down == nil {
opts.Down = new(spotinsttasks.AutoScalerDownOpts) opts.Down = new(spotinsttasks.AutoScalerDownOpts)
} }
opts.Down.MaxPercentage = fi.Int(int(fi.Int64Value(v))) opts.Down.MaxPercentage = v
} }
case InstanceGroupLabelAutoScalerScaleDownEvaluationPeriods: case InstanceGroupLabelAutoScalerScaleDownEvaluationPeriods:

View File

@ -96,7 +96,7 @@ type AutoScalerHeadroomOpts struct {
} }
type AutoScalerDownOpts struct { type AutoScalerDownOpts struct {
MaxPercentage *int MaxPercentage *float64
EvaluationPeriods *int EvaluationPeriods *int
} }
@ -1349,7 +1349,7 @@ type terraformAutoScalerHeadroom struct {
} }
type terraformAutoScalerDown struct { type terraformAutoScalerDown struct {
MaxPercentage *int `json:"max_scale_down_percentage,omitempty" cty:"max_scale_down_percentage"` MaxPercentage *float64 `json:"max_scale_down_percentage,omitempty" cty:"max_scale_down_percentage"`
EvaluationPeriods *int `json:"evaluation_periods,omitempty" cty:"evaluation_periods"` EvaluationPeriods *int `json:"evaluation_periods,omitempty" cty:"evaluation_periods"`
} }

View File

@ -175,7 +175,7 @@ type AutoScaleHeadroom struct {
type AutoScaleDown struct { type AutoScaleDown struct {
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"` EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
MaxScaleDownPercentage *int `json:"maxScaleDownPercentage,omitempty"` MaxScaleDownPercentage *float64 `json:"maxScaleDownPercentage,omitempty"`
forceSendFields []string forceSendFields []string
nullFields []string nullFields []string
@ -440,6 +440,7 @@ type ScalingPolicy struct {
Action *Action `json:"action,omitempty"` Action *Action `json:"action,omitempty"`
Target *float64 `json:"target,omitempty"` Target *float64 `json:"target,omitempty"`
IsEnabled *bool `json:"isEnabled,omitempty"` IsEnabled *bool `json:"isEnabled,omitempty"`
MaxCapacityPerScale *string `json:"maxCapacityPerScale,omitempty"`
Predictive *Predictive `json:"predictive,omitempty"` Predictive *Predictive `json:"predictive,omitempty"`
forceSendFields []string forceSendFields []string
@ -2213,7 +2214,7 @@ func (o *AutoScaleDown) SetEvaluationPeriods(v *int) *AutoScaleDown {
return o return o
} }
func (o *AutoScaleDown) SetMaxScaleDownPercentage(v *int) *AutoScaleDown { func (o *AutoScaleDown) SetMaxScaleDownPercentage(v *float64) *AutoScaleDown {
if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil { if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil {
o.nullFields = append(o.nullFields, "MaxScaleDownPercentage") o.nullFields = append(o.nullFields, "MaxScaleDownPercentage")
} }
@ -2792,6 +2793,13 @@ func (o *ScalingPolicy) SetIsEnabled(v *bool) *ScalingPolicy {
return o return o
} }
func (o *ScalingPolicy) SetMaxCapacityPerScale(v *string) *ScalingPolicy {
if o.MaxCapacityPerScale = v; o.MaxCapacityPerScale == nil {
o.nullFields = append(o.nullFields, "MaxCapacityPerScale")
}
return o
}
// endregion // endregion
// region Action // region Action

View File

@ -22,10 +22,13 @@ type LaunchSpec struct {
RootVolumeSize *int `json:"rootVolumeSize,omitempty"` RootVolumeSize *int `json:"rootVolumeSize,omitempty"`
SecurityGroupIDs []string `json:"securityGroupIds,omitempty"` SecurityGroupIDs []string `json:"securityGroupIds,omitempty"`
SubnetIDs []string `json:"subnetIds,omitempty"` SubnetIDs []string `json:"subnetIds,omitempty"`
ResourceLimits *ResourceLimits `json:"resourceLimits,omitempty"`
IAMInstanceProfile *IAMInstanceProfile `json:"iamInstanceProfile,omitempty"` IAMInstanceProfile *IAMInstanceProfile `json:"iamInstanceProfile,omitempty"`
AutoScale *AutoScale `json:"autoScale,omitempty"`
ElasticIPPool *ElasticIPPool `json:"elasticIpPool,omitempty"`
BlockDeviceMappings []*BlockDeviceMapping `json:"blockDeviceMappings,omitempty"`
Labels []*Label `json:"labels,omitempty"` Labels []*Label `json:"labels,omitempty"`
Taints []*Taint `json:"taints,omitempty"` Taints []*Taint `json:"taints,omitempty"`
AutoScale *AutoScale `json:"autoScale,omitempty"`
Tags []*Tag `json:"tags,omitempty"` Tags []*Tag `json:"tags,omitempty"`
// Read-only fields. // Read-only fields.
@ -49,6 +52,46 @@ type LaunchSpec struct {
nullFields []string nullFields []string
} }
type ResourceLimits struct {
MaxInstanceCount *int `json:"maxInstanceCount,omitempty"`
forceSendFields []string
nullFields []string
}
type BlockDeviceMapping struct {
DeviceName *string `json:"deviceName,omitempty"`
NoDevice *string `json:"noDevice,omitempty"`
VirtualName *string `json:"virtualName,omitempty"`
EBS *EBS `json:"ebs,omitempty"`
forceSendFields []string
nullFields []string
}
type EBS struct {
DeleteOnTermination *bool `json:"deleteOnTermination,omitempty"`
Encrypted *bool `json:"encrypted,omitempty"`
KMSKeyID *string `json:"kmsKeyId,omitempty"`
SnapshotID *string `json:"snapshotId,omitempty"`
VolumeType *string `json:"volumeType,omitempty"`
IOPS *int `json:"iops,omitempty"`
VolumeSize *int `json:"volumeSize,omitempty"`
DynamicVolumeSize *DynamicVolumeSize `json:"dynamicVolumeSize,omitempty"`
forceSendFields []string
nullFields []string
}
type DynamicVolumeSize struct {
BaseSize *int `json:"baseSize,omitempty"`
SizePerResourceUnit *int `json:"sizePerResourceUnit,omitempty"`
Resource *string `json:"resource,omitempty"`
forceSendFields []string
nullFields []string
}
type Label struct { type Label struct {
Key *string `json:"key,omitempty"` Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"` Value *string `json:"value,omitempty"`
@ -83,6 +126,21 @@ type AutoScaleHeadroom struct {
nullFields []string nullFields []string
} }
type ElasticIPPool struct {
TagSelector *TagSelector `json:"tagSelector,omitempty"`
forceSendFields []string
nullFields []string
}
type TagSelector struct {
Key *string `json:"tagKey,omitempty"`
Value *string `json:"tagValue,omitempty"`
forceSendFields []string
nullFields []string
}
type ListLaunchSpecsInput struct { type ListLaunchSpecsInput struct {
OceanID *string `json:"oceanId,omitempty"` OceanID *string `json:"oceanId,omitempty"`
} }
@ -374,6 +432,186 @@ func (o *LaunchSpec) SetAutoScale(v *AutoScale) *LaunchSpec {
return o return o
} }
func (o *LaunchSpec) SetElasticIPPool(v *ElasticIPPool) *LaunchSpec {
if o.ElasticIPPool = v; o.ElasticIPPool == nil {
o.nullFields = append(o.nullFields, "ElasticIPPool")
}
return o
}
func (o *LaunchSpec) SetBlockDeviceMappings(v []*BlockDeviceMapping) *LaunchSpec {
if o.BlockDeviceMappings = v; o.BlockDeviceMappings == nil {
o.nullFields = append(o.nullFields, "BlockDeviceMappings")
}
return o
}
func (o *LaunchSpec) SetTags(v []*Tag) *LaunchSpec {
if o.Tags = v; o.Tags == nil {
o.nullFields = append(o.nullFields, "Tags")
}
return o
}
func (o *LaunchSpec) SetResourceLimits(v *ResourceLimits) *LaunchSpec {
if o.ResourceLimits = v; o.ResourceLimits == nil {
o.nullFields = append(o.nullFields, "ResourceLimits")
}
return o
}
// endregion
// region BlockDeviceMappings
func (o BlockDeviceMapping) MarshalJSON() ([]byte, error) {
type noMethod BlockDeviceMapping
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *BlockDeviceMapping) SetDeviceName(v *string) *BlockDeviceMapping {
if o.DeviceName = v; o.DeviceName == nil {
o.nullFields = append(o.nullFields, "DeviceName")
}
return o
}
func (o *BlockDeviceMapping) SetNoDevice(v *string) *BlockDeviceMapping {
if o.NoDevice = v; o.NoDevice == nil {
o.nullFields = append(o.nullFields, "NoDevice")
}
return o
}
func (o *BlockDeviceMapping) SetVirtualName(v *string) *BlockDeviceMapping {
if o.VirtualName = v; o.VirtualName == nil {
o.nullFields = append(o.nullFields, "VirtualName")
}
return o
}
func (o *BlockDeviceMapping) SetEBS(v *EBS) *BlockDeviceMapping {
if o.EBS = v; o.EBS == nil {
o.nullFields = append(o.nullFields, "EBS")
}
return o
}
// endregion
// region EBS
func (o EBS) MarshalJSON() ([]byte, error) {
type noMethod EBS
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *EBS) SetEncrypted(v *bool) *EBS {
if o.Encrypted = v; o.Encrypted == nil {
o.nullFields = append(o.nullFields, "Encrypted")
}
return o
}
func (o *EBS) SetIOPS(v *int) *EBS {
if o.IOPS = v; o.IOPS == nil {
o.nullFields = append(o.nullFields, "IOPS")
}
return o
}
func (o *EBS) SetKMSKeyId(v *string) *EBS {
if o.KMSKeyID = v; o.KMSKeyID == nil {
o.nullFields = append(o.nullFields, "KMSKeyID")
}
return o
}
func (o *EBS) SetSnapshotId(v *string) *EBS {
if o.SnapshotID = v; o.SnapshotID == nil {
o.nullFields = append(o.nullFields, "SnapshotID")
}
return o
}
func (o *EBS) SetVolumeType(v *string) *EBS {
if o.VolumeType = v; o.VolumeType == nil {
o.nullFields = append(o.nullFields, "VolumeType")
}
return o
}
func (o *EBS) SetDeleteOnTermination(v *bool) *EBS {
if o.DeleteOnTermination = v; o.DeleteOnTermination == nil {
o.nullFields = append(o.nullFields, "DeleteOnTermination")
}
return o
}
func (o *EBS) SetVolumeSize(v *int) *EBS {
if o.VolumeSize = v; o.VolumeSize == nil {
o.nullFields = append(o.nullFields, "VolumeSize")
}
return o
}
func (o *EBS) SetDynamicVolumeSize(v *DynamicVolumeSize) *EBS {
if o.DynamicVolumeSize = v; o.DynamicVolumeSize == nil {
o.nullFields = append(o.nullFields, "DynamicVolumeSize")
}
return o
}
// end region
// region Dynamic Volume Size
func (o DynamicVolumeSize) MarshalJSON() ([]byte, error) {
type noMethod DynamicVolumeSize
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *DynamicVolumeSize) SetBaseSize(v *int) *DynamicVolumeSize {
if o.BaseSize = v; o.BaseSize == nil {
o.nullFields = append(o.nullFields, "BaseSize")
}
return o
}
func (o *DynamicVolumeSize) SetResource(v *string) *DynamicVolumeSize {
if o.Resource = v; o.Resource == nil {
o.nullFields = append(o.nullFields, "Resource")
}
return o
}
func (o *DynamicVolumeSize) SetSizePerResourceUnit(v *int) *DynamicVolumeSize {
if o.SizePerResourceUnit = v; o.SizePerResourceUnit == nil {
o.nullFields = append(o.nullFields, "SizePerResourceUnit")
}
return o
}
// end region
// region ResourceLimits
func (o ResourceLimits) MarshalJSON() ([]byte, error) {
type noMethod ResourceLimits
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *ResourceLimits) SetMaxInstanceCount(v *int) *ResourceLimits {
if o.MaxInstanceCount = v; o.MaxInstanceCount == nil {
o.nullFields = append(o.nullFields, "MaxInstanceCount")
}
return o
}
// endregion // endregion
// region Label // region Label
@ -484,9 +722,43 @@ func (o *AutoScaleHeadroom) SetNumOfUnits(v *int) *AutoScaleHeadroom {
return o return o
} }
func (o *LaunchSpec) SetTags(v []*Tag) *LaunchSpec { // endregion
if o.Tags = v; o.Tags == nil {
o.nullFields = append(o.nullFields, "Tags") // region ElasticIPPool
func (o ElasticIPPool) MarshalJSON() ([]byte, error) {
type noMethod ElasticIPPool
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *ElasticIPPool) SetTagSelector(v *TagSelector) *ElasticIPPool {
if o.TagSelector = v; o.TagSelector == nil {
o.nullFields = append(o.nullFields, "TagSelector")
}
return o
}
// endregion
// region TagSelector
func (o TagSelector) MarshalJSON() ([]byte, error) {
type noMethod TagSelector
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *TagSelector) SetTagKey(v *string) *TagSelector {
if o.Key = v; o.Key == nil {
o.nullFields = append(o.nullFields, "Key")
}
return o
}
func (o *TagSelector) SetTagValue(v *string) *TagSelector {
if o.Value = v; o.Value == nil {
o.nullFields = append(o.nullFields, "Value")
} }
return o return o
} }

View File

@ -174,7 +174,7 @@ type AutoScalerResourceLimits struct {
type AutoScalerDown struct { type AutoScalerDown struct {
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"` EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
MaxScaleDownPercentage *int `json:"maxScaleDownPercentage,omitempty"` MaxScaleDownPercentage *float64 `json:"maxScaleDownPercentage,omitempty"`
forceSendFields []string forceSendFields []string
nullFields []string nullFields []string
@ -216,19 +216,30 @@ type DeleteClusterInput struct {
type DeleteClusterOutput struct{} type DeleteClusterOutput struct{}
// Deprecated: Use CreateRollInput instead.
type RollClusterInput struct { type RollClusterInput struct {
Roll *Roll `json:"roll,omitempty"` Roll *Roll `json:"roll,omitempty"`
} }
// Deprecated: Use CreateRollOutput instead.
type RollClusterOutput struct { type RollClusterOutput struct {
RollClusterStatus *RollClusterStatus `json:"clusterDeploymentStatus,omitempty"` RollClusterStatus *RollClusterStatus `json:"clusterDeploymentStatus,omitempty"`
} }
// Deprecated: Use RollSpec instead.
type Roll struct { type Roll struct {
ClusterID *string `json:"clusterId,omitempty"` ClusterID *string `json:"clusterId,omitempty"`
Comment *string `json:"comment,omitempty"`
BatchSizePercentage *int `json:"batchSizePercentage,omitempty"` BatchSizePercentage *int `json:"batchSizePercentage,omitempty"`
DisableLaunchSpecAutoscaling *bool `json:"disableLaunchSpecAutoscaling,omitempty"`
LaunchSpecIDs []string `json:"launchSpecIds,omitempty"`
InstanceIDs []string `json:"instanceIds,omitempty"`
forceSendFields []string
nullFields []string
} }
// Deprecated: Use RollStatus instead.
type RollClusterStatus struct { type RollClusterStatus struct {
OceanID *string `json:"oceanId,omitempty"` OceanID *string `json:"oceanId,omitempty"`
RollID *string `json:"id,omitempty"` RollID *string `json:"id,omitempty"`
@ -240,11 +251,71 @@ type RollClusterStatus struct {
UpdatedAt *string `json:"updatedAt,omitempty"` UpdatedAt *string `json:"updatedAt,omitempty"`
} }
type RollSpec struct {
ID *string `json:"id,omitempty"`
ClusterID *string `json:"clusterId,omitempty"`
Comment *string `json:"comment,omitempty"`
BatchSizePercentage *int `json:"batchSizePercentage,omitempty"`
DisableLaunchSpecAutoScaling *bool `json:"disableLaunchSpecAutoScaling,omitempty"`
LaunchSpecIDs []string `json:"launchSpecIds,omitempty"`
InstanceIDs []string `json:"instanceIds,omitempty"`
forceSendFields []string
nullFields []string
}
type RollStatus struct {
ID *string `json:"id,omitempty"`
ClusterID *string `json:"oceanId,omitempty"`
Comment *string `json:"comment,omitempty"`
Status *string `json:"status,omitempty"`
Progress *Progress `json:"progress,omitempty"`
CurrentBatch *int `json:"currentBatch,omitempty"`
NumOfBatches *int `json:"numOfBatches,omitempty"`
LaunchSpecIDs []string `json:"launchSpecIds,omitempty"`
InstanceIDs []string `json:"instanceIds,omitempty"`
CreatedAt *string `json:"createdAt,omitempty"`
UpdatedAt *string `json:"updatedAt,omitempty"`
}
type Progress struct { type Progress struct {
Unit *string `json:"unit,omitempty"` Unit *string `json:"unit,omitempty"`
Value *int `json:"value,omitempty"` Value *int `json:"value,omitempty"`
} }
type ListRollsInput struct {
ClusterID *string `json:"clusterId,omitempty"`
}
type ListRollsOutput struct {
Rolls []*RollStatus `json:"rolls,omitempty"`
}
type CreateRollInput struct {
Roll *RollSpec `json:"roll,omitempty"`
}
type CreateRollOutput struct {
Roll *RollStatus `json:"roll,omitempty"`
}
type ReadRollInput struct {
RollID *string `json:"rollId,omitempty"`
ClusterID *string `json:"clusterId,omitempty"`
}
type ReadRollOutput struct {
Roll *RollStatus `json:"roll,omitempty"`
}
type UpdateRollInput struct {
Roll *RollSpec `json:"roll,omitempty"`
}
type UpdateRollOutput struct {
Roll *RollStatus `json:"roll,omitempty"`
}
func clusterFromJSON(in []byte) (*Cluster, error) { func clusterFromJSON(in []byte) (*Cluster, error) {
b := new(Cluster) b := new(Cluster)
if err := json.Unmarshal(in, b); err != nil { if err := json.Unmarshal(in, b); err != nil {
@ -280,7 +351,7 @@ func clustersFromHttpResponse(resp *http.Response) ([]*Cluster, error) {
return clustersFromJSON(body) return clustersFromJSON(body)
} }
func rollStatusFromJSON(in []byte) (*RollClusterStatus, error) { func rollClusterStatusFromJSON(in []byte) (*RollClusterStatus, error) {
b := new(RollClusterStatus) b := new(RollClusterStatus)
if err := json.Unmarshal(in, b); err != nil { if err := json.Unmarshal(in, b); err != nil {
return nil, err return nil, err
@ -288,7 +359,7 @@ func rollStatusFromJSON(in []byte) (*RollClusterStatus, error) {
return b, nil return b, nil
} }
func rollStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) { func rollClusterStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
var rw client.Response var rw client.Response
if err := json.Unmarshal(in, &rw); err != nil { if err := json.Unmarshal(in, &rw); err != nil {
return nil, err return nil, err
@ -297,6 +368,41 @@ func rollStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
if len(out) == 0 { if len(out) == 0 {
return out, nil return out, nil
} }
for i, rb := range rw.Response.Items {
b, err := rollClusterStatusFromJSON(rb)
if err != nil {
return nil, err
}
out[i] = b
}
return out, nil
}
func rollClusterStatusesFromHttpResponse(resp *http.Response) ([]*RollClusterStatus, error) {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return rollClusterStatusesFromJSON(body)
}
func rollStatusFromJSON(in []byte) (*RollStatus, error) {
b := new(RollStatus)
if err := json.Unmarshal(in, b); err != nil {
return nil, err
}
return b, nil
}
func rollStatusesFromJSON(in []byte) ([]*RollStatus, error) {
var rw client.Response
if err := json.Unmarshal(in, &rw); err != nil {
return nil, err
}
out := make([]*RollStatus, len(rw.Response.Items))
if len(out) == 0 {
return out, nil
}
for i, rb := range rw.Response.Items { for i, rb := range rw.Response.Items {
b, err := rollStatusFromJSON(rb) b, err := rollStatusFromJSON(rb)
if err != nil { if err != nil {
@ -307,7 +413,7 @@ func rollStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
return out, nil return out, nil
} }
func rollStatusesFromHttpResponse(resp *http.Response) ([]*RollClusterStatus, error) { func rollStatusesFromHttpResponse(resp *http.Response) ([]*RollStatus, error) {
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
@ -434,6 +540,130 @@ func (s *ServiceOp) DeleteCluster(ctx context.Context, input *DeleteClusterInput
return &DeleteClusterOutput{}, nil return &DeleteClusterOutput{}, nil
} }
func (s *ServiceOp) ListRolls(ctx context.Context, input *ListRollsInput) (*ListRollsOutput, error) {
path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll", uritemplates.Values{
"clusterId": spotinst.StringValue(input.ClusterID),
})
if err != nil {
return nil, err
}
r := client.NewRequest(http.MethodGet, path)
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
v, err := rollStatusesFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(ListRollsOutput)
if len(v) > 0 {
output.Rolls = v
}
return output, nil
}
func (s *ServiceOp) CreateRoll(ctx context.Context, input *CreateRollInput) (*CreateRollOutput, error) {
path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll", uritemplates.Values{
"clusterId": spotinst.StringValue(input.Roll.ClusterID),
})
if err != nil {
return nil, err
}
// We do not need the ID anymore so let's drop it.
input.Roll.ClusterID = nil
r := client.NewRequest(http.MethodPost, path)
r.Obj = input
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
v, err := rollStatusesFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(CreateRollOutput)
if len(v) > 0 {
output.Roll = v[0]
}
return output, nil
}
func (s *ServiceOp) ReadRoll(ctx context.Context, input *ReadRollInput) (*ReadRollOutput, error) {
path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll/{rollId}", uritemplates.Values{
"clusterId": spotinst.StringValue(input.ClusterID),
"rollId": spotinst.StringValue(input.RollID),
})
if err != nil {
return nil, err
}
r := client.NewRequest(http.MethodGet, path)
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
v, err := rollStatusesFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(ReadRollOutput)
if len(v) > 0 {
output.Roll = v[0]
}
return output, nil
}
func (s *ServiceOp) UpdateRoll(ctx context.Context, input *UpdateRollInput) (*UpdateRollOutput, error) {
path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll/{rollId}", uritemplates.Values{
"clusterId": spotinst.StringValue(input.Roll.ClusterID),
})
if err != nil {
return nil, err
}
// We do not need the ID anymore so let's drop it.
input.Roll.ClusterID = nil
r := client.NewRequest(http.MethodPut, path)
r.Obj = input
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
v, err := rollStatusesFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(UpdateRollOutput)
if len(v) > 0 {
output.Roll = v[0]
}
return output, nil
}
// Deprecated: Use CreateRoll instead.
func (s *ServiceOp) Roll(ctx context.Context, input *RollClusterInput) (*RollClusterOutput, error) { func (s *ServiceOp) Roll(ctx context.Context, input *RollClusterInput) (*RollClusterOutput, error) {
path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll", uritemplates.Values{ path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll", uritemplates.Values{
"clusterId": spotinst.StringValue(input.Roll.ClusterID), "clusterId": spotinst.StringValue(input.Roll.ClusterID),
@ -454,7 +684,7 @@ func (s *ServiceOp) Roll(ctx context.Context, input *RollClusterInput) (*RollClu
} }
defer resp.Body.Close() defer resp.Body.Close()
rs, err := rollStatusesFromHttpResponse(resp) rs, err := rollClusterStatusesFromHttpResponse(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1022,7 +1252,7 @@ func (o *AutoScalerDown) SetEvaluationPeriods(v *int) *AutoScalerDown {
return o return o
} }
func (o *AutoScalerDown) SetMaxScaleDownPercentage(v *int) *AutoScalerDown { func (o *AutoScalerDown) SetMaxScaleDownPercentage(v *float64) *AutoScalerDown {
if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil { if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil {
o.nullFields = append(o.nullFields, "MaxScaleDownPercentage") o.nullFields = append(o.nullFields, "MaxScaleDownPercentage")
} }
@ -1030,3 +1260,48 @@ func (o *AutoScalerDown) SetMaxScaleDownPercentage(v *int) *AutoScalerDown {
} }
// endregion // endregion
// region Roll
func (o Roll) MarshalJSON() ([]byte, error) {
type noMethod Roll
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Roll) SetComment(v *string) *Roll {
if o.Comment = v; o.Comment == nil {
o.nullFields = append(o.nullFields, "Comment")
}
return o
}
func (o *Roll) SetBatchSizePercentage(v *int) *Roll {
if o.BatchSizePercentage = v; o.BatchSizePercentage == nil {
o.nullFields = append(o.nullFields, "BatchSizePercentage")
}
return o
}
func (o *Roll) SetDisableLaunchSpecAutoscaling(v *bool) *Roll {
if o.DisableLaunchSpecAutoscaling = v; o.DisableLaunchSpecAutoscaling == nil {
o.nullFields = append(o.nullFields, "DisableLaunchSpecAutoscaling")
}
return o
}
func (o *Roll) SetLaunchSpecIDs(v []string) *Roll {
if o.LaunchSpecIDs = v; o.LaunchSpecIDs == nil {
o.nullFields = append(o.nullFields, "LaunchSpecIDs")
}
return o
}
func (o *Roll) SetInstanceIDs(v []string) *Roll {
if o.InstanceIDs = v; o.InstanceIDs == nil {
o.nullFields = append(o.nullFields, "InstanceIDs")
}
return o
}
// endregion

View File

@ -156,7 +156,7 @@ type ECSAutoScalerResourceLimits struct {
} }
type ECSAutoScalerDown struct { type ECSAutoScalerDown struct {
MaxScaleDownPercentage *int `json:"maxScaleDownPercentage,omitempty"` MaxScaleDownPercentage *float64 `json:"maxScaleDownPercentage,omitempty"`
forceSendFields []string forceSendFields []string
nullFields []string nullFields []string
@ -208,7 +208,13 @@ type ECSRollClusterOutput struct {
type ECSRoll struct { type ECSRoll struct {
ClusterID *string `json:"clusterId,omitempty"` ClusterID *string `json:"clusterId,omitempty"`
Comment *string `json:"comment,omitempty"`
BatchSizePercentage *int `json:"batchSizePercentage,omitempty"` BatchSizePercentage *int `json:"batchSizePercentage,omitempty"`
LaunchSpecIDs []string `json:"launchSpecIds,omitempty"`
InstanceIDs []string `json:"instanceIds,omitempty"`
forceSendFields []string
nullFields []string
} }
type ECSRollClusterStatus struct { type ECSRollClusterStatus struct {
@ -912,7 +918,7 @@ func (o ECSAutoScalerDown) MarshalJSON() ([]byte, error) {
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields) return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
} }
func (o *ECSAutoScalerDown) SetMaxScaleDownPercentage(v *int) *ECSAutoScalerDown { func (o *ECSAutoScalerDown) SetMaxScaleDownPercentage(v *float64) *ECSAutoScalerDown {
if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil { if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil {
o.nullFields = append(o.nullFields, "MaxScaleDownPercentage") o.nullFields = append(o.nullFields, "MaxScaleDownPercentage")
} }
@ -920,3 +926,41 @@ func (o *ECSAutoScalerDown) SetMaxScaleDownPercentage(v *int) *ECSAutoScalerDown
} }
// endregion // endregion
// region ECSRoll
func (o ECSRoll) MarshalJSON() ([]byte, error) {
type noMethod ECSRoll
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *ECSRoll) SetComment(v *string) *ECSRoll {
if o.Comment = v; o.Comment == nil {
o.nullFields = append(o.nullFields, "Comment")
}
return o
}
func (o *ECSRoll) SetBatchSizePercentage(v *int) *ECSRoll {
if o.BatchSizePercentage = v; o.BatchSizePercentage == nil {
o.nullFields = append(o.nullFields, "BatchSizePercentage")
}
return o
}
func (o *ECSRoll) SetLaunchSpecIDs(v []string) *ECSRoll {
if o.LaunchSpecIDs = v; o.LaunchSpecIDs == nil {
o.nullFields = append(o.nullFields, "LaunchSpecIDs")
}
return o
}
func (o *ECSRoll) SetInstanceIDs(v []string) *ECSRoll {
if o.InstanceIDs = v; o.InstanceIDs == nil {
o.nullFields = append(o.nullFields, "InstanceIDs")
}
return o
}
// endregion

View File

@ -12,6 +12,13 @@ import (
// of the Spotinst API. See this package's package overview docs for details on // of the Spotinst API. See this package's package overview docs for details on
// the service. // the service.
type Service interface { type Service interface {
serviceKubernetes
serviceECS
ListResourceSuggestions(context.Context, *ListResourceSuggestionsInput) (*ListResourceSuggestionsOutput, error)
}
type serviceKubernetes interface {
ListClusters(context.Context, *ListClustersInput) (*ListClustersOutput, error) ListClusters(context.Context, *ListClustersInput) (*ListClustersOutput, error)
CreateCluster(context.Context, *CreateClusterInput) (*CreateClusterOutput, error) CreateCluster(context.Context, *CreateClusterInput) (*CreateClusterOutput, error)
ReadCluster(context.Context, *ReadClusterInput) (*ReadClusterOutput, error) ReadCluster(context.Context, *ReadClusterInput) (*ReadClusterOutput, error)
@ -27,7 +34,17 @@ type Service interface {
ListClusterInstances(context.Context, *ListClusterInstancesInput) (*ListClusterInstancesOutput, error) ListClusterInstances(context.Context, *ListClusterInstancesInput) (*ListClusterInstancesOutput, error)
DetachClusterInstances(context.Context, *DetachClusterInstancesInput) (*DetachClusterInstancesOutput, error) DetachClusterInstances(context.Context, *DetachClusterInstancesInput) (*DetachClusterInstancesOutput, error)
ListRolls(context.Context, *ListRollsInput) (*ListRollsOutput, error)
CreateRoll(context.Context, *CreateRollInput) (*CreateRollOutput, error)
ReadRoll(context.Context, *ReadRollInput) (*ReadRollOutput, error)
UpdateRoll(context.Context, *UpdateRollInput) (*UpdateRollOutput, error)
// Deprecated: Roll is obsolete, exists for backward compatibility only,
// and should not be used. Please use CreateRoll instead.
Roll(context.Context, *RollClusterInput) (*RollClusterOutput, error) Roll(context.Context, *RollClusterInput) (*RollClusterOutput, error)
}
type serviceECS interface {
ListECSClusters(context.Context, *ListECSClustersInput) (*ListECSClustersOutput, error) ListECSClusters(context.Context, *ListECSClustersInput) (*ListECSClustersOutput, error)
CreateECSCluster(context.Context, *CreateECSClusterInput) (*CreateECSClusterOutput, error) CreateECSCluster(context.Context, *CreateECSClusterInput) (*CreateECSClusterOutput, error)
ReadECSCluster(context.Context, *ReadECSClusterInput) (*ReadECSClusterOutput, error) ReadECSCluster(context.Context, *ReadECSClusterInput) (*ReadECSClusterOutput, error)
@ -41,8 +58,6 @@ type Service interface {
DeleteECSLaunchSpec(context.Context, *DeleteECSLaunchSpecInput) (*DeleteECSLaunchSpecOutput, error) DeleteECSLaunchSpec(context.Context, *DeleteECSLaunchSpecInput) (*DeleteECSLaunchSpecOutput, error)
RollECS(context.Context, *ECSRollClusterInput) (*ECSRollClusterOutput, error) RollECS(context.Context, *ECSRollClusterInput) (*ECSRollClusterOutput, error)
ListResourceSuggestions(context.Context, *ListResourceSuggestionsInput) (*ListResourceSuggestionsOutput, error)
} }
type ServiceOp struct { type ServiceOp struct {

View File

@ -154,11 +154,11 @@ func (c *Config) WithLogger(logger log.Logger) *Config {
// Merge merges the passed in configs into the existing config object. // Merge merges the passed in configs into the existing config object.
func (c *Config) Merge(cfgs ...*Config) { func (c *Config) Merge(cfgs ...*Config) {
for _, cfg := range cfgs { for _, cfg := range cfgs {
mergeConfig(c, cfg) mergeConfigs(c, cfg)
} }
} }
func mergeConfig(c1, c2 *Config) { func mergeConfigs(c1, c2 *Config) {
if c2 == nil { if c2 == nil {
return return
} }

View File

@ -13,5 +13,8 @@ go_library(
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials", importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/credentials",
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/credentials", importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/credentials",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = ["//vendor/github.com/go-ini/ini:go_default_library"], deps = [
"//vendor/github.com/go-ini/ini:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/featureflag:go_default_library",
],
) )

View File

@ -1,9 +1,13 @@
package credentials package credentials
import ( import (
"errors"
"sync" "sync"
) )
// ErrNoValidTokenFound is returned when there is no valid token.
var ErrNoValidTokenFound = errors.New("spotinst: no valid token found")
// A Credentials provides synchronous safe retrieval of Spotinst credentials. // A Credentials provides synchronous safe retrieval of Spotinst credentials.
// Credentials will cache the credentials value. // Credentials will cache the credentials value.
// //
@ -43,6 +47,9 @@ func (c *Credentials) Get() (Value, error) {
if err != nil { if err != nil {
return Value{}, err return Value{}, err
} }
if creds.Token == "" {
return Value{ProviderName: creds.ProviderName}, ErrNoValidTokenFound
}
c.creds = creds c.creds = creds
c.forceRefresh = false c.forceRefresh = false
} }

View File

@ -5,13 +5,13 @@ import "fmt"
// A Value is the Spotinst credentials value for individual credential fields. // A Value is the Spotinst credentials value for individual credential fields.
type Value struct { type Value struct {
// Spotinst API token. // Spotinst API token.
Token string `json:"token"` Token string `ini:"token" json:"token"`
// Spotinst account ID. // Spotinst account ID.
Account string `json:"account"` Account string `ini:"account" json:"account"`
// Provider used to get credentials. // Provider used to get credentials.
ProviderName string `json:"-"` ProviderName string `ini:"-" json:"-"`
} }
// A Provider is the interface for any component which will provide credentials // A Provider is the interface for any component which will provide credentials
@ -26,3 +26,19 @@ type Provider interface {
// returned if the value were not obtainable, or empty. // returned if the value were not obtainable, or empty.
Retrieve() (Value, error) Retrieve() (Value, error)
} }
// IsEmpty if all fields of a Value are empty.
func (v *Value) IsEmpty() bool { return v.Token == "" && v.Account == "" }
// IsComplete if all fields of a Value are set.
func (v *Value) IsComplete() bool { return v.Token != "" && v.Account != "" }
// Merge merges the passed in Value into the existing Value object.
func (v *Value) Merge(v2 Value) {
if v.Token == "" {
v.Token = v2.Token
}
if v.Account == "" {
v.Account = v2.Account
}
}

View File

@ -3,14 +3,16 @@ package credentials
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/spotinst/spotinst-sdk-go/spotinst/featureflag"
) )
// ErrNoValidProvidersFoundInChain Is returned when there are no valid credentials // ErrNoValidProvidersFoundInChain is returned when there are no valid credentials
// providers in the ChainProvider. // providers in the ChainProvider.
var ErrNoValidProvidersFoundInChain = errors.New("spotinst: no valid credentials providers in chain") var ErrNoValidProvidersFoundInChain = errors.New("spotinst: no valid " +
"credentials providers in chain")
// A ChainProvider will search for a provider which returns credentials and cache // A ChainProvider will search for a provider which returns credentials.
// that provider until Retrieve is called again.
// //
// The ChainProvider provides a way of chaining multiple providers together which // 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. // will pick the first available using priority order of the Providers in the list.
@ -34,7 +36,6 @@ var ErrNoValidProvidersFoundInChain = errors.New("spotinst: no valid credentials
// ) // )
type ChainProvider struct { type ChainProvider struct {
Providers []Provider Providers []Provider
active Provider
} }
// NewChainCredentials returns a pointer to a new Credentials object // NewChainCredentials returns a pointer to a new Credentials object
@ -46,27 +47,41 @@ func NewChainCredentials(providers ...Provider) *Credentials {
} }
// Retrieve returns the credentials value or error if no provider returned // Retrieve returns the credentials value or error if no provider returned
// without error. If a provider is found it will be cached. // without error.
func (c *ChainProvider) Retrieve() (Value, error) { func (c *ChainProvider) Retrieve() (Value, error) {
var value Value
var errs errorList var errs errorList
for _, p := range c.Providers { for _, p := range c.Providers {
value, err := p.Retrieve() v, err := p.Retrieve()
if err == nil { if err == nil {
c.active = p if featureflag.MergeCredentialsChain.Enabled() {
value.Merge(v)
if value.IsComplete() {
return value, nil return value, nil
} }
} else {
value = v
break
}
} else {
errs = append(errs, err) errs = append(errs, err)
} }
c.active = nil }
if value.Token == "" {
err := ErrNoValidProvidersFoundInChain err := ErrNoValidProvidersFoundInChain
if len(errs) > 0 { if len(errs) > 0 {
err = errs err = errs
} }
return Value{}, err return Value{ProviderName: c.String()}, err
} }
return value, nil
}
// String returns the string representation of the provider.
func (c *ChainProvider) String() string { func (c *ChainProvider) String() string {
var out string var out string
for i, provider := range c.Providers { for i, provider := range c.Providers {

View File

@ -6,7 +6,7 @@ import (
) )
const ( const (
// EnvCredentialsProviderName provides a name of Env provider. // EnvCredentialsProviderName specifies the name of the Env provider.
EnvCredentialsProviderName = "EnvCredentialsProvider" EnvCredentialsProviderName = "EnvCredentialsProvider"
// EnvCredentialsVarToken specifies the name of the environment variable // EnvCredentialsVarToken specifies the name of the environment variable
@ -18,9 +18,10 @@ const (
EnvCredentialsVarAccount = "SPOTINST_ACCOUNT" EnvCredentialsVarAccount = "SPOTINST_ACCOUNT"
) )
// ErrEnvCredentialsTokenNotFound is returned when the Spotinst Token can't be // ErrEnvCredentialsNotFound is returned when no credentials can be found in the
// found in the process's environment. // process's environment.
var ErrEnvCredentialsTokenNotFound = fmt.Errorf("spotinst: %s not found in environment", EnvCredentialsVarToken) var ErrEnvCredentialsNotFound = fmt.Errorf("spotinst: %s and %s not found "+
"in environment", EnvCredentialsVarToken, EnvCredentialsVarAccount)
// A EnvProvider retrieves credentials from the environment variables of the // A EnvProvider retrieves credentials from the environment variables of the
// running process. // running process.
@ -38,21 +39,18 @@ func NewEnvCredentials() *Credentials {
// Retrieve retrieves the keys from the environment. // Retrieve retrieves the keys from the environment.
func (e *EnvProvider) Retrieve() (Value, error) { func (e *EnvProvider) Retrieve() (Value, error) {
token := os.Getenv(EnvCredentialsVarToken)
if token == "" {
return Value{ProviderName: EnvCredentialsProviderName},
ErrEnvCredentialsTokenNotFound
}
value := Value{ value := Value{
Token: token, Token: os.Getenv(EnvCredentialsVarToken),
Account: os.Getenv(EnvCredentialsVarAccount), Account: os.Getenv(EnvCredentialsVarAccount),
ProviderName: EnvCredentialsProviderName, ProviderName: EnvCredentialsProviderName,
} }
if value.IsEmpty() {
return value, ErrEnvCredentialsNotFound
}
return value, nil return value, nil
} }
func (e *EnvProvider) String() string { // String returns the string representation of the provider.
return EnvCredentialsProviderName func (e *EnvProvider) String() string { return EnvCredentialsProviderName }
}

View File

@ -12,7 +12,7 @@ import (
) )
const ( const (
// FileCredentialsProviderName provides a name of File provider. // FileCredentialsProviderName specifies the name of the File provider.
FileCredentialsProviderName = "FileCredentialsProvider" FileCredentialsProviderName = "FileCredentialsProvider"
// FileCredentialsEnvVarFile specifies the name of the environment variable // FileCredentialsEnvVarFile specifies the name of the environment variable
@ -25,13 +25,13 @@ const (
) )
var ( var (
// ErrFileCredentialsLoadFailed is emitted when the provider is unable to load // ErrFileCredentialsLoadFailed is returned when the provider is unable to load
// credentials from the credentials file. // credentials from the credentials file.
ErrFileCredentialsLoadFailed = errors.New("spotinst: failed to load credentials file") ErrFileCredentialsLoadFailed = errors.New("spotinst: failed to load credentials file")
// ErrFileCredentialsTokenNotFound is emitted when the loaded credentials // ErrFileCredentialsNotFound is returned when the loaded credentials
// do not contain a valid token. // are empty.
ErrFileCredentialsTokenNotFound = errors.New("spotinst: credentials do not contain token") ErrFileCredentialsNotFound = errors.New("spotinst: credentials file or profile is empty")
) )
// DefaultProfile returns the SDK's default profile name to use when loading // DefaultProfile returns the SDK's default profile name to use when loading
@ -93,11 +93,10 @@ func (p *FileProvider) Retrieve() (Value, error) {
return value, nil return value, nil
} }
func (p *FileProvider) String() string { // String returns the string representation of the provider.
return FileCredentialsProviderName func (p *FileProvider) String() string { return FileCredentialsProviderName }
}
// profile returns the profile to use to read Spotinst credentials. // profile returns the profile to use to read the user credentials.
func (p *FileProvider) profile() string { func (p *FileProvider) profile() string {
if p.Profile == "" { if p.Profile == "" {
if p.Profile = os.Getenv(FileCredentialsEnvVarProfile); p.Profile != "" { if p.Profile = os.Getenv(FileCredentialsEnvVarProfile); p.Profile != "" {
@ -110,7 +109,7 @@ func (p *FileProvider) profile() string {
return p.Profile return p.Profile
} }
// filename returns the filename to use to read Spotinst credentials. // filename returns the filename to use to read the user credentials.
func (p *FileProvider) filename() string { func (p *FileProvider) filename() string {
if p.Filename == "" { if p.Filename == "" {
if p.Filename = os.Getenv(FileCredentialsEnvVarFile); p.Filename != "" { if p.Filename = os.Getenv(FileCredentialsEnvVarFile); p.Filename != "" {
@ -136,8 +135,8 @@ func (p *FileProvider) loadCredentials(profile, filename string) (Value, error)
} }
} }
if len(value.Token) == 0 { if value.IsEmpty() {
return value, ErrFileCredentialsTokenNotFound return value, ErrFileCredentialsNotFound
} }
return value, nil return value, nil
@ -151,13 +150,33 @@ func (p *FileProvider) loadCredentialsINI(profile, filename string) (Value, erro
return value, err return value, err
} }
value, err = getCredentialsFromINIProfile(profile, config)
if err != nil {
return value, err
}
// Try to complete missing fields with default profile.
if profile != DefaultProfile() && !value.IsComplete() {
defaultValue, err := getCredentialsFromINIProfile(DefaultProfile(), config)
if err == nil {
value.Merge(defaultValue)
}
}
return value, nil
}
func getCredentialsFromINIProfile(profile string, config *ini.File) (Value, error) {
var value Value
section, err := config.GetSection(profile) section, err := config.GetSection(profile)
if err != nil { if err != nil {
return value, err return value, err
} }
value.Token = section.Key("token").String() if err := section.StrictMapTo(&value); err != nil {
value.Account = section.Key("account").String() return value, err
}
return value, nil return value, nil
} }

View File

@ -4,10 +4,10 @@ import (
"errors" "errors"
) )
// StaticCredentialsProviderName provides a name of Static provider. // StaticCredentialsProviderName specifies the name of the Static provider.
const StaticCredentialsProviderName = "StaticCredentialsProvider" const StaticCredentialsProviderName = "StaticCredentialsProvider"
// ErrStaticCredentialsEmpty is emitted when static credentials are empty. // ErrStaticCredentialsEmpty is returned when static credentials are empty.
var ErrStaticCredentialsEmpty = errors.New("spotinst: static credentials are empty") var ErrStaticCredentialsEmpty = errors.New("spotinst: static credentials are empty")
// A StaticProvider is a set of credentials which are set programmatically. // A StaticProvider is a set of credentials which are set programmatically.
@ -19,6 +19,7 @@ type StaticProvider struct {
// a static credentials value provider. // a static credentials value provider.
func NewStaticCredentials(token, account string) *Credentials { func NewStaticCredentials(token, account string) *Credentials {
return NewCredentials(&StaticProvider{Value: Value{ return NewCredentials(&StaticProvider{Value: Value{
ProviderName: StaticCredentialsProviderName,
Token: token, Token: token,
Account: account, Account: account,
}}) }})
@ -26,18 +27,12 @@ func NewStaticCredentials(token, account string) *Credentials {
// Retrieve returns the credentials or error if the credentials are invalid. // Retrieve returns the credentials or error if the credentials are invalid.
func (s *StaticProvider) Retrieve() (Value, error) { func (s *StaticProvider) Retrieve() (Value, error) {
if s.Token == "" { if s.IsEmpty() {
return Value{ProviderName: StaticCredentialsProviderName}, return s.Value, ErrStaticCredentialsEmpty
ErrStaticCredentialsEmpty
}
if len(s.Value.ProviderName) == 0 {
s.Value.ProviderName = StaticCredentialsProviderName
} }
return s.Value, nil return s.Value, nil
} }
func (s *StaticProvider) String() string { // String returns the string representation of the provider.
return StaticCredentialsProviderName func (s *StaticProvider) String() string { return StaticCredentialsProviderName }
}

View File

@ -0,0 +1,12 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"featureflag.go",
"features.go",
],
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/spotinst/featureflag",
importpath = "github.com/spotinst/spotinst-sdk-go/spotinst/featureflag",
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,119 @@
package featureflag
import (
"fmt"
"strconv"
"strings"
"sync"
)
// All registered feature flags.
var (
flagsMutex sync.Mutex
flags = make(map[string]FeatureFlag)
)
// FeatureFlag indicates whether a given feature is enabled or not.
type FeatureFlag interface {
fmt.Stringer
// Name returns the name of the feature flag.
Name() string
// Enabled returns true if the feature is enabled.
Enabled() bool
}
// featureFlag represents a feature being gated.
type featureFlag struct {
name string
enabled bool
}
// New returns a new feature flag.
func New(name string, enabled bool) FeatureFlag {
flagsMutex.Lock()
defer flagsMutex.Unlock()
ff, ok := flags[name]
if !ok {
ff = &featureFlag{name: name}
flags[name] = ff
}
ff.(*featureFlag).enabled = enabled
return ff
}
// Name returns the name of the feature flag.
func (f *featureFlag) Name() string { return f.name }
// Enabled returns true if the feature is enabled.
func (f *featureFlag) Enabled() bool { return f.enabled }
// String returns the string representation of the feature flag.
func (f *featureFlag) String() string { return fmt.Sprintf("%s=%t", f.name, f.enabled) }
// Set parses and stores features from a string like "feature1=true,feature2=false".
func Set(features string) {
for _, s := range strings.Split(strings.TrimSpace(features), ",") {
if len(s) == 0 {
continue
}
segments := strings.SplitN(s, "=", 2)
name := strings.TrimSpace(segments[0])
enabled := true
if len(segments) > 1 {
value := strings.TrimSpace(segments[1])
enabled, _ = strconv.ParseBool(value) // ignore errors and fallback to `false`
}
New(name, enabled)
}
}
// Get returns a specific feature flag by name.
func Get(name string) FeatureFlag {
flagsMutex.Lock()
defer flagsMutex.Unlock()
f, ok := flags[name]
if !ok {
f = new(featureFlag)
}
return &featureFlag{
name: name,
enabled: f.Enabled(),
}
}
// All returns a list of all known feature flags.
func All() FeatureFlags {
flagsMutex.Lock()
defer flagsMutex.Unlock()
features := make(FeatureFlags, 0, len(flags))
for name, flag := range flags {
features = append(features, &featureFlag{
name: name,
enabled: flag.Enabled(),
})
}
return features
}
// FeatureFlags defines a list of feature flags.
type FeatureFlags []FeatureFlag
// String returns the string representation of a list of feature flags.
func (f FeatureFlags) String() string {
features := make([]string, len(f))
for i, ff := range f {
features[i] = ff.String()
}
return strings.Join(features, ",")
}

View File

@ -0,0 +1,24 @@
package featureflag
import "os"
// Default features.
var (
// Toggle the usage of merging credentials in chain provider.
//
// This feature allows users to configure their credentials using multiple
// providers. For example, a token can be statically configured using a file,
// while the account can be dynamically configured via environment variables.
MergeCredentialsChain = New("MergeCredentialsChain", false)
)
// EnvVar is the name of the environment variable to read feature flags from.
// The value should be a comma-separated list of K=V flags, while V is optional.
const EnvVar = "SPOTINST_FEATURE_FLAGS"
// setFromEnv reads an environment variable and sets features from its value.
func setFromEnv() { Set(os.Getenv(EnvVar)) }
func init() {
setFromEnv()
}

View File

@ -1,7 +1,7 @@
package spotinst package spotinst
// SDKVersion is the current version of the SDK. // SDKVersion is the current version of the SDK.
const SDKVersion = "1.49.0" const SDKVersion = "1.56.0"
// SDKName is the name of the SDK. // SDKName is the name of the SDK.
const SDKName = "spotinst-sdk-go" const SDKName = "spotinst-sdk-go"

3
vendor/modules.txt vendored
View File

@ -505,7 +505,7 @@ github.com/spf13/pflag
# github.com/spf13/viper v1.4.0 # github.com/spf13/viper v1.4.0
## explicit ## explicit
github.com/spf13/viper github.com/spf13/viper
# github.com/spotinst/spotinst-sdk-go v1.49.0 # github.com/spotinst/spotinst-sdk-go v1.56.0
## explicit ## explicit
github.com/spotinst/spotinst-sdk-go/service/elastigroup github.com/spotinst/spotinst-sdk-go/service/elastigroup
github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws
@ -517,6 +517,7 @@ github.com/spotinst/spotinst-sdk-go/service/ocean/providers/gcp
github.com/spotinst/spotinst-sdk-go/spotinst github.com/spotinst/spotinst-sdk-go/spotinst
github.com/spotinst/spotinst-sdk-go/spotinst/client github.com/spotinst/spotinst-sdk-go/spotinst/client
github.com/spotinst/spotinst-sdk-go/spotinst/credentials github.com/spotinst/spotinst-sdk-go/spotinst/credentials
github.com/spotinst/spotinst-sdk-go/spotinst/featureflag
github.com/spotinst/spotinst-sdk-go/spotinst/log github.com/spotinst/spotinst-sdk-go/spotinst/log
github.com/spotinst/spotinst-sdk-go/spotinst/session github.com/spotinst/spotinst-sdk-go/spotinst/session
github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil