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/pflag v1.0.5
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/urfave/cli v1.20.0
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.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
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.49.0/go.mod h1:nWi2DyjUi1WUZclpsqZFXvImsU0T39ppqqHwC4/T5mw=
github.com/spotinst/spotinst-sdk-go v1.56.0 h1:w7OnAWXWPxvOvqjYv4i0kN70KWGTNcyoNB72suIA30g=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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:
{
v, err := parseInt(v)
v, err := parseFloat(v)
if err != nil {
return nil, err
}
if opts.Down == nil {
opts.Down = new(spotinsttasks.AutoScalerDownOpts)
}
opts.Down.MaxPercentage = fi.Int(int(fi.Int64Value(v)))
opts.Down.MaxPercentage = v
}
case InstanceGroupLabelAutoScalerScaleDownEvaluationPeriods:

View File

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

View File

@ -174,8 +174,8 @@ type AutoScaleHeadroom struct {
}
type AutoScaleDown struct {
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
MaxScaleDownPercentage *int `json:"maxScaleDownPercentage,omitempty"`
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
MaxScaleDownPercentage *float64 `json:"maxScaleDownPercentage,omitempty"`
forceSendFields []string
nullFields []string
@ -422,25 +422,26 @@ type Scaling struct {
}
type ScalingPolicy struct {
PolicyName *string `json:"policyName,omitempty"`
MetricName *string `json:"metricName,omitempty"`
Namespace *string `json:"namespace,omitempty"`
Source *string `json:"source,omitempty"`
Statistic *string `json:"statistic,omitempty"`
Unit *string `json:"unit,omitempty"`
Threshold *float64 `json:"threshold,omitempty"`
Adjustment *int `json:"adjustment,omitempty"`
MinTargetCapacity *int `json:"minTargetCapacity,omitempty"`
MaxTargetCapacity *int `json:"maxTargetCapacity,omitempty"`
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
Period *int `json:"period,omitempty"`
Cooldown *int `json:"cooldown,omitempty"`
Operator *string `json:"operator,omitempty"`
Dimensions []*Dimension `json:"dimensions,omitempty"`
Action *Action `json:"action,omitempty"`
Target *float64 `json:"target,omitempty"`
IsEnabled *bool `json:"isEnabled,omitempty"`
Predictive *Predictive `json:"predictive,omitempty"`
PolicyName *string `json:"policyName,omitempty"`
MetricName *string `json:"metricName,omitempty"`
Namespace *string `json:"namespace,omitempty"`
Source *string `json:"source,omitempty"`
Statistic *string `json:"statistic,omitempty"`
Unit *string `json:"unit,omitempty"`
Threshold *float64 `json:"threshold,omitempty"`
Adjustment *int `json:"adjustment,omitempty"`
MinTargetCapacity *int `json:"minTargetCapacity,omitempty"`
MaxTargetCapacity *int `json:"maxTargetCapacity,omitempty"`
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
Period *int `json:"period,omitempty"`
Cooldown *int `json:"cooldown,omitempty"`
Operator *string `json:"operator,omitempty"`
Dimensions []*Dimension `json:"dimensions,omitempty"`
Action *Action `json:"action,omitempty"`
Target *float64 `json:"target,omitempty"`
IsEnabled *bool `json:"isEnabled,omitempty"`
MaxCapacityPerScale *string `json:"maxCapacityPerScale,omitempty"`
Predictive *Predictive `json:"predictive,omitempty"`
forceSendFields []string
nullFields []string
@ -2213,7 +2214,7 @@ func (o *AutoScaleDown) SetEvaluationPeriods(v *int) *AutoScaleDown {
return o
}
func (o *AutoScaleDown) SetMaxScaleDownPercentage(v *int) *AutoScaleDown {
func (o *AutoScaleDown) SetMaxScaleDownPercentage(v *float64) *AutoScaleDown {
if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil {
o.nullFields = append(o.nullFields, "MaxScaleDownPercentage")
}
@ -2792,6 +2793,13 @@ func (o *ScalingPolicy) SetIsEnabled(v *bool) *ScalingPolicy {
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
// region Action

View File

@ -14,19 +14,22 @@ import (
)
type LaunchSpec struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
OceanID *string `json:"oceanId,omitempty"`
ImageID *string `json:"imageId,omitempty"`
UserData *string `json:"userData,omitempty"`
RootVolumeSize *int `json:"rootVolumeSize,omitempty"`
SecurityGroupIDs []string `json:"securityGroupIds,omitempty"`
SubnetIDs []string `json:"subnetIds,omitempty"`
IAMInstanceProfile *IAMInstanceProfile `json:"iamInstanceProfile,omitempty"`
Labels []*Label `json:"labels,omitempty"`
Taints []*Taint `json:"taints,omitempty"`
AutoScale *AutoScale `json:"autoScale,omitempty"`
Tags []*Tag `json:"tags,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
OceanID *string `json:"oceanId,omitempty"`
ImageID *string `json:"imageId,omitempty"`
UserData *string `json:"userData,omitempty"`
RootVolumeSize *int `json:"rootVolumeSize,omitempty"`
SecurityGroupIDs []string `json:"securityGroupIds,omitempty"`
SubnetIDs []string `json:"subnetIds,omitempty"`
ResourceLimits *ResourceLimits `json:"resourceLimits,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"`
Taints []*Taint `json:"taints,omitempty"`
Tags []*Tag `json:"tags,omitempty"`
// Read-only fields.
CreatedAt *time.Time `json:"createdAt,omitempty"`
@ -49,6 +52,46 @@ type LaunchSpec struct {
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 {
Key *string `json:"key,omitempty"`
Value *string `json:"value,omitempty"`
@ -83,6 +126,21 @@ type AutoScaleHeadroom struct {
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 {
OceanID *string `json:"oceanId,omitempty"`
}
@ -374,6 +432,186 @@ func (o *LaunchSpec) SetAutoScale(v *AutoScale) *LaunchSpec {
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
// region Label
@ -484,9 +722,43 @@ func (o *AutoScaleHeadroom) SetNumOfUnits(v *int) *AutoScaleHeadroom {
return o
}
func (o *LaunchSpec) SetTags(v []*Tag) *LaunchSpec {
if o.Tags = v; o.Tags == nil {
o.nullFields = append(o.nullFields, "Tags")
// endregion
// 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
}

View File

@ -173,8 +173,8 @@ type AutoScalerResourceLimits struct {
}
type AutoScalerDown struct {
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
MaxScaleDownPercentage *int `json:"maxScaleDownPercentage,omitempty"`
EvaluationPeriods *int `json:"evaluationPeriods,omitempty"`
MaxScaleDownPercentage *float64 `json:"maxScaleDownPercentage,omitempty"`
forceSendFields []string
nullFields []string
@ -216,19 +216,30 @@ type DeleteClusterInput struct {
type DeleteClusterOutput struct{}
// Deprecated: Use CreateRollInput instead.
type RollClusterInput struct {
Roll *Roll `json:"roll,omitempty"`
}
// Deprecated: Use CreateRollOutput instead.
type RollClusterOutput struct {
RollClusterStatus *RollClusterStatus `json:"clusterDeploymentStatus,omitempty"`
}
// Deprecated: Use RollSpec instead.
type Roll struct {
ClusterID *string `json:"clusterId,omitempty"`
BatchSizePercentage *int `json:"batchSizePercentage,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
}
// Deprecated: Use RollStatus instead.
type RollClusterStatus struct {
OceanID *string `json:"oceanId,omitempty"`
RollID *string `json:"id,omitempty"`
@ -240,11 +251,71 @@ type RollClusterStatus struct {
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 {
Unit *string `json:"unit,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) {
b := new(Cluster)
if err := json.Unmarshal(in, b); err != nil {
@ -280,7 +351,7 @@ func clustersFromHttpResponse(resp *http.Response) ([]*Cluster, error) {
return clustersFromJSON(body)
}
func rollStatusFromJSON(in []byte) (*RollClusterStatus, error) {
func rollClusterStatusFromJSON(in []byte) (*RollClusterStatus, error) {
b := new(RollClusterStatus)
if err := json.Unmarshal(in, b); err != nil {
return nil, err
@ -288,7 +359,7 @@ func rollStatusFromJSON(in []byte) (*RollClusterStatus, error) {
return b, nil
}
func rollStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
func rollClusterStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
var rw client.Response
if err := json.Unmarshal(in, &rw); err != nil {
return nil, err
@ -297,6 +368,41 @@ func rollStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
if len(out) == 0 {
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 {
b, err := rollStatusFromJSON(rb)
if err != nil {
@ -307,7 +413,7 @@ func rollStatusesFromJSON(in []byte) ([]*RollClusterStatus, error) {
return out, nil
}
func rollStatusesFromHttpResponse(resp *http.Response) ([]*RollClusterStatus, error) {
func rollStatusesFromHttpResponse(resp *http.Response) ([]*RollStatus, error) {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
@ -434,6 +540,130 @@ func (s *ServiceOp) DeleteCluster(ctx context.Context, input *DeleteClusterInput
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) {
path, err := uritemplates.Expand("/ocean/aws/k8s/cluster/{clusterId}/roll", uritemplates.Values{
"clusterId": spotinst.StringValue(input.Roll.ClusterID),
@ -454,7 +684,7 @@ func (s *ServiceOp) Roll(ctx context.Context, input *RollClusterInput) (*RollClu
}
defer resp.Body.Close()
rs, err := rollStatusesFromHttpResponse(resp)
rs, err := rollClusterStatusesFromHttpResponse(resp)
if err != nil {
return nil, err
}
@ -1022,7 +1252,7 @@ func (o *AutoScalerDown) SetEvaluationPeriods(v *int) *AutoScalerDown {
return o
}
func (o *AutoScalerDown) SetMaxScaleDownPercentage(v *int) *AutoScalerDown {
func (o *AutoScalerDown) SetMaxScaleDownPercentage(v *float64) *AutoScalerDown {
if o.MaxScaleDownPercentage = v; o.MaxScaleDownPercentage == nil {
o.nullFields = append(o.nullFields, "MaxScaleDownPercentage")
}
@ -1030,3 +1260,48 @@ func (o *AutoScalerDown) SetMaxScaleDownPercentage(v *int) *AutoScalerDown {
}
// 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 {
MaxScaleDownPercentage *int `json:"maxScaleDownPercentage,omitempty"`
MaxScaleDownPercentage *float64 `json:"maxScaleDownPercentage,omitempty"`
forceSendFields []string
nullFields []string
@ -207,8 +207,14 @@ type ECSRollClusterOutput struct {
}
type ECSRoll struct {
ClusterID *string `json:"clusterId,omitempty"`
BatchSizePercentage *int `json:"batchSizePercentage,omitempty"`
ClusterID *string `json:"clusterId,omitempty"`
Comment *string `json:"comment,omitempty"`
BatchSizePercentage *int `json:"batchSizePercentage,omitempty"`
LaunchSpecIDs []string `json:"launchSpecIds,omitempty"`
InstanceIDs []string `json:"instanceIds,omitempty"`
forceSendFields []string
nullFields []string
}
type ECSRollClusterStatus struct {
@ -912,7 +918,7 @@ func (o ECSAutoScalerDown) MarshalJSON() ([]byte, error) {
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 {
o.nullFields = append(o.nullFields, "MaxScaleDownPercentage")
}
@ -920,3 +926,41 @@ func (o *ECSAutoScalerDown) SetMaxScaleDownPercentage(v *int) *ECSAutoScalerDown
}
// 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
// the service.
type Service interface {
serviceKubernetes
serviceECS
ListResourceSuggestions(context.Context, *ListResourceSuggestionsInput) (*ListResourceSuggestionsOutput, error)
}
type serviceKubernetes interface {
ListClusters(context.Context, *ListClustersInput) (*ListClustersOutput, error)
CreateCluster(context.Context, *CreateClusterInput) (*CreateClusterOutput, error)
ReadCluster(context.Context, *ReadClusterInput) (*ReadClusterOutput, error)
@ -27,7 +34,17 @@ type Service interface {
ListClusterInstances(context.Context, *ListClusterInstancesInput) (*ListClusterInstancesOutput, 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)
}
type serviceECS interface {
ListECSClusters(context.Context, *ListECSClustersInput) (*ListECSClustersOutput, error)
CreateECSCluster(context.Context, *CreateECSClusterInput) (*CreateECSClusterOutput, error)
ReadECSCluster(context.Context, *ReadECSClusterInput) (*ReadECSClusterOutput, error)
@ -41,8 +58,6 @@ type Service interface {
DeleteECSLaunchSpec(context.Context, *DeleteECSLaunchSpecInput) (*DeleteECSLaunchSpecOutput, error)
RollECS(context.Context, *ECSRollClusterInput) (*ECSRollClusterOutput, error)
ListResourceSuggestions(context.Context, *ListResourceSuggestionsInput) (*ListResourceSuggestionsOutput, error)
}
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.
func (c *Config) Merge(cfgs ...*Config) {
for _, cfg := range cfgs {
mergeConfig(c, cfg)
mergeConfigs(c, cfg)
}
}
func mergeConfig(c1, c2 *Config) {
func mergeConfigs(c1, c2 *Config) {
if c2 == nil {
return
}

View File

@ -13,5 +13,8 @@ go_library(
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"],
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
import (
"errors"
"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.
// Credentials will cache the credentials value.
//
@ -43,6 +47,9 @@ func (c *Credentials) Get() (Value, error) {
if err != nil {
return Value{}, err
}
if creds.Token == "" {
return Value{ProviderName: creds.ProviderName}, ErrNoValidTokenFound
}
c.creds = creds
c.forceRefresh = false
}

View File

@ -5,13 +5,13 @@ import "fmt"
// A Value is the Spotinst credentials value for individual credential fields.
type Value struct {
// Spotinst API token.
Token string `json:"token"`
Token string `ini:"token" json:"token"`
// Spotinst account ID.
Account string `json:"account"`
Account string `ini:"account" json:"account"`
// Provider used to get credentials.
ProviderName string `json:"-"`
ProviderName string `ini:"-" json:"-"`
}
// 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.
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 (
"errors"
"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.
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
// that provider until Retrieve is called again.
// A ChainProvider will search for a provider which returns credentials.
//
// 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.
@ -34,7 +36,6 @@ var ErrNoValidProvidersFoundInChain = errors.New("spotinst: no valid credentials
// )
type ChainProvider struct {
Providers []Provider
active Provider
}
// 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
// without error. If a provider is found it will be cached.
// without error.
func (c *ChainProvider) Retrieve() (Value, error) {
var value Value
var errs errorList
for _, p := range c.Providers {
value, err := p.Retrieve()
v, err := p.Retrieve()
if err == nil {
c.active = p
return value, nil
if featureflag.MergeCredentialsChain.Enabled() {
value.Merge(v)
if value.IsComplete() {
return value, nil
}
} else {
value = v
break
}
} else {
errs = append(errs, err)
}
errs = append(errs, err)
}
c.active = nil
err := ErrNoValidProvidersFoundInChain
if len(errs) > 0 {
err = errs
}
return Value{}, err
if value.Token == "" {
err := ErrNoValidProvidersFoundInChain
if len(errs) > 0 {
err = errs
}
return Value{ProviderName: c.String()}, err
}
return value, nil
}
// String returns the string representation of the provider.
func (c *ChainProvider) String() string {
var out string
for i, provider := range c.Providers {

View File

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

View File

@ -12,7 +12,7 @@ import (
)
const (
// FileCredentialsProviderName provides a name of File provider.
// FileCredentialsProviderName specifies the name of the File provider.
FileCredentialsProviderName = "FileCredentialsProvider"
// FileCredentialsEnvVarFile specifies the name of the environment variable
@ -25,13 +25,13 @@ const (
)
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.
ErrFileCredentialsLoadFailed = errors.New("spotinst: failed to load credentials file")
// ErrFileCredentialsTokenNotFound is emitted when the loaded credentials
// do not contain a valid token.
ErrFileCredentialsTokenNotFound = errors.New("spotinst: credentials do not contain token")
// ErrFileCredentialsNotFound is returned when the loaded credentials
// are empty.
ErrFileCredentialsNotFound = errors.New("spotinst: credentials file or profile is empty")
)
// 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
}
func (p *FileProvider) String() string {
return FileCredentialsProviderName
}
// String returns the string representation of the provider.
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 {
if p.Profile == "" {
if p.Profile = os.Getenv(FileCredentialsEnvVarProfile); p.Profile != "" {
@ -110,7 +109,7 @@ func (p *FileProvider) profile() string {
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 {
if 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 {
return value, ErrFileCredentialsTokenNotFound
if value.IsEmpty() {
return value, ErrFileCredentialsNotFound
}
return value, nil
@ -151,13 +150,33 @@ func (p *FileProvider) loadCredentialsINI(profile, filename string) (Value, erro
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)
if err != nil {
return value, err
}
value.Token = section.Key("token").String()
value.Account = section.Key("account").String()
if err := section.StrictMapTo(&value); err != nil {
return value, err
}
return value, nil
}

View File

@ -4,10 +4,10 @@ import (
"errors"
)
// StaticCredentialsProviderName provides a name of Static provider.
// StaticCredentialsProviderName specifies the name of the Static provider.
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")
// A StaticProvider is a set of credentials which are set programmatically.
@ -19,25 +19,20 @@ type StaticProvider struct {
// a static credentials value provider.
func NewStaticCredentials(token, account string) *Credentials {
return NewCredentials(&StaticProvider{Value: Value{
Token: token,
Account: account,
ProviderName: StaticCredentialsProviderName,
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
if s.IsEmpty() {
return s.Value, ErrStaticCredentialsEmpty
}
return s.Value, nil
}
func (s *StaticProvider) String() string {
return StaticCredentialsProviderName
}
// String returns the string representation of the provider.
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
// 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.
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
## explicit
github.com/spf13/viper
# github.com/spotinst/spotinst-sdk-go v1.49.0
# github.com/spotinst/spotinst-sdk-go v1.56.0
## explicit
github.com/spotinst/spotinst-sdk-go/service/elastigroup
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/client
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/session
github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil