Merge pull request #11009 from spotinst/feat-launchspec-bdm

Spotinst: Add support for block device mappings in Ocean Launch Spec
This commit is contained in:
Kubernetes Prow Robot 2021-03-12 06:16:18 -08:00 committed by GitHub
commit 928b8cc442
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2263 additions and 224 deletions

4
go.mod
View File

@ -76,8 +76,8 @@ require (
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.0 github.com/spf13/viper v1.7.0
github.com/spotinst/spotinst-sdk-go v1.76.0 github.com/spotinst/spotinst-sdk-go v1.80.0
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.7.0
github.com/weaveworks/mesh v0.0.0-20170419100114-1f158d31de55 github.com/weaveworks/mesh v0.0.0-20170419100114-1f158d31de55
github.com/zclconf/go-cty v1.3.1 github.com/zclconf/go-cty v1.3.1
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83

10
go.sum
View File

@ -1006,8 +1006,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
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/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spotinst/spotinst-sdk-go v1.76.0 h1:tlFKrUAu7h3FgZM3Xy7436XR5lYo5Q/uePEy0AX5Ydw= github.com/spotinst/spotinst-sdk-go v1.80.0 h1:ygqAYGTA2Z/1nlIue1YtXsDGQCv6Lgx3fwO34EIi36E=
github.com/spotinst/spotinst-sdk-go v1.76.0/go.mod h1:sSRVZTSdUAPxeELD/urZkxcfU/DcxO1/UIdOxagqFBc= github.com/spotinst/spotinst-sdk-go v1.80.0/go.mod h1:tJtl0XQCi8YaZnCQfC/y3qHB9OpaxADH/N7EPa0hkt8=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY= github.com/storageos/go-api v2.2.0+incompatible/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
@ -1021,8 +1021,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -1561,8 +1562,9 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=

View File

@ -503,9 +503,7 @@ func (b *SpotInstanceGroupModelBuilder) buildOcean(c *fi.ModelBuilderContext, ig
} }
func (b *SpotInstanceGroupModelBuilder) buildLaunchSpec(c *fi.ModelBuilderContext, func (b *SpotInstanceGroupModelBuilder) buildLaunchSpec(c *fi.ModelBuilderContext,
ig, igOcean *kops.InstanceGroup, ocean *spotinsttasks.Ocean) (err error) { ig, igOcean *kops.InstanceGroup, ocean *spotinsttasks.Ocean) (err error) {
klog.V(4).Infof("Building instance group as LaunchSpec: %q", b.AutoscalingGroupName(ig)) klog.V(4).Infof("Building instance group as LaunchSpec: %q", b.AutoscalingGroupName(ig))
launchSpec := &spotinsttasks.LaunchSpec{ launchSpec := &spotinsttasks.LaunchSpec{
Name: fi.String(b.AutoscalingGroupName(ig)), Name: fi.String(b.AutoscalingGroupName(ig)),
@ -564,8 +562,6 @@ func (b *SpotInstanceGroupModelBuilder) buildLaunchSpec(c *fi.ModelBuilderContex
} }
if rootVolumeOpts != nil { // remove unsupported options if rootVolumeOpts != nil { // remove unsupported options
launchSpec.RootVolumeOpts = rootVolumeOpts launchSpec.RootVolumeOpts = rootVolumeOpts
launchSpec.RootVolumeOpts.Type = nil
launchSpec.RootVolumeOpts.IOPS = nil
launchSpec.RootVolumeOpts.Optimization = nil launchSpec.RootVolumeOpts.Optimization = nil
} }
@ -613,7 +609,6 @@ func (b *SpotInstanceGroupModelBuilder) buildLaunchSpec(c *fi.ModelBuilderContex
func (b *SpotInstanceGroupModelBuilder) buildSecurityGroups(c *fi.ModelBuilderContext, func (b *SpotInstanceGroupModelBuilder) buildSecurityGroups(c *fi.ModelBuilderContext,
ig *kops.InstanceGroup) ([]*awstasks.SecurityGroup, error) { ig *kops.InstanceGroup) ([]*awstasks.SecurityGroup, error) {
securityGroups := []*awstasks.SecurityGroup{ securityGroups := []*awstasks.SecurityGroup{
b.LinkToSecurityGroup(ig.Spec.Role), b.LinkToSecurityGroup(ig.Spec.Role),
} }
@ -692,9 +687,7 @@ func (b *SpotInstanceGroupModelBuilder) buildPublicIpOpts(ig *kops.InstanceGroup
} }
func (b *SpotInstanceGroupModelBuilder) buildRootVolumeOpts(ig *kops.InstanceGroup) (*spotinsttasks.RootVolumeOpts, error) { func (b *SpotInstanceGroupModelBuilder) buildRootVolumeOpts(ig *kops.InstanceGroup) (*spotinsttasks.RootVolumeOpts, error) {
opts := &spotinsttasks.RootVolumeOpts{ opts := new(spotinsttasks.RootVolumeOpts)
IOPS: ig.Spec.RootVolumeIops,
}
// Optimization. // Optimization.
{ {
@ -713,7 +706,7 @@ func (b *SpotInstanceGroupModelBuilder) buildRootVolumeOpts(ig *kops.InstanceGro
return nil, err return nil, err
} }
} }
opts.Size = fi.Int32(size) opts.Size = fi.Int64(int64(size))
} }
// Type. // Type.
@ -725,6 +718,22 @@ func (b *SpotInstanceGroupModelBuilder) buildRootVolumeOpts(ig *kops.InstanceGro
opts.Type = fi.String(typ) opts.Type = fi.String(typ)
} }
// IOPS.
{
iops := fi.Int32Value(ig.Spec.RootVolumeIops)
if iops > 0 {
opts.IOPS = fi.Int64(int64(iops))
}
}
// Throughput.
{
throughput := fi.Int32Value(ig.Spec.RootVolumeThroughput)
if throughput > 0 {
opts.Throughput = fi.Int64(int64(throughput))
}
}
return opts, nil return opts, nil
} }

View File

@ -27,7 +27,6 @@ go_library(
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library", "//vendor/k8s.io/klog/v2:go_default_library",
], ],
) )

View File

@ -31,7 +31,6 @@ import (
"github.com/spotinst/spotinst-sdk-go/spotinst/client" "github.com/spotinst/spotinst-sdk-go/spotinst/client"
"github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil" "github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kops/pkg/resources/spotinst" "k8s.io/kops/pkg/resources/spotinst"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
@ -76,8 +75,9 @@ type Elastigroup struct {
type RootVolumeOpts struct { type RootVolumeOpts struct {
Type *string Type *string
Size *int32 Size *int64
IOPS *int32 IOPS *int64
Throughput *int64
Optimization *bool Optimization *bool
} }
@ -284,12 +284,18 @@ func (e *Elastigroup) Find(c *fi.Context) (*Elastigroup, error) {
if actual.RootVolumeOpts == nil { if actual.RootVolumeOpts == nil {
actual.RootVolumeOpts = new(RootVolumeOpts) actual.RootVolumeOpts = new(RootVolumeOpts)
} }
if b.EBS.IOPS != nil { if b.EBS.VolumeType != nil {
actual.RootVolumeOpts.IOPS = fi.Int32(int32(fi.IntValue(b.EBS.IOPS))) actual.RootVolumeOpts.Type = fi.String(strings.ToLower(fi.StringValue(b.EBS.VolumeType)))
}
if b.EBS.VolumeSize != nil {
actual.RootVolumeOpts.Size = fi.Int64(int64(fi.IntValue(b.EBS.VolumeSize)))
}
if b.EBS.IOPS != nil {
actual.RootVolumeOpts.IOPS = fi.Int64(int64(fi.IntValue(b.EBS.IOPS)))
}
if b.EBS.Throughput != nil {
actual.RootVolumeOpts.Throughput = fi.Int64(int64(fi.IntValue(b.EBS.Throughput)))
} }
actual.RootVolumeOpts.Type = fi.String(strings.ToLower(fi.StringValue(b.EBS.VolumeType)))
actual.RootVolumeOpts.Size = fi.Int32(int32(fi.IntValue(b.EBS.VolumeSize)))
} }
} }
} }
@ -548,28 +554,25 @@ func (_ *Elastigroup) create(cloud awsup.AWSCloud, a, e, changes *Elastigroup) e
// Block device mappings. // Block device mappings.
{ {
rootDevices, err := e.buildRootDevice(cloud) rootDevice, err := buildRootDevice(cloud, e.RootVolumeOpts, e.ImageID)
if err != nil { if err != nil {
return err return err
} }
ephemeralDevices, err := e.buildEphemeralDevices(cloud, e.OnDemandInstanceType) mappings := []*aws.BlockDeviceMapping{
e.convertBlockDeviceMapping(rootDevice),
}
ephemeralDevices, err := buildEphemeralDevices(cloud, e.OnDemandInstanceType)
if err != nil { if err != nil {
return err return err
} }
if len(rootDevices) != 0 || len(ephemeralDevices) != 0 { for _, bdm := range ephemeralDevices {
var mappings []*aws.BlockDeviceMapping mappings = append(mappings, e.convertBlockDeviceMapping(bdm))
for device, bdm := range rootDevices {
mappings = append(mappings, e.buildBlockDeviceMapping(device, bdm))
}
for device, bdm := range ephemeralDevices {
mappings = append(mappings, e.buildBlockDeviceMapping(device, bdm))
}
if len(mappings) > 0 {
group.Compute.LaunchSpecification.SetBlockDeviceMappings(mappings)
}
} }
group.Compute.LaunchSpecification.SetBlockDeviceMappings(mappings)
} }
// Image. // Image.
@ -1001,40 +1004,36 @@ func (_ *Elastigroup) update(cloud awsup.AWSCloud, a, e, changes *Elastigroup) e
// Root volume options. // Root volume options.
{ {
if opts := changes.RootVolumeOpts; opts != nil { if opts := changes.RootVolumeOpts; opts != nil {
// Block device mappings. // Block device mappings.
{ {
if opts.Type != nil || opts.Size != nil || opts.IOPS != nil { if opts.Type != nil || opts.Size != nil || opts.IOPS != nil {
rootDevices, err := e.buildRootDevice(cloud) rootDevice, err := buildRootDevice(cloud, opts, e.ImageID)
if err != nil { if err != nil {
return err return err
} }
ephemeralDevices, err := e.buildEphemeralDevices(cloud, e.OnDemandInstanceType) mappings := []*aws.BlockDeviceMapping{
e.convertBlockDeviceMapping(rootDevice),
}
ephemeralDevices, err := buildEphemeralDevices(cloud, e.OnDemandInstanceType)
if err != nil { if err != nil {
return err return err
} }
if len(rootDevices) != 0 || len(ephemeralDevices) != 0 { for _, bdm := range ephemeralDevices {
var mappings []*aws.BlockDeviceMapping mappings = append(mappings, e.convertBlockDeviceMapping(bdm))
for device, bdm := range rootDevices {
mappings = append(mappings, e.buildBlockDeviceMapping(device, bdm))
}
for device, bdm := range ephemeralDevices {
mappings = append(mappings, e.buildBlockDeviceMapping(device, bdm))
}
if len(mappings) > 0 {
if group.Compute == nil {
group.Compute = new(aws.Compute)
}
if group.Compute.LaunchSpecification == nil {
group.Compute.LaunchSpecification = new(aws.LaunchSpecification)
}
group.Compute.LaunchSpecification.SetBlockDeviceMappings(mappings)
changed = true
}
} }
if group.Compute == nil {
group.Compute = new(aws.Compute)
}
if group.Compute.LaunchSpecification == nil {
group.Compute.LaunchSpecification = new(aws.LaunchSpecification)
}
group.Compute.LaunchSpecification.SetBlockDeviceMappings(mappings)
changed = true
} }
} }
@ -1379,6 +1378,8 @@ type terraformElastigroupBlockDevice struct {
VirtualName *string `json:"virtual_name,omitempty" cty:"virtual_name"` VirtualName *string `json:"virtual_name,omitempty" cty:"virtual_name"`
VolumeType *string `json:"volume_type,omitempty" cty:"volume_type"` VolumeType *string `json:"volume_type,omitempty" cty:"volume_type"`
VolumeSize *int64 `json:"volume_size,omitempty" cty:"volume_size"` VolumeSize *int64 `json:"volume_size,omitempty" cty:"volume_size"`
VolumeIOPS *int64 `json:"iops,omitempty" cty:"iops"`
VolumeThroughput *int64 `json:"throughput,omitempty" cty:"throughput"`
DeleteOnTermination *bool `json:"delete_on_termination,omitempty" cty:"delete_on_termination"` DeleteOnTermination *bool `json:"delete_on_termination,omitempty" cty:"delete_on_termination"`
} }
@ -1556,42 +1557,34 @@ func (_ *Elastigroup) RenderTerraform(t *terraform.TerraformTarget, a, e, change
// Root volume options. // Root volume options.
{ {
if opts := e.RootVolumeOpts; opts != nil { if opts := e.RootVolumeOpts; opts != nil {
// Block device mappings. // Block device mappings.
{ {
rootDevices, err := e.buildRootDevice(t.Cloud.(awsup.AWSCloud)) rootDevice, err := buildRootDevice(t.Cloud.(awsup.AWSCloud), e.RootVolumeOpts, e.ImageID)
if err != nil { if err != nil {
return err return err
} }
ephemeralDevices, err := e.buildEphemeralDevices(cloud, e.OnDemandInstanceType) tf.RootBlockDevice = &terraformElastigroupBlockDevice{
if err != nil { DeviceName: rootDevice.DeviceName,
return err VolumeType: rootDevice.EbsVolumeType,
VolumeSize: rootDevice.EbsVolumeSize,
VolumeIOPS: rootDevice.EbsVolumeIops,
VolumeThroughput: rootDevice.EbsVolumeThroughput,
DeleteOnTermination: fi.Bool(true),
} }
if len(rootDevices) != 0 { ephemeralDevices, err := buildEphemeralDevices(cloud, e.OnDemandInstanceType)
if len(rootDevices) != 1 { if err != nil {
return fmt.Errorf("unexpectedly found multiple root devices") return err
}
for name, bdm := range rootDevices {
tf.RootBlockDevice = &terraformElastigroupBlockDevice{
DeviceName: fi.String(name),
VolumeType: bdm.EbsVolumeType,
VolumeSize: bdm.EbsVolumeSize,
DeleteOnTermination: fi.Bool(true),
}
}
} }
if len(ephemeralDevices) != 0 { if len(ephemeralDevices) != 0 {
tf.EphemeralBlockDevice = []*terraformElastigroupBlockDevice{} tf.EphemeralBlockDevice = make([]*terraformElastigroupBlockDevice, len(ephemeralDevices))
for _, deviceName := range sets.StringKeySet(ephemeralDevices).List() { for i, bdm := range ephemeralDevices {
bdm := ephemeralDevices[deviceName] tf.EphemeralBlockDevice[i] = &terraformElastigroupBlockDevice{
tf.EphemeralBlockDevice = append(tf.EphemeralBlockDevice, &terraformElastigroupBlockDevice{ DeviceName: bdm.DeviceName,
VirtualName: bdm.VirtualName, VirtualName: bdm.VirtualName,
DeviceName: fi.String(deviceName), }
})
} }
} }
} }
@ -1696,70 +1689,76 @@ func (e *Elastigroup) buildAutoScaleLabels(labelsMap map[string]string) []*aws.A
return labels return labels
} }
func (e *Elastigroup) buildEphemeralDevices(c awsup.AWSCloud, instanceTypeName *string) (map[string]*awstasks.BlockDeviceMapping, error) { func buildEphemeralDevices(cloud awsup.AWSCloud, machineType *string) ([]*awstasks.BlockDeviceMapping, error) {
if instanceTypeName == nil { info, err := awsup.GetMachineTypeInfo(cloud, fi.StringValue(machineType))
return nil, fi.RequiredField("InstanceType")
}
instanceType, err := awsup.GetMachineTypeInfo(c, *instanceTypeName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
blockDeviceMappings := make(map[string]*awstasks.BlockDeviceMapping) bdms := make([]*awstasks.BlockDeviceMapping, len(info.EphemeralDevices()))
for _, ed := range instanceType.EphemeralDevices() { for i, ed := range info.EphemeralDevices() {
m := &awstasks.BlockDeviceMapping{ bdms[i] = &awstasks.BlockDeviceMapping{
DeviceName: fi.String(ed.DeviceName),
VirtualName: fi.String(ed.VirtualName), VirtualName: fi.String(ed.VirtualName),
} }
blockDeviceMappings[ed.DeviceName] = m
} }
return blockDeviceMappings, nil return bdms, nil
} }
func (e *Elastigroup) buildRootDevice(cloud awsup.AWSCloud) (map[string]*awstasks.BlockDeviceMapping, error) { func buildRootDevice(cloud awsup.AWSCloud, volumeOpts *RootVolumeOpts,
image, err := resolveImage(cloud, fi.StringValue(e.ImageID)) imageID *string) (*awstasks.BlockDeviceMapping, error) {
img, err := resolveImage(cloud, fi.StringValue(imageID))
if err != nil { if err != nil {
return nil, err return nil, err
} }
rootDeviceName := fi.StringValue(image.RootDeviceName) bdm := &awstasks.BlockDeviceMapping{
blockDeviceMappings := make(map[string]*awstasks.BlockDeviceMapping) DeviceName: img.RootDeviceName,
EbsVolumeSize: volumeOpts.Size,
rootDeviceMapping := &awstasks.BlockDeviceMapping{ EbsVolumeType: volumeOpts.Type,
EbsDeleteOnTermination: fi.Bool(true), EbsDeleteOnTermination: fi.Bool(true),
EbsVolumeSize: fi.Int64(int64(fi.Int32Value(e.RootVolumeOpts.Size))),
EbsVolumeType: e.RootVolumeOpts.Type,
} }
// The parameter IOPS is not supported for gp2 volumes. // IOPS is not supported for gp2 volumes.
if e.RootVolumeOpts.IOPS != nil && fi.StringValue(e.RootVolumeOpts.Type) != "gp2" { if volumeOpts.IOPS != nil && fi.StringValue(volumeOpts.Type) != "gp2" {
rootDeviceMapping.EbsVolumeIops = fi.Int64(int64(fi.Int32Value(e.RootVolumeOpts.IOPS))) bdm.EbsVolumeIops = volumeOpts.IOPS
} }
blockDeviceMappings[rootDeviceName] = rootDeviceMapping // Throughput is only supported for gp3 volumes.
if volumeOpts.Throughput != nil && fi.StringValue(volumeOpts.Type) == "gp3" {
bdm.EbsVolumeThroughput = volumeOpts.Throughput
}
return blockDeviceMappings, nil return bdm, nil
} }
func (e *Elastigroup) buildBlockDeviceMapping(deviceName string, i *awstasks.BlockDeviceMapping) *aws.BlockDeviceMapping { func (e *Elastigroup) convertBlockDeviceMapping(in *awstasks.BlockDeviceMapping) *aws.BlockDeviceMapping {
o := &aws.BlockDeviceMapping{} out := &aws.BlockDeviceMapping{
o.DeviceName = fi.String(deviceName) DeviceName: in.DeviceName,
o.VirtualName = i.VirtualName VirtualName: in.VirtualName,
}
if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || i.EbsVolumeType != nil { if in.EbsDeleteOnTermination != nil || in.EbsVolumeSize != nil || in.EbsVolumeType != nil {
o.EBS = &aws.EBS{} out.EBS = &aws.EBS{
o.EBS.DeleteOnTermination = i.EbsDeleteOnTermination VolumeType: in.EbsVolumeType,
o.EBS.VolumeSize = fi.Int(int(fi.Int64Value(i.EbsVolumeSize))) VolumeSize: fi.Int(int(fi.Int64Value(in.EbsVolumeSize))),
o.EBS.VolumeType = i.EbsVolumeType DeleteOnTermination: in.EbsDeleteOnTermination,
}
// The parameter IOPS is not supported for gp2 volumes. // IOPS is not valid for gp2 volumes.
if i.EbsVolumeIops != nil && fi.StringValue(i.EbsVolumeType) != "gp2" { if in.EbsVolumeIops != nil && fi.StringValue(in.EbsVolumeType) != "gp2" {
o.EBS.IOPS = fi.Int(int(fi.Int64Value(i.EbsVolumeIops))) out.EBS.IOPS = fi.Int(int(fi.Int64Value(in.EbsVolumeIops)))
}
// Throughput is only valid for gp3 volumes.
if in.EbsVolumeThroughput != nil && fi.StringValue(in.EbsVolumeType) == "gp3" {
out.EBS.Throughput = fi.Int(int(fi.Int64Value(in.EbsVolumeThroughput)))
} }
} }
return o return out
} }
func (e *Elastigroup) applyDefaults() { func (e *Elastigroup) applyDefaults() {

View File

@ -182,9 +182,38 @@ func (o *LaunchSpec) Find(c *fi.Context) (*LaunchSpec, error) {
// Root volume options. // Root volume options.
{ {
if spec.RootVolumeSize != nil { // Volume size.
actual.RootVolumeOpts = new(RootVolumeOpts) {
actual.RootVolumeOpts.Size = fi.Int32(int32(*spec.RootVolumeSize)) if spec.RootVolumeSize != nil {
actual.RootVolumeOpts = new(RootVolumeOpts)
actual.RootVolumeOpts.Size = fi.Int64(int64(*spec.RootVolumeSize))
}
}
// Block device mappings.
{
if spec.BlockDeviceMappings != nil {
for _, b := range spec.BlockDeviceMappings {
if b.EBS == nil || b.EBS.SnapshotID != nil {
continue // not the root
}
if actual.RootVolumeOpts == nil {
actual.RootVolumeOpts = new(RootVolumeOpts)
}
if b.EBS.VolumeType != nil {
actual.RootVolumeOpts.Type = fi.String(strings.ToLower(fi.StringValue(b.EBS.VolumeType)))
}
if b.EBS.VolumeSize != nil {
actual.RootVolumeOpts.Size = fi.Int64(int64(fi.IntValue(b.EBS.VolumeSize)))
}
if b.EBS.IOPS != nil {
actual.RootVolumeOpts.IOPS = fi.Int64(int64(fi.IntValue(b.EBS.IOPS)))
}
if b.EBS.Throughput != nil {
actual.RootVolumeOpts.Throughput = fi.Int64(int64(fi.IntValue(b.EBS.Throughput)))
}
}
}
} }
} }
@ -367,11 +396,15 @@ func (_ *LaunchSpec) create(cloud awsup.AWSCloud, a, e, changes *LaunchSpec) err
// Root volume options. // Root volume options.
{ {
if opts := e.RootVolumeOpts; opts != nil { if opts := e.RootVolumeOpts; opts != nil {
rootDevice, err := buildRootDevice(cloud, e.RootVolumeOpts, e.ImageID)
// Volume size. if err != nil {
if opts.Size != nil { return err
spec.SetRootVolumeSize(fi.Int(int(*opts.Size)))
} }
spec.SetRootVolumeSize(nil) // mutually exclusive
spec.SetBlockDeviceMappings([]*aws.BlockDeviceMapping{
e.convertBlockDeviceMapping(rootDevice),
})
} }
} }
@ -548,14 +581,17 @@ func (_ *LaunchSpec) update(cloud awsup.AWSCloud, a, e, changes *LaunchSpec) err
// Root volume options. // Root volume options.
{ {
if opts := changes.RootVolumeOpts; opts != nil { if opts := changes.RootVolumeOpts; opts != nil {
rootDevice, err := buildRootDevice(cloud, opts, e.ImageID)
// Volume size. if err != nil {
if opts.Size != nil { return err
spec.SetRootVolumeSize(fi.Int(int(*opts.Size)))
changed = true
} }
spec.SetRootVolumeSize(nil) // mutually exclusive
spec.SetBlockDeviceMappings([]*aws.BlockDeviceMapping{
e.convertBlockDeviceMapping(rootDevice),
})
changes.RootVolumeOpts = nil changes.RootVolumeOpts = nil
changed = true
} }
} }
@ -743,7 +779,7 @@ type terraformLaunchSpec struct {
ImageID *string `json:"image_id,omitempty" cty:"image_id"` ImageID *string `json:"image_id,omitempty" cty:"image_id"`
AssociatePublicIPAddress *bool `json:"associate_public_ip_address,omitempty" cty:"associate_public_ip_address"` AssociatePublicIPAddress *bool `json:"associate_public_ip_address,omitempty" cty:"associate_public_ip_address"`
RestrictScaleDown *bool `json:"restrict_scale_down,omitempty" cty:"restrict_scale_down"` RestrictScaleDown *bool `json:"restrict_scale_down,omitempty" cty:"restrict_scale_down"`
RootVolumeSize *int32 `json:"root_volume_size,omitempty" cty:"root_volume_size"` RootVolumeSize *int64 `json:"root_volume_size,omitempty" cty:"root_volume_size"`
UserData *terraform.Literal `json:"user_data,omitempty" cty:"user_data"` UserData *terraform.Literal `json:"user_data,omitempty" cty:"user_data"`
IAMInstanceProfile *terraform.Literal `json:"iam_instance_profile,omitempty" cty:"iam_instance_profile"` IAMInstanceProfile *terraform.Literal `json:"iam_instance_profile,omitempty" cty:"iam_instance_profile"`
KeyName *terraform.Literal `json:"key_name,omitempty" cty:"key_name"` KeyName *terraform.Literal `json:"key_name,omitempty" cty:"key_name"`
@ -754,6 +790,7 @@ type terraformLaunchSpec struct {
Labels []*terraformKV `json:"labels,omitempty" cty:"labels"` Labels []*terraformKV `json:"labels,omitempty" cty:"labels"`
Tags []*terraformKV `json:"tags,omitempty" cty:"tags"` Tags []*terraformKV `json:"tags,omitempty" cty:"tags"`
Headrooms []*terraformAutoScalerHeadroom `json:"autoscale_headrooms,omitempty" cty:"autoscale_headrooms"` Headrooms []*terraformAutoScalerHeadroom `json:"autoscale_headrooms,omitempty" cty:"autoscale_headrooms"`
BlockDeviceMappings []*terraformBlockDeviceMapping `json:"block_device_mappings,omitempty" cty:"block_device_mappings"`
Strategy *terraformLaunchSpecStrategy `json:"strategy,omitempty" cty:"strategy"` Strategy *terraformLaunchSpecStrategy `json:"strategy,omitempty" cty:"strategy"`
} }
@ -761,6 +798,20 @@ type terraformLaunchSpecStrategy struct {
SpotPercentage *int64 `json:"spot_percentage,omitempty" cty:"spot_percentage"` SpotPercentage *int64 `json:"spot_percentage,omitempty" cty:"spot_percentage"`
} }
type terraformBlockDeviceMapping struct {
DeviceName *string `json:"device_name,omitempty" cty:"device_name"`
EBS *terraformBlockDeviceMappingEBS `json:"ebs,omitempty" cty:"ebs"`
}
type terraformBlockDeviceMappingEBS struct {
VirtualName *string `json:"virtual_name,omitempty" cty:"virtual_name"`
VolumeType *string `json:"volume_type,omitempty" cty:"volume_type"`
VolumeSize *int64 `json:"volume_size,omitempty" cty:"volume_size"`
VolumeIOPS *int64 `json:"iops,omitempty" cty:"iops"`
VolumeThroughput *int64 `json:"throughput,omitempty" cty:"throughput"`
DeleteOnTermination *bool `json:"delete_on_termination,omitempty" cty:"delete_on_termination"`
}
func (_ *LaunchSpec) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LaunchSpec) error { func (_ *LaunchSpec) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *LaunchSpec) error {
cloud := t.Cloud.(awsup.AWSCloud) cloud := t.Cloud.(awsup.AWSCloud)
@ -840,11 +891,21 @@ func (_ *LaunchSpec) RenderTerraform(t *terraform.TerraformTarget, a, e, changes
// Root volume options. // Root volume options.
if opts := e.RootVolumeOpts; opts != nil { if opts := e.RootVolumeOpts; opts != nil {
rootDevice, err := buildRootDevice(t.Cloud.(awsup.AWSCloud), e.RootVolumeOpts, e.ImageID)
// Volume size. if err != nil {
if opts.Size != nil { return err
tf.RootVolumeSize = opts.Size
} }
tf.BlockDeviceMappings = append(tf.BlockDeviceMappings, &terraformBlockDeviceMapping{
DeviceName: rootDevice.DeviceName,
EBS: &terraformBlockDeviceMappingEBS{
VolumeType: rootDevice.EbsVolumeType,
VolumeSize: rootDevice.EbsVolumeSize,
VolumeIOPS: rootDevice.EbsVolumeIops,
VolumeThroughput: rootDevice.EbsVolumeThroughput,
DeleteOnTermination: fi.Bool(true),
},
})
} }
// Tags. // Tags.
@ -937,3 +998,30 @@ func (o *LaunchSpec) buildTags() []*aws.Tag {
return tags return tags
} }
func (o *LaunchSpec) convertBlockDeviceMapping(in *awstasks.BlockDeviceMapping) *aws.BlockDeviceMapping {
out := &aws.BlockDeviceMapping{
DeviceName: in.DeviceName,
VirtualName: in.VirtualName,
}
if in.EbsDeleteOnTermination != nil || in.EbsVolumeSize != nil || in.EbsVolumeType != nil {
out.EBS = &aws.EBS{
VolumeType: in.EbsVolumeType,
VolumeSize: fi.Int(int(fi.Int64Value(in.EbsVolumeSize))),
DeleteOnTermination: in.EbsDeleteOnTermination,
}
// IOPS is not valid for gp2 volumes.
if in.EbsVolumeIops != nil && fi.StringValue(in.EbsVolumeType) != "gp2" {
out.EBS.IOPS = fi.Int(int(fi.Int64Value(in.EbsVolumeIops)))
}
// Throughput is only valid for gp3 volumes.
if in.EbsVolumeThroughput != nil && fi.StringValue(in.EbsVolumeType) == "gp3" {
out.EBS.Throughput = fi.Int(int(fi.Int64Value(in.EbsVolumeThroughput)))
}
}
return out
}

View File

@ -275,7 +275,7 @@ func (o *Ocean) Find(c *fi.Context) (*Ocean, error) {
// Root volume options. // Root volume options.
if lc.RootVolumeSize != nil { if lc.RootVolumeSize != nil {
actual.RootVolumeOpts = new(RootVolumeOpts) actual.RootVolumeOpts = new(RootVolumeOpts)
actual.RootVolumeOpts.Size = fi.Int32(int32(*lc.RootVolumeSize)) actual.RootVolumeOpts.Size = fi.Int64(int64(*lc.RootVolumeSize))
} }
// Monitoring. // Monitoring.
@ -1034,7 +1034,7 @@ type terraformOcean struct {
EBSOptimized *bool `json:"ebs_optimized,omitempty" cty:"ebs_optimized"` EBSOptimized *bool `json:"ebs_optimized,omitempty" cty:"ebs_optimized"`
ImageID *string `json:"image_id,omitempty" cty:"image_id"` ImageID *string `json:"image_id,omitempty" cty:"image_id"`
AssociatePublicIPAddress *bool `json:"associate_public_ip_address,omitempty" cty:"associate_public_ip_address"` AssociatePublicIPAddress *bool `json:"associate_public_ip_address,omitempty" cty:"associate_public_ip_address"`
RootVolumeSize *int32 `json:"root_volume_size,omitempty" cty:"root_volume_size"` RootVolumeSize *int64 `json:"root_volume_size,omitempty" cty:"root_volume_size"`
UserData *terraform.Literal `json:"user_data,omitempty" cty:"user_data"` UserData *terraform.Literal `json:"user_data,omitempty" cty:"user_data"`
IAMInstanceProfile *terraform.Literal `json:"iam_instance_profile,omitempty" cty:"iam_instance_profile"` IAMInstanceProfile *terraform.Literal `json:"iam_instance_profile,omitempty" cty:"iam_instance_profile"`
KeyName *terraform.Literal `json:"key_name,omitempty" cty:"key_name"` KeyName *terraform.Literal `json:"key_name,omitempty" cty:"key_name"`

View File

@ -9,6 +9,7 @@ go_library(
deps = [ deps = [
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gcp:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gcp:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",

View File

@ -3,6 +3,7 @@ package elastigroup
import ( import (
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws" "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws"
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure" "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure"
azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3"
"github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gcp" "github.com/spotinst/spotinst-sdk-go/service/elastigroup/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"
@ -15,6 +16,7 @@ import (
type Service interface { type Service interface {
CloudProviderAWS() aws.Service CloudProviderAWS() aws.Service
CloudProviderAzure() azure.Service CloudProviderAzure() azure.Service
CloudProviderAzureV3() azurev3.Service
CloudProviderGCP() gcp.Service CloudProviderGCP() gcp.Service
} }
@ -46,6 +48,12 @@ func (s *ServiceOp) CloudProviderAzure() azure.Service {
} }
} }
func (s *ServiceOp) CloudProviderAzureV3() azurev3.Service {
return &azurev3.ServiceOp{
Client: s.Client,
}
}
func (s *ServiceOp) CloudProviderGCP() gcp.Service { func (s *ServiceOp) CloudProviderGCP() gcp.Service {
return &gcp.ServiceOp{ return &gcp.ServiceOp{
Client: s.Client, Client: s.Client,

View File

@ -499,6 +499,7 @@ type Strategy struct {
RevertToSpot *RevertToSpot `json:"revertToSpot,omitempty"` RevertToSpot *RevertToSpot `json:"revertToSpot,omitempty"`
ScalingStrategy *ScalingStrategy `json:"scalingStrategy,omitempty"` ScalingStrategy *ScalingStrategy `json:"scalingStrategy,omitempty"`
UtilizeCommitments *bool `json:"utilizeCommitments,omitempty"` UtilizeCommitments *bool `json:"utilizeCommitments,omitempty"`
MinimumInstanceLifetime *int `json:"minimumInstanceLifetime,omitempty"`
forceSendFields []string forceSendFields []string
nullFields []string nullFields []string
@ -618,6 +619,22 @@ type LaunchSpecification struct {
NetworkInterfaces []*NetworkInterface `json:"networkInterfaces,omitempty"` NetworkInterfaces []*NetworkInterface `json:"networkInterfaces,omitempty"`
Tags []*Tag `json:"tags,omitempty"` Tags []*Tag `json:"tags,omitempty"`
MetadataOptions *MetadataOptions `json:"metadataOptions,omitempty"` MetadataOptions *MetadataOptions `json:"metadataOptions,omitempty"`
CPUOptions *CPUOptions `json:"cpuOptions,omitempty"`
forceSendFields []string
nullFields []string
}
type MetadataOptions struct {
HTTPTokens *string `json:"httpTokens,omitempty"`
HTTPPutResponseHopLimit *int `json:"httpPutResponseHopLimit,omitempty"`
forceSendFields []string
nullFields []string
}
type CPUOptions struct {
ThreadsPerCore *int `json:"threadsPerCore,omitempty"`
forceSendFields []string forceSendFields []string
nullFields []string nullFields []string
@ -920,14 +937,6 @@ type StopDeploymentInput struct {
type StopDeploymentOutput struct{} type StopDeploymentOutput struct{}
type MetadataOptions struct {
HTTPTokens *string `json:"httpTokens,omitempty"`
HTTPPutResponseHopLimit *int `json:"httpPutResponseHopLimit,omitempty"`
forceSendFields []string
nullFields []string
}
func deploymentStatusFromJSON(in []byte) (*RollGroupStatus, error) { func deploymentStatusFromJSON(in []byte) (*RollGroupStatus, error) {
b := new(RollGroupStatus) b := new(RollGroupStatus)
if err := json.Unmarshal(in, b); err != nil { if err := json.Unmarshal(in, b); err != nil {
@ -3064,6 +3073,13 @@ func (o *Strategy) SetUtilizeCommitments(v *bool) *Strategy {
return o return o
} }
func (o *Strategy) SetMinimumInstanceLifetime(v *int) *Strategy {
if o.MinimumInstanceLifetime = v; o.MinimumInstanceLifetime == nil {
o.nullFields = append(o.nullFields, "MinimumInstanceLifetime")
}
return o
}
// endregion // endregion
// region ScalingStrategy // region ScalingStrategy
@ -3547,6 +3563,13 @@ func (o *LaunchSpecification) SetMetadataOptions(v *MetadataOptions) *LaunchSpec
return o return o
} }
func (o *LaunchSpecification) SetCPUOptions(v *CPUOptions) *LaunchSpecification {
if o.CPUOptions = v; o.CPUOptions == nil {
o.nullFields = append(o.nullFields, "CPUOptions")
}
return o
}
// endregion // endregion
// region LoadBalancersConfig // region LoadBalancersConfig
@ -4313,3 +4336,19 @@ func (o *MetadataOptions) SetHTTPPutResponseHopLimit(v *int) *MetadataOptions {
} }
// endregion // endregion
// region CPUOptions
func (o CPUOptions) MarshalJSON() ([]byte, error) {
type noMethod CPUOptions
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *CPUOptions) SetThreadsPerCore(v *int) *CPUOptions {
if o.ThreadsPerCore = v; o.ThreadsPerCore == nil {
o.nullFields = append(o.nullFields, "ThreadsPerCore")
}
return o
}
// endregion

View File

@ -766,7 +766,7 @@ func (s *ServiceOp) List(ctx context.Context, input *ListGroupsInput) (*ListGrou
} }
func (s *ServiceOp) Create(ctx context.Context, input *CreateGroupInput) (*CreateGroupOutput, error) { func (s *ServiceOp) Create(ctx context.Context, input *CreateGroupInput) (*CreateGroupOutput, error) {
r := client.NewRequest(http.MethodPost, "/compute/azure/group") r := client.NewRequest(http.MethodPost, "/azure/compute/group")
r.Obj = input r.Obj = input
resp, err := client.RequireOK(s.Client.Do(ctx, r)) resp, err := client.RequireOK(s.Client.Do(ctx, r))

View File

@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"azure.go",
"service.go",
],
importmap = "k8s.io/kops/vendor/github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3",
importpath = "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/client:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/session:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates:go_default_library",
],
)

View File

@ -0,0 +1,774 @@
package v3
import (
"context"
"encoding/json"
"io/ioutil"
"net/http"
"time"
"github.com/spotinst/spotinst-sdk-go/spotinst"
"github.com/spotinst/spotinst-sdk-go/spotinst/client"
"github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil"
"github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates"
)
type Group struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ResourceGroupName *string `json:"resourceGroupName,omitempty"`
Region *string `json:"region,omitempty"`
Capacity *Capacity `json:"capacity,omitempty"`
Compute *Compute `json:"compute,omitempty"`
Strategy *Strategy `json:"strategy,omitempty"`
// Read-only fields.
CreatedAt *time.Time `json:"createdAt,omitempty"`
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
// forceSendFields is a list of field names (e.g. "Keys") to
// unconditionally include in API requests. By default, fields with
// empty values are omitted from API requests. However, any non-pointer,
// non-interface field appearing in ForceSendFields will be sent to the
// server regardless of whether the field is empty or not. This may be
// used to include empty fields in Patch requests.
forceSendFields []string
// nullFields is a list of field names (e.g. "Keys") to include in API
// requests with the JSON null value. By default, fields with empty
// values are omitted from API requests. However, any field with an
// empty value appearing in NullFields will be sent to the server as
// null. It is an error if a field in this list has a non-empty value.
// This may be used to include null fields in Patch requests.
nullFields []string
}
type Strategy struct {
OnDemandCount *int `json:"onDemandCount,omitempty"`
DrainingTimeout *int `json:"drainingTimeout,omitempty"`
SpotPercentage *int `json:"spotPercentage,omitempty"`
FallbackToOnDemand *bool `json:"fallbackToOd,omitempty"`
forceSendFields []string
nullFields []string
}
type Capacity struct {
Minimum *int `json:"minimum,omitempty"`
Maximum *int `json:"maximum,omitempty"`
Target *int `json:"target,omitempty"`
forceSendFields []string
nullFields []string
}
type Compute struct {
VMSizes *VMSizes `json:"vmSizes,omitempty"`
OS *string `json:"os,omitempty"`
LaunchSpecification *LaunchSpecification `json:"launchSpecification,omitempty"`
forceSendFields []string
nullFields []string
}
type VMSizes struct {
OnDemandSizes []string `json:"odSizes,omitempty"`
SpotSizes []string `json:"spotSizes,omitempty"`
forceSendFields []string
nullFields []string
}
type LaunchSpecification struct {
Image *Image `json:"image,omitempty"`
Network *Network `json:"network,omitempty"`
Login *Login `json:"login,omitempty"`
forceSendFields []string
nullFields []string
}
type Image struct {
MarketPlace *MarketPlaceImage `json:"marketplace,omitempty"`
Custom *CustomImage `json:"custom,omitempty"`
forceSendFields []string
nullFields []string
}
type MarketPlaceImage struct {
Publisher *string `json:"publisher,omitempty"`
Offer *string `json:"offer,omitempty"`
SKU *string `json:"sku,omitempty"`
Version *string `json:"version,omitempty"`
forceSendFields []string
nullFields []string
}
type CustomImage struct {
ResourceGroupName *string `json:"resourceGroupName,omitempty"`
Name *string `json:"name,omitempty"`
forceSendFields []string
nullFields []string
}
type Network struct {
VirtualNetworkName *string `json:"virtualNetworkName,omitempty"`
ResourceGroupName *string `json:"resourceGroupName,omitempty"`
NetworkInterfaces []*NetworkInterface `json:"networkInterfaces,omitempty"`
forceSendFields []string
nullFields []string
}
type NetworkInterface struct {
SubnetName *string `json:"subnetName,omitempty"`
AssignPublicIP *bool `json:"assignPublicIp,omitempty"`
IsPrimary *bool `json:"isPrimary,omitempty"`
AdditionalIPConfigs []*AdditionalIPConfig `json:"additionalIpConfigurations,omitempty"`
forceSendFields []string
nullFields []string
}
type AdditionalIPConfig struct {
Name *string `json:"name,omitempty"`
PrivateIPAddressVersion *string `json:"privateIpAddressVersion,omitempty"`
forceSendFields []string
nullFields []string
}
type Login struct {
UserName *string `json:"userName,omitempty"`
SSHPublicKey *string `json:"sshPublicKey,omitempty"`
Password *string `json:"password,omitempty"`
forceSendFields []string
nullFields []string
}
type CreateGroupInput struct {
Group *Group `json:"group,omitempty"`
}
type CreateGroupOutput struct {
Group *Group `json:"group,omitempty"`
}
type ReadGroupInput struct {
GroupID *string `json:"groupId,omitempty"`
}
type ReadGroupOutput struct {
Group *Group `json:"group,omitempty"`
}
type UpdateGroupInput struct {
Group *Group `json:"group,omitempty"`
}
type UpdateGroupOutput struct {
Group *Group `json:"group,omitempty"`
}
type DeleteGroupInput struct {
GroupID *string `json:"groupId,omitempty"`
}
type DeleteGroupOutput struct{}
type ListGroupsInput struct{}
type ListGroupsOutput struct {
Groups []*Group `json:"groups,omitempty"`
}
// region Unmarshallers
func groupFromJSON(in []byte) (*Group, error) {
b := new(Group)
if err := json.Unmarshal(in, b); err != nil {
return nil, err
}
return b, nil
}
func groupsFromJSON(in []byte) ([]*Group, error) {
var rw client.Response
if err := json.Unmarshal(in, &rw); err != nil {
return nil, err
}
out := make([]*Group, len(rw.Response.Items))
if len(out) == 0 {
return out, nil
}
for i, rb := range rw.Response.Items {
b, err := groupFromJSON(rb)
if err != nil {
return nil, err
}
out[i] = b
}
return out, nil
}
func groupsFromHttpResponse(resp *http.Response) ([]*Group, error) {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return groupsFromJSON(body)
}
// endregion
// region API requests
func (s *ServiceOp) List(ctx context.Context, input *ListGroupsInput) (*ListGroupsOutput, error) {
r := client.NewRequest(http.MethodGet, "/azure/compute/group")
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
gs, err := groupsFromHttpResponse(resp)
if err != nil {
return nil, err
}
return &ListGroupsOutput{Groups: gs}, nil
}
func (s *ServiceOp) Create(ctx context.Context, input *CreateGroupInput) (*CreateGroupOutput, error) {
r := client.NewRequest(http.MethodPost, "/azure/compute/group")
r.Obj = input
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
gs, err := groupsFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(CreateGroupOutput)
if len(gs) > 0 {
output.Group = gs[0]
}
return output, nil
}
func (s *ServiceOp) Read(ctx context.Context, input *ReadGroupInput) (*ReadGroupOutput, error) {
path, err := uritemplates.Expand("/azure/compute/group/{groupId}", uritemplates.Values{
"groupId": spotinst.StringValue(input.GroupID),
})
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()
gs, err := groupsFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(ReadGroupOutput)
if len(gs) > 0 {
output.Group = gs[0]
}
return output, nil
}
func (s *ServiceOp) Update(ctx context.Context, input *UpdateGroupInput) (*UpdateGroupOutput, error) {
path, err := uritemplates.Expand("/azure/compute/group/{groupId}", uritemplates.Values{
"groupId": spotinst.StringValue(input.Group.ID),
})
if err != nil {
return nil, err
}
// We do NOT need the ID anymore, so let's drop it.
input.Group.ID = 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()
gs, err := groupsFromHttpResponse(resp)
if err != nil {
return nil, err
}
output := new(UpdateGroupOutput)
if len(gs) > 0 {
output.Group = gs[0]
}
return output, nil
}
func (s *ServiceOp) Delete(ctx context.Context, input *DeleteGroupInput) (*DeleteGroupOutput, error) {
path, err := uritemplates.Expand("/azure/compute/group/{groupId}", uritemplates.Values{
"groupId": spotinst.StringValue(input.GroupID),
})
if err != nil {
return nil, err
}
r := client.NewRequest(http.MethodDelete, path)
resp, err := client.RequireOK(s.Client.Do(ctx, r))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return &DeleteGroupOutput{}, nil
}
// endregion
// region Group
func (o Group) MarshalJSON() ([]byte, error) {
type noMethod Group
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Group) SetId(v *string) *Group {
if o.ID = v; o.ID == nil {
o.nullFields = append(o.nullFields, "ID")
}
return o
}
func (o *Group) SetName(v *string) *Group {
if o.Name = v; o.Name == nil {
o.nullFields = append(o.nullFields, "Name")
}
return o
}
func (o *Group) SetResourceGroupName(v *string) *Group {
if o.ResourceGroupName = v; o.ResourceGroupName == nil {
o.nullFields = append(o.nullFields, "ResourceGroupName")
}
return o
}
func (o *Group) SetCapacity(v *Capacity) *Group {
if o.Capacity = v; o.Capacity == nil {
o.nullFields = append(o.nullFields, "Capacity")
}
return o
}
func (o *Group) SetCompute(v *Compute) *Group {
if o.Compute = v; o.Compute == nil {
o.nullFields = append(o.nullFields, "Compute")
}
return o
}
func (o *Group) SetStrategy(v *Strategy) *Group {
if o.Strategy = v; o.Strategy == nil {
o.nullFields = append(o.nullFields, "Strategy")
}
return o
}
func (o *Group) SetRegion(v *string) *Group {
if o.Region = v; o.Region == nil {
o.nullFields = append(o.nullFields, "Region")
}
return o
}
// endregion
// region Strategy
func (o Strategy) MarshalJSON() ([]byte, error) {
type noMethod Strategy
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Strategy) SetOnDemandCount(v *int) *Strategy {
if o.OnDemandCount = v; o.OnDemandCount == nil {
o.nullFields = append(o.nullFields, "OnDemandCount")
}
return o
}
func (o *Strategy) SetDrainingTimeout(v *int) *Strategy {
if o.DrainingTimeout = v; o.DrainingTimeout == nil {
o.nullFields = append(o.nullFields, "DrainingTimeout")
}
return o
}
func (o *Strategy) SetSpotPercentage(v *int) *Strategy {
if o.SpotPercentage = v; o.SpotPercentage == nil {
o.nullFields = append(o.nullFields, "SpotPercentage")
}
return o
}
func (o *Strategy) SetFallbackToOnDemand(v *bool) *Strategy {
if o.FallbackToOnDemand = v; o.FallbackToOnDemand == nil {
o.nullFields = append(o.nullFields, "FallbackToOnDemand")
}
return o
}
// endregion
// region Capacity
func (o Capacity) MarshalJSON() ([]byte, error) {
type noMethod Capacity
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Capacity) SetMinimum(v *int) *Capacity {
if o.Minimum = v; o.Minimum == nil {
o.nullFields = append(o.nullFields, "Minimum")
}
return o
}
func (o *Capacity) SetMaximum(v *int) *Capacity {
if o.Maximum = v; o.Maximum == nil {
o.nullFields = append(o.nullFields, "Maximum")
}
return o
}
func (o *Capacity) SetTarget(v *int) *Capacity {
if o.Target = v; o.Target == nil {
o.nullFields = append(o.nullFields, "Target")
}
return o
}
// endregion
// region Compute
func (o Compute) MarshalJSON() ([]byte, error) {
type noMethod Compute
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Compute) SetVMSizes(v *VMSizes) *Compute {
if o.VMSizes = v; o.VMSizes == nil {
o.nullFields = append(o.nullFields, "VMSizes")
}
return o
}
func (o *Compute) SetOS(v *string) *Compute {
if o.OS = v; o.OS == nil {
o.nullFields = append(o.nullFields, "OS")
}
return o
}
func (o *Compute) SetLaunchSpecification(v *LaunchSpecification) *Compute {
if o.LaunchSpecification = v; o.LaunchSpecification == nil {
o.nullFields = append(o.nullFields, "LaunchSpecification")
}
return o
}
// endregion
// region VMSize
func (o VMSizes) MarshalJSON() ([]byte, error) {
type noMethod VMSizes
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *VMSizes) SetOnDemandSizes(v []string) *VMSizes {
if o.OnDemandSizes = v; o.OnDemandSizes == nil {
o.nullFields = append(o.nullFields, "OnDemandSizes")
}
return o
}
func (o *VMSizes) SetSpotSizes(v []string) *VMSizes {
if o.SpotSizes = v; o.SpotSizes == nil {
o.nullFields = append(o.nullFields, "SpotSizes")
}
return o
}
// endregion
// region LaunchSpecification
func (o LaunchSpecification) MarshalJSON() ([]byte, error) {
type noMethod LaunchSpecification
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *LaunchSpecification) SetImage(v *Image) *LaunchSpecification {
if o.Image = v; o.Image == nil {
o.nullFields = append(o.nullFields, "Image")
}
return o
}
func (o *LaunchSpecification) SetNetwork(v *Network) *LaunchSpecification {
if o.Network = v; o.Network == nil {
o.nullFields = append(o.nullFields, "Network")
}
return o
}
func (o *LaunchSpecification) SetLogin(v *Login) *LaunchSpecification {
if o.Login = v; o.Login == nil {
o.nullFields = append(o.nullFields, "Login")
}
return o
}
// endregion
// region Image
func (o Image) MarshalJSON() ([]byte, error) {
type noMethod Image
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Image) SetMarketPlaceImage(v *MarketPlaceImage) *Image {
if o.MarketPlace = v; o.MarketPlace == nil {
o.nullFields = append(o.nullFields, "MarketPlace")
}
return o
}
func (o *Image) SetCustom(v *CustomImage) *Image {
if o.Custom = v; o.Custom == nil {
o.nullFields = append(o.nullFields, "Custom")
}
return o
}
// endregion
// region MarketPlaceImage
func (o MarketPlaceImage) MarshalJSON() ([]byte, error) {
type noMethod MarketPlaceImage
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *MarketPlaceImage) SetPublisher(v *string) *MarketPlaceImage {
if o.Publisher = v; o.Publisher == nil {
o.nullFields = append(o.nullFields, "Publisher")
}
return o
}
func (o *MarketPlaceImage) SetOffer(v *string) *MarketPlaceImage {
if o.Offer = v; o.Offer == nil {
o.nullFields = append(o.nullFields, "Offer")
}
return o
}
func (o *MarketPlaceImage) SetSKU(v *string) *MarketPlaceImage {
if o.SKU = v; o.SKU == nil {
o.nullFields = append(o.nullFields, "SKU")
}
return o
}
func (o *MarketPlaceImage) SetVersion(v *string) *MarketPlaceImage {
if o.Version = v; o.Version == nil {
o.nullFields = append(o.nullFields, "Version")
}
return o
}
// endregion
// region CustomImage
func (o CustomImage) MarshalJSON() ([]byte, error) {
type noMethod CustomImage
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *CustomImage) SetResourceGroupName(v *string) *CustomImage {
if o.ResourceGroupName = v; o.ResourceGroupName == nil {
o.nullFields = append(o.nullFields, "ResourceGroupName")
}
return o
}
func (o *CustomImage) SetName(v *string) *CustomImage {
if o.Name = v; o.Name == nil {
o.nullFields = append(o.nullFields, "Name")
}
return o
}
// endregion
// region Network
func (o Network) MarshalJSON() ([]byte, error) {
type noMethod Network
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Network) SetVirtualNetworkName(v *string) *Network {
if o.VirtualNetworkName = v; o.VirtualNetworkName == nil {
o.nullFields = append(o.nullFields, "VirtualNetworkName")
}
return o
}
func (o *Network) SetResourceGroupName(v *string) *Network {
if o.ResourceGroupName = v; o.ResourceGroupName == nil {
o.nullFields = append(o.nullFields, "ResourceGroupName")
}
return o
}
func (o *Network) SetNetworkInterfaces(v []*NetworkInterface) *Network {
if o.NetworkInterfaces = v; o.NetworkInterfaces == nil {
o.nullFields = append(o.nullFields, "NetworkInterfaces")
}
return o
}
// endregion
// region NetworkInterface
func (o NetworkInterface) MarshalJSON() ([]byte, error) {
type noMethod NetworkInterface
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *NetworkInterface) SetSubnetName(v *string) *NetworkInterface {
if o.SubnetName = v; o.SubnetName == nil {
o.nullFields = append(o.nullFields, "SubnetName")
}
return o
}
func (o *NetworkInterface) SetAdditionalIPConfigs(v []*AdditionalIPConfig) *NetworkInterface {
if o.AdditionalIPConfigs = v; o.AdditionalIPConfigs == nil {
o.nullFields = append(o.nullFields, "AdditionalIPConfigs")
}
return o
}
func (o *NetworkInterface) SetAssignPublicIP(v *bool) *NetworkInterface {
if o.AssignPublicIP = v; o.AssignPublicIP == nil {
o.nullFields = append(o.nullFields, "AssignPublicIP")
}
return o
}
func (o *NetworkInterface) SetIsPrimary(v *bool) *NetworkInterface {
if o.IsPrimary = v; o.IsPrimary == nil {
o.nullFields = append(o.nullFields, "IsPrimary")
}
return o
}
// endregion
// region AdditionalIPConfig
func (o AdditionalIPConfig) MarshalJSON() ([]byte, error) {
type noMethod AdditionalIPConfig
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
// SetName sets the name
func (o *AdditionalIPConfig) SetName(v *string) *AdditionalIPConfig {
if o.Name = v; o.Name == nil {
o.nullFields = append(o.nullFields, "Name")
}
return o
}
// SetPrivateIPAddressVersion sets the ip address version
func (o *AdditionalIPConfig) SetPrivateIPAddressVersion(v *string) *AdditionalIPConfig {
if o.PrivateIPAddressVersion = v; o.PrivateIPAddressVersion == nil {
o.nullFields = append(o.nullFields, "PrivateIPAddressVersion")
}
return o
}
// endregion
// region Login
func (o Login) MarshalJSON() ([]byte, error) {
type noMethod Login
raw := noMethod(o)
return jsonutil.MarshalJSON(raw, o.forceSendFields, o.nullFields)
}
func (o *Login) SetUserName(v *string) *Login {
if o.UserName = v; o.UserName == nil {
o.nullFields = append(o.nullFields, "UserName")
}
return o
}
func (o *Login) SetSSHPublicKey(v *string) *Login {
if o.SSHPublicKey = v; o.SSHPublicKey == nil {
o.nullFields = append(o.nullFields, "SSHPublicKey")
}
return o
}
func (o *Login) SetPassword(v *string) *Login {
if o.Password = v; o.Password == nil {
o.nullFields = append(o.nullFields, "Password")
}
return o
}
// endregion

View File

@ -0,0 +1,36 @@
package v3
import (
"context"
"github.com/spotinst/spotinst-sdk-go/spotinst"
"github.com/spotinst/spotinst-sdk-go/spotinst/client"
"github.com/spotinst/spotinst-sdk-go/spotinst/session"
)
// Service provides the API operation methods for making requests to endpoints
// of the Spotinst API. See this package's package overview docs for details on
// the service.
type Service interface {
Create(context.Context, *CreateGroupInput) (*CreateGroupOutput, error)
Read(context.Context, *ReadGroupInput) (*ReadGroupOutput, error)
Update(context.Context, *UpdateGroupInput) (*UpdateGroupOutput, error)
Delete(context.Context, *DeleteGroupInput) (*DeleteGroupOutput, error)
List(context.Context, *ListGroupsInput) (*ListGroupsOutput, error)
}
type ServiceOp struct {
Client *client.Client
}
var _ Service = &ServiceOp{}
func New(sess *session.Session, cfgs ...*spotinst.Config) *ServiceOp {
cfg := &spotinst.Config{}
cfg.Merge(sess.Config)
cfg.Merge(cfgs...)
return &ServiceOp{
Client: client.New(sess.Config),
}
}

View File

@ -26,6 +26,7 @@ type ECSLaunchSpec struct {
BlockDeviceMappings []*ECSBlockDeviceMapping `json:"blockDeviceMappings,omitempty"` BlockDeviceMappings []*ECSBlockDeviceMapping `json:"blockDeviceMappings,omitempty"`
Tags []*Tag `json:"tags,omitempty"` Tags []*Tag `json:"tags,omitempty"`
InstanceTypes []string `json:"instanceTypes,omitempty"` InstanceTypes []string `json:"instanceTypes,omitempty"`
RestrictScaleDown *bool `json:"restrictScaleDown,omitempty"`
// Read-only fields. // Read-only fields.
CreatedAt *time.Time `json:"createdAt,omitempty"` CreatedAt *time.Time `json:"createdAt,omitempty"`
@ -397,6 +398,13 @@ func (o *ECSLaunchSpec) SetInstanceTypes(v []string) *ECSLaunchSpec {
return o return o
} }
func (o *ECSLaunchSpec) SetRestrictScaleDown(v *bool) *ECSLaunchSpec {
if o.RestrictScaleDown = v; o.RestrictScaleDown == nil {
o.nullFields = append(o.nullFields, "RestrictScaleDown")
}
return o
}
// endregion // endregion
// region Attributes // region Attributes

View File

@ -13,13 +13,14 @@ import (
) )
type LaunchSpec struct { type LaunchSpec struct {
ID *string `json:"id,omitempty"` ID *string `json:"id,omitempty"`
OceanID *string `json:"oceanId,omitempty"` OceanID *string `json:"oceanId,omitempty"`
SourceImage *string `json:"sourceImage,omitempty"` SourceImage *string `json:"sourceImage,omitempty"`
Metadata []*Metadata `json:"metadata,omitempty"` Metadata []*Metadata `json:"metadata,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"` AutoScale *AutoScale `json:"autoScale,omitempty"`
RestrictScaleDown *bool `json:"restrictScaleDown,omitempty"`
// forceSendFields is a list of field names (e.g. "Keys") to // forceSendFields is a list of field names (e.g. "Keys") to
// unconditionally include in API requests. By default, fields with // unconditionally include in API requests. By default, fields with
@ -356,6 +357,13 @@ func (o *LaunchSpec) SetAutoScale(v *AutoScale) *LaunchSpec {
return o return o
} }
func (o *LaunchSpec) SetRestrictScaleDown(v *bool) *LaunchSpec {
if o.RestrictScaleDown = v; o.RestrictScaleDown == nil {
o.nullFields = append(o.nullFields, "RestrictScaleDown")
}
return o
}
// endregion // endregion
// region Label // region Label

View File

@ -14,7 +14,7 @@ go_library(
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 = [ deps = [
"//vendor/github.com/go-ini/ini:go_default_library",
"//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/featureflag:go_default_library", "//vendor/github.com/spotinst/spotinst-sdk-go/spotinst/featureflag:go_default_library",
"//vendor/gopkg.in/ini.v1:go_default_library",
], ],
) )

View File

@ -8,7 +8,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"github.com/go-ini/ini" "gopkg.in/ini.v1"
) )
const ( const (

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.76.0" const SDKVersion = "1.80.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"

View File

@ -6,6 +6,7 @@ go_library(
"assertion_compare.go", "assertion_compare.go",
"assertion_format.go", "assertion_format.go",
"assertion_forward.go", "assertion_forward.go",
"assertion_order.go",
"assertions.go", "assertions.go",
"doc.go", "doc.go",
"errors.go", "errors.go",

View File

@ -13,12 +13,42 @@ const (
compareGreater compareGreater
) )
var (
intType = reflect.TypeOf(int(1))
int8Type = reflect.TypeOf(int8(1))
int16Type = reflect.TypeOf(int16(1))
int32Type = reflect.TypeOf(int32(1))
int64Type = reflect.TypeOf(int64(1))
uintType = reflect.TypeOf(uint(1))
uint8Type = reflect.TypeOf(uint8(1))
uint16Type = reflect.TypeOf(uint16(1))
uint32Type = reflect.TypeOf(uint32(1))
uint64Type = reflect.TypeOf(uint64(1))
float32Type = reflect.TypeOf(float32(1))
float64Type = reflect.TypeOf(float64(1))
stringType = reflect.TypeOf("")
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
obj1Value := reflect.ValueOf(obj1)
obj2Value := reflect.ValueOf(obj2)
// throughout this switch we try and avoid calling .Convert() if possible,
// as this has a pretty big performance impact
switch kind { switch kind {
case reflect.Int: case reflect.Int:
{ {
intobj1 := obj1.(int) intobj1, ok := obj1.(int)
intobj2 := obj2.(int) if !ok {
intobj1 = obj1Value.Convert(intType).Interface().(int)
}
intobj2, ok := obj2.(int)
if !ok {
intobj2 = obj2Value.Convert(intType).Interface().(int)
}
if intobj1 > intobj2 { if intobj1 > intobj2 {
return compareGreater, true return compareGreater, true
} }
@ -31,8 +61,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int8: case reflect.Int8:
{ {
int8obj1 := obj1.(int8) int8obj1, ok := obj1.(int8)
int8obj2 := obj2.(int8) if !ok {
int8obj1 = obj1Value.Convert(int8Type).Interface().(int8)
}
int8obj2, ok := obj2.(int8)
if !ok {
int8obj2 = obj2Value.Convert(int8Type).Interface().(int8)
}
if int8obj1 > int8obj2 { if int8obj1 > int8obj2 {
return compareGreater, true return compareGreater, true
} }
@ -45,8 +81,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int16: case reflect.Int16:
{ {
int16obj1 := obj1.(int16) int16obj1, ok := obj1.(int16)
int16obj2 := obj2.(int16) if !ok {
int16obj1 = obj1Value.Convert(int16Type).Interface().(int16)
}
int16obj2, ok := obj2.(int16)
if !ok {
int16obj2 = obj2Value.Convert(int16Type).Interface().(int16)
}
if int16obj1 > int16obj2 { if int16obj1 > int16obj2 {
return compareGreater, true return compareGreater, true
} }
@ -59,8 +101,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int32: case reflect.Int32:
{ {
int32obj1 := obj1.(int32) int32obj1, ok := obj1.(int32)
int32obj2 := obj2.(int32) if !ok {
int32obj1 = obj1Value.Convert(int32Type).Interface().(int32)
}
int32obj2, ok := obj2.(int32)
if !ok {
int32obj2 = obj2Value.Convert(int32Type).Interface().(int32)
}
if int32obj1 > int32obj2 { if int32obj1 > int32obj2 {
return compareGreater, true return compareGreater, true
} }
@ -73,8 +121,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Int64: case reflect.Int64:
{ {
int64obj1 := obj1.(int64) int64obj1, ok := obj1.(int64)
int64obj2 := obj2.(int64) if !ok {
int64obj1 = obj1Value.Convert(int64Type).Interface().(int64)
}
int64obj2, ok := obj2.(int64)
if !ok {
int64obj2 = obj2Value.Convert(int64Type).Interface().(int64)
}
if int64obj1 > int64obj2 { if int64obj1 > int64obj2 {
return compareGreater, true return compareGreater, true
} }
@ -87,8 +141,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint: case reflect.Uint:
{ {
uintobj1 := obj1.(uint) uintobj1, ok := obj1.(uint)
uintobj2 := obj2.(uint) if !ok {
uintobj1 = obj1Value.Convert(uintType).Interface().(uint)
}
uintobj2, ok := obj2.(uint)
if !ok {
uintobj2 = obj2Value.Convert(uintType).Interface().(uint)
}
if uintobj1 > uintobj2 { if uintobj1 > uintobj2 {
return compareGreater, true return compareGreater, true
} }
@ -101,8 +161,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint8: case reflect.Uint8:
{ {
uint8obj1 := obj1.(uint8) uint8obj1, ok := obj1.(uint8)
uint8obj2 := obj2.(uint8) if !ok {
uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8)
}
uint8obj2, ok := obj2.(uint8)
if !ok {
uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8)
}
if uint8obj1 > uint8obj2 { if uint8obj1 > uint8obj2 {
return compareGreater, true return compareGreater, true
} }
@ -115,8 +181,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint16: case reflect.Uint16:
{ {
uint16obj1 := obj1.(uint16) uint16obj1, ok := obj1.(uint16)
uint16obj2 := obj2.(uint16) if !ok {
uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16)
}
uint16obj2, ok := obj2.(uint16)
if !ok {
uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16)
}
if uint16obj1 > uint16obj2 { if uint16obj1 > uint16obj2 {
return compareGreater, true return compareGreater, true
} }
@ -129,8 +201,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint32: case reflect.Uint32:
{ {
uint32obj1 := obj1.(uint32) uint32obj1, ok := obj1.(uint32)
uint32obj2 := obj2.(uint32) if !ok {
uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32)
}
uint32obj2, ok := obj2.(uint32)
if !ok {
uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32)
}
if uint32obj1 > uint32obj2 { if uint32obj1 > uint32obj2 {
return compareGreater, true return compareGreater, true
} }
@ -143,8 +221,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Uint64: case reflect.Uint64:
{ {
uint64obj1 := obj1.(uint64) uint64obj1, ok := obj1.(uint64)
uint64obj2 := obj2.(uint64) if !ok {
uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64)
}
uint64obj2, ok := obj2.(uint64)
if !ok {
uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64)
}
if uint64obj1 > uint64obj2 { if uint64obj1 > uint64obj2 {
return compareGreater, true return compareGreater, true
} }
@ -157,8 +241,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Float32: case reflect.Float32:
{ {
float32obj1 := obj1.(float32) float32obj1, ok := obj1.(float32)
float32obj2 := obj2.(float32) if !ok {
float32obj1 = obj1Value.Convert(float32Type).Interface().(float32)
}
float32obj2, ok := obj2.(float32)
if !ok {
float32obj2 = obj2Value.Convert(float32Type).Interface().(float32)
}
if float32obj1 > float32obj2 { if float32obj1 > float32obj2 {
return compareGreater, true return compareGreater, true
} }
@ -171,8 +261,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.Float64: case reflect.Float64:
{ {
float64obj1 := obj1.(float64) float64obj1, ok := obj1.(float64)
float64obj2 := obj2.(float64) if !ok {
float64obj1 = obj1Value.Convert(float64Type).Interface().(float64)
}
float64obj2, ok := obj2.(float64)
if !ok {
float64obj2 = obj2Value.Convert(float64Type).Interface().(float64)
}
if float64obj1 > float64obj2 { if float64obj1 > float64obj2 {
return compareGreater, true return compareGreater, true
} }
@ -185,8 +281,14 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
} }
case reflect.String: case reflect.String:
{ {
stringobj1 := obj1.(string) stringobj1, ok := obj1.(string)
stringobj2 := obj2.(string) if !ok {
stringobj1 = obj1Value.Convert(stringType).Interface().(string)
}
stringobj2, ok := obj2.(string)
if !ok {
stringobj2 = obj2Value.Convert(stringType).Interface().(string)
}
if stringobj1 > stringobj2 { if stringobj1 > stringobj2 {
return compareGreater, true return compareGreater, true
} }
@ -240,6 +342,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
} }
// Positive asserts that the specified element is positive
//
// assert.Positive(t, 1)
// assert.Positive(t, 1.23)
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs)
}
// Negative asserts that the specified element is negative
//
// assert.Negative(t, -1)
// assert.Negative(t, -1.23)
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
zero := reflect.Zero(reflect.TypeOf(e))
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs)
}
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {
h.Helper() h.Helper()

View File

@ -114,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
return Error(t, err, append([]interface{}{msg}, args...)...) return Error(t, err, append([]interface{}{msg}, args...)...)
} }
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
}
// Eventuallyf asserts that given condition will be met in waitFor time, // Eventuallyf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick. // periodically checking target function each tick.
// //
@ -321,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
} }
// IsDecreasingf asserts that the collection is decreasing
//
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsDecreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsIncreasingf asserts that the collection is increasing
//
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsIncreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...)
}
// IsTypef asserts that the specified objects are of the same type. // IsTypef asserts that the specified objects are of the same type.
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {
@ -375,6 +441,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
} }
// Negativef asserts that the specified element is negative
//
// assert.Negativef(t, -1, "error message %s", "formatted")
// assert.Negativef(t, -1.23, "error message %s", "formatted")
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Negative(t, e, append([]interface{}{msg}, args...)...)
}
// Neverf asserts that the given condition doesn't satisfy in waitFor time, // Neverf asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -476,6 +553,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
} }
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
}
// NotNilf asserts that the specified object is not nil. // NotNilf asserts that the specified object is not nil.
// //
// assert.NotNilf(t, err, "error message %s", "formatted") // assert.NotNilf(t, err, "error message %s", "formatted")
@ -572,6 +658,17 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
} }
// Positivef asserts that the specified element is positive
//
// assert.Positivef(t, 1, "error message %s", "formatted")
// assert.Positivef(t, 1.23, "error message %s", "formatted")
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Positive(t, e, append([]interface{}{msg}, args...)...)
}
// Regexpf asserts that a specified regexp matches a string. // Regexpf asserts that a specified regexp matches a string.
// //
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")

View File

@ -204,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
return Error(a.t, err, msgAndArgs...) return Error(a.t, err, msgAndArgs...)
} }
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorAs(a.t, err, target, msgAndArgs...)
}
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorAsf(a.t, err, target, msg, args...)
}
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorIs(a.t, err, target, msgAndArgs...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return ErrorIsf(a.t, err, target, msg, args...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`). // Errorf asserts that a function returned an error (i.e. not `nil`).
// //
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
@ -631,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) return InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
} }
// IsDecreasing asserts that the collection is decreasing
//
// a.IsDecreasing([]int{2, 1, 0})
// a.IsDecreasing([]float{2, 1})
// a.IsDecreasing([]string{"b", "a"})
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsDecreasing(a.t, object, msgAndArgs...)
}
// IsDecreasingf asserts that the collection is decreasing
//
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted")
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsDecreasingf(a.t, object, msg, args...)
}
// IsIncreasing asserts that the collection is increasing
//
// a.IsIncreasing([]int{1, 2, 3})
// a.IsIncreasing([]float{1, 2})
// a.IsIncreasing([]string{"a", "b"})
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsIncreasing(a.t, object, msgAndArgs...)
}
// IsIncreasingf asserts that the collection is increasing
//
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted")
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsIncreasingf(a.t, object, msg, args...)
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// a.IsNonDecreasing([]int{1, 1, 2})
// a.IsNonDecreasing([]float{1, 2})
// a.IsNonDecreasing([]string{"a", "b"})
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonDecreasing(a.t, object, msgAndArgs...)
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonDecreasingf(a.t, object, msg, args...)
}
// IsNonIncreasing asserts that the collection is not increasing
//
// a.IsNonIncreasing([]int{2, 1, 1})
// a.IsNonIncreasing([]float{2, 1})
// a.IsNonIncreasing([]string{"b", "a"})
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonIncreasing(a.t, object, msgAndArgs...)
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return IsNonIncreasingf(a.t, object, msg, args...)
}
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok { if h, ok := a.t.(tHelper); ok {
@ -739,6 +871,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
return Lessf(a.t, e1, e2, msg, args...) return Lessf(a.t, e1, e2, msg, args...)
} }
// Negative asserts that the specified element is negative
//
// a.Negative(-1)
// a.Negative(-1.23)
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Negative(a.t, e, msgAndArgs...)
}
// Negativef asserts that the specified element is negative
//
// a.Negativef(-1, "error message %s", "formatted")
// a.Negativef(-1.23, "error message %s", "formatted")
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Negativef(a.t, e, msg, args...)
}
// Never asserts that the given condition doesn't satisfy in waitFor time, // Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -941,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
return NotEqualf(a.t, expected, actual, msg, args...) return NotEqualf(a.t, expected, actual, msg, args...)
} }
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotErrorIs(a.t, err, target, msgAndArgs...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotErrorIsf(a.t, err, target, msg, args...)
}
// NotNil asserts that the specified object is not nil. // NotNil asserts that the specified object is not nil.
// //
// a.NotNil(err) // a.NotNil(err)
@ -1133,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b
return Panicsf(a.t, f, msg, args...) return Panicsf(a.t, f, msg, args...)
} }
// Positive asserts that the specified element is positive
//
// a.Positive(1)
// a.Positive(1.23)
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Positive(a.t, e, msgAndArgs...)
}
// Positivef asserts that the specified element is positive
//
// a.Positivef(1, "error message %s", "formatted")
// a.Positivef(1.23, "error message %s", "formatted")
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Positivef(a.t, e, msg, args...)
}
// Regexp asserts that a specified regexp matches a string. // Regexp asserts that a specified regexp matches a string.
// //
// a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp(regexp.MustCompile("start"), "it's starting")

View File

@ -0,0 +1,81 @@
package assert
import (
"fmt"
"reflect"
)
// isOrdered checks that collection contains orderable elements.
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
objKind := reflect.TypeOf(object).Kind()
if objKind != reflect.Slice && objKind != reflect.Array {
return false
}
objValue := reflect.ValueOf(object)
objLen := objValue.Len()
if objLen <= 1 {
return true
}
value := objValue.Index(0)
valueInterface := value.Interface()
firstValueKind := value.Kind()
for i := 1; i < objLen; i++ {
prevValue := value
prevValueInterface := valueInterface
value = objValue.Index(i)
valueInterface = value.Interface()
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...)
}
if !containsValue(allowedComparesResults, compareResult) {
return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...)
}
}
return true
}
// IsIncreasing asserts that the collection is increasing
//
// assert.IsIncreasing(t, []int{1, 2, 3})
// assert.IsIncreasing(t, []float{1, 2})
// assert.IsIncreasing(t, []string{"a", "b"})
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
}
// IsNonIncreasing asserts that the collection is not increasing
//
// assert.IsNonIncreasing(t, []int{2, 1, 1})
// assert.IsNonIncreasing(t, []float{2, 1})
// assert.IsNonIncreasing(t, []string{"b", "a"})
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
}
// IsDecreasing asserts that the collection is decreasing
//
// assert.IsDecreasing(t, []int{2, 1, 0})
// assert.IsDecreasing(t, []float{2, 1})
// assert.IsDecreasing(t, []string{"b", "a"})
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// assert.IsNonDecreasing(t, []int{1, 1, 2})
// assert.IsNonDecreasing(t, []float{1, 2})
// assert.IsNonDecreasing(t, []string{"a", "b"})
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
}

View File

@ -172,8 +172,8 @@ func isTest(name, prefix string) bool {
if len(name) == len(prefix) { // "Test" is ok if len(name) == len(prefix) { // "Test" is ok
return true return true
} }
rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) r, _ := utf8.DecodeRuneInString(name[len(prefix):])
return !unicode.IsLower(rune) return !unicode.IsLower(r)
} }
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
@ -1622,6 +1622,7 @@ var spewConfig = spew.ConfigState{
DisableCapacities: true, DisableCapacities: true,
SortKeys: true, SortKeys: true,
DisableMethods: true, DisableMethods: true,
MaxDepth: 10,
} }
type tHelper interface { type tHelper interface {
@ -1693,3 +1694,81 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
} }
} }
} }
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if errors.Is(err, target) {
return true
}
var expectedText string
if target != nil {
expectedText = target.Error()
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
"expected: %q\n"+
"in chain: %s", expectedText, chain,
), msgAndArgs...)
}
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !errors.Is(err, target) {
return true
}
var expectedText string
if target != nil {
expectedText = target.Error()
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %q\n"+
"in chain: %s", expectedText, chain,
), msgAndArgs...)
}
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if errors.As(err, target) {
return true
}
chain := buildErrorChainString(err)
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
"expected: %q\n"+
"in chain: %s", target, chain,
), msgAndArgs...)
}
func buildErrorChainString(err error) string {
if err == nil {
return ""
}
e := errors.Unwrap(err)
chain := fmt.Sprintf("%q", err.Error())
for e != nil {
chain += fmt.Sprintf("\n\t%q", e.Error())
e = errors.Unwrap(e)
}
return chain
}

View File

@ -256,6 +256,54 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) {
t.FailNow() t.FailNow()
} }
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorAs(t, err, target, msgAndArgs...) {
return
}
t.FailNow()
}
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorAsf(t, err, target, msg, args...) {
return
}
t.FailNow()
}
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorIs(t, err, target, msgAndArgs...) {
return
}
t.FailNow()
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.ErrorIsf(t, err, target, msg, args...) {
return
}
t.FailNow()
}
// Errorf asserts that a function returned an error (i.e. not `nil`). // Errorf asserts that a function returned an error (i.e. not `nil`).
// //
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
@ -806,6 +854,126 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl
t.FailNow() t.FailNow()
} }
// IsDecreasing asserts that the collection is decreasing
//
// assert.IsDecreasing(t, []int{2, 1, 0})
// assert.IsDecreasing(t, []float{2, 1})
// assert.IsDecreasing(t, []string{"b", "a"})
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsDecreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsDecreasingf asserts that the collection is decreasing
//
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsDecreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsIncreasing asserts that the collection is increasing
//
// assert.IsIncreasing(t, []int{1, 2, 3})
// assert.IsIncreasing(t, []float{1, 2})
// assert.IsIncreasing(t, []string{"a", "b"})
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsIncreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsIncreasingf asserts that the collection is increasing
//
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsIncreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// assert.IsNonDecreasing(t, []int{1, 1, 2})
// assert.IsNonDecreasing(t, []float{1, 2})
// assert.IsNonDecreasing(t, []string{"a", "b"})
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonDecreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonDecreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsNonIncreasing asserts that the collection is not increasing
//
// assert.IsNonIncreasing(t, []int{2, 1, 1})
// assert.IsNonIncreasing(t, []float{2, 1})
// assert.IsNonIncreasing(t, []string{"b", "a"})
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonIncreasing(t, object, msgAndArgs...) {
return
}
t.FailNow()
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.IsNonIncreasingf(t, object, msg, args...) {
return
}
t.FailNow()
}
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok { if h, ok := t.(tHelper); ok {
@ -944,6 +1112,34 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter
t.FailNow() t.FailNow()
} }
// Negative asserts that the specified element is negative
//
// assert.Negative(t, -1)
// assert.Negative(t, -1.23)
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Negative(t, e, msgAndArgs...) {
return
}
t.FailNow()
}
// Negativef asserts that the specified element is negative
//
// assert.Negativef(t, -1, "error message %s", "formatted")
// assert.Negativef(t, -1.23, "error message %s", "formatted")
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Negativef(t, e, msg, args...) {
return
}
t.FailNow()
}
// Never asserts that the given condition doesn't satisfy in waitFor time, // Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -1200,6 +1396,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
t.FailNow() t.FailNow()
} }
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.NotErrorIs(t, err, target, msgAndArgs...) {
return
}
t.FailNow()
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.NotErrorIsf(t, err, target, msg, args...) {
return
}
t.FailNow()
}
// NotNil asserts that the specified object is not nil. // NotNil asserts that the specified object is not nil.
// //
// assert.NotNil(t, err) // assert.NotNil(t, err)
@ -1446,6 +1666,34 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}
t.FailNow() t.FailNow()
} }
// Positive asserts that the specified element is positive
//
// assert.Positive(t, 1)
// assert.Positive(t, 1.23)
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Positive(t, e, msgAndArgs...) {
return
}
t.FailNow()
}
// Positivef asserts that the specified element is positive
//
// assert.Positivef(t, 1, "error message %s", "formatted")
// assert.Positivef(t, 1.23, "error message %s", "formatted")
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if assert.Positivef(t, e, msg, args...) {
return
}
t.FailNow()
}
// Regexp asserts that a specified regexp matches a string. // Regexp asserts that a specified regexp matches a string.
// //
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, regexp.MustCompile("start"), "it's starting")

View File

@ -205,6 +205,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
Error(a.t, err, msgAndArgs...) Error(a.t, err, msgAndArgs...)
} }
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorAs(a.t, err, target, msgAndArgs...)
}
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
// This is a wrapper for errors.As.
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorAsf(a.t, err, target, msg, args...)
}
// ErrorIs asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorIs(a.t, err, target, msgAndArgs...)
}
// ErrorIsf asserts that at least one of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ErrorIsf(a.t, err, target, msg, args...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`). // Errorf asserts that a function returned an error (i.e. not `nil`).
// //
// actualObj, err := SomeFunction() // actualObj, err := SomeFunction()
@ -632,6 +668,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo
InEpsilonf(a.t, expected, actual, epsilon, msg, args...) InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
} }
// IsDecreasing asserts that the collection is decreasing
//
// a.IsDecreasing([]int{2, 1, 0})
// a.IsDecreasing([]float{2, 1})
// a.IsDecreasing([]string{"b", "a"})
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsDecreasing(a.t, object, msgAndArgs...)
}
// IsDecreasingf asserts that the collection is decreasing
//
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted")
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsDecreasingf(a.t, object, msg, args...)
}
// IsIncreasing asserts that the collection is increasing
//
// a.IsIncreasing([]int{1, 2, 3})
// a.IsIncreasing([]float{1, 2})
// a.IsIncreasing([]string{"a", "b"})
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsIncreasing(a.t, object, msgAndArgs...)
}
// IsIncreasingf asserts that the collection is increasing
//
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted")
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsIncreasingf(a.t, object, msg, args...)
}
// IsNonDecreasing asserts that the collection is not decreasing
//
// a.IsNonDecreasing([]int{1, 1, 2})
// a.IsNonDecreasing([]float{1, 2})
// a.IsNonDecreasing([]string{"a", "b"})
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonDecreasing(a.t, object, msgAndArgs...)
}
// IsNonDecreasingf asserts that the collection is not decreasing
//
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted")
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted")
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonDecreasingf(a.t, object, msg, args...)
}
// IsNonIncreasing asserts that the collection is not increasing
//
// a.IsNonIncreasing([]int{2, 1, 1})
// a.IsNonIncreasing([]float{2, 1})
// a.IsNonIncreasing([]string{"b", "a"})
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonIncreasing(a.t, object, msgAndArgs...)
}
// IsNonIncreasingf asserts that the collection is not increasing
//
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted")
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted")
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsNonIncreasingf(a.t, object, msg, args...)
}
// IsType asserts that the specified objects are of the same type. // IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok { if h, ok := a.t.(tHelper); ok {
@ -740,6 +872,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
Lessf(a.t, e1, e2, msg, args...) Lessf(a.t, e1, e2, msg, args...)
} }
// Negative asserts that the specified element is negative
//
// a.Negative(-1)
// a.Negative(-1.23)
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Negative(a.t, e, msgAndArgs...)
}
// Negativef asserts that the specified element is negative
//
// a.Negativef(-1, "error message %s", "formatted")
// a.Negativef(-1.23, "error message %s", "formatted")
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Negativef(a.t, e, msg, args...)
}
// Never asserts that the given condition doesn't satisfy in waitFor time, // Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick. // periodically checking the target function each tick.
// //
@ -942,6 +1096,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str
NotEqualf(a.t, expected, actual, msg, args...) NotEqualf(a.t, expected, actual, msg, args...)
} }
// NotErrorIs asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotErrorIs(a.t, err, target, msgAndArgs...)
}
// NotErrorIsf asserts that at none of the errors in err's chain matches target.
// This is a wrapper for errors.Is.
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotErrorIsf(a.t, err, target, msg, args...)
}
// NotNil asserts that the specified object is not nil. // NotNil asserts that the specified object is not nil.
// //
// a.NotNil(err) // a.NotNil(err)
@ -1134,6 +1306,28 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa
Panicsf(a.t, f, msg, args...) Panicsf(a.t, f, msg, args...)
} }
// Positive asserts that the specified element is positive
//
// a.Positive(1)
// a.Positive(1.23)
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Positive(a.t, e, msgAndArgs...)
}
// Positivef asserts that the specified element is positive
//
// a.Positivef(1, "error message %s", "formatted")
// a.Positivef(1.23, "error message %s", "formatted")
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Positivef(a.t, e, msg, args...)
}
// Regexp asserts that a specified regexp matches a string. // Regexp asserts that a specified regexp matches a string.
// //
// a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp(regexp.MustCompile("start"), "it's starting")

18
vendor/gopkg.in/ini.v1/file.go generated vendored
View File

@ -55,6 +55,9 @@ func newFile(dataSources []dataSource, opts LoadOptions) *File {
if len(opts.KeyValueDelimiterOnWrite) == 0 { if len(opts.KeyValueDelimiterOnWrite) == 0 {
opts.KeyValueDelimiterOnWrite = "=" opts.KeyValueDelimiterOnWrite = "="
} }
if len(opts.ChildSectionDelimiter) == 0 {
opts.ChildSectionDelimiter = "."
}
return &File{ return &File{
BlockMode: true, BlockMode: true,
@ -82,7 +85,7 @@ func (f *File) NewSection(name string) (*Section, error) {
return nil, errors.New("empty section name") return nil, errors.New("empty section name")
} }
if f.options.Insensitive && name != DefaultSection { if (f.options.Insensitive || f.options.InsensitiveSections) && name != DefaultSection {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -144,7 +147,7 @@ func (f *File) SectionsByName(name string) ([]*Section, error) {
if len(name) == 0 { if len(name) == 0 {
name = DefaultSection name = DefaultSection
} }
if f.options.Insensitive { if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -236,7 +239,7 @@ func (f *File) DeleteSectionWithIndex(name string, index int) error {
if len(name) == 0 { if len(name) == 0 {
name = DefaultSection name = DefaultSection
} }
if f.options.Insensitive { if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -299,6 +302,9 @@ func (f *File) Reload() (err error) {
} }
return err return err
} }
if f.options.ShortCircuit {
return nil
}
} }
return nil return nil
} }
@ -347,7 +353,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
} }
} }
if i > 0 || DefaultHeader { if i > 0 || DefaultHeader || (i == 0 && strings.ToUpper(sec.name) != DefaultSection) {
if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
return nil, err return nil, err
} }
@ -451,6 +457,8 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
val = `"""` + val + `"""` val = `"""` + val + `"""`
} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
val = "`" + val + "`" val = "`" + val + "`"
} else if len(strings.TrimSpace(val)) != len(val) {
val = `"` + val + `"`
} }
if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
return nil, err return nil, err
@ -494,7 +502,7 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
// SaveToIndent writes content to file system with given value indention. // SaveToIndent writes content to file system with given value indention.
func (f *File) SaveToIndent(filename, indent string) error { func (f *File) SaveToIndent(filename, indent string) error {
// Note: Because we are truncating with os.Create, // Note: Because we are truncating with os.Create,
// so it's safer to save to a temporary file location and rename afte done. // so it's safer to save to a temporary file location and rename after done.
buf, err := f.writeToBuffer(indent) buf, err := f.writeToBuffer(indent)
if err != nil { if err != nil {
return err return err

10
vendor/gopkg.in/ini.v1/ini.go generated vendored
View File

@ -71,12 +71,18 @@ type LoadOptions struct {
Loose bool Loose bool
// Insensitive indicates whether the parser forces all section and key names to lowercase. // Insensitive indicates whether the parser forces all section and key names to lowercase.
Insensitive bool Insensitive bool
// InsensitiveSections indicates whether the parser forces all section to lowercase.
InsensitiveSections bool
// InsensitiveKeys indicates whether the parser forces all key names to lowercase.
InsensitiveKeys bool
// IgnoreContinuation indicates whether to ignore continuation lines while parsing. // IgnoreContinuation indicates whether to ignore continuation lines while parsing.
IgnoreContinuation bool IgnoreContinuation bool
// IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value.
IgnoreInlineComment bool IgnoreInlineComment bool
// SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs. // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs.
SkipUnrecognizableLines bool SkipUnrecognizableLines bool
// ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source.
ShortCircuit bool
// AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing.
// This type of keys are mostly used in my.cnf. // This type of keys are mostly used in my.cnf.
AllowBooleanKeys bool AllowBooleanKeys bool
@ -107,8 +113,10 @@ type LoadOptions struct {
UnparseableSections []string UnparseableSections []string
// KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:".
KeyValueDelimiters string KeyValueDelimiters string
// KeyValueDelimiters is the delimiter that are used to separate key and value output. By default, it is "=". // KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=".
KeyValueDelimiterOnWrite string KeyValueDelimiterOnWrite string
// ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".".
ChildSectionDelimiter string
// PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes).
PreserveSurroundedQuote bool PreserveSurroundedQuote bool
// DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values). // DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values).

4
vendor/gopkg.in/ini.v1/parser.go generated vendored
View File

@ -377,7 +377,7 @@ func (f *File) parse(reader io.Reader) (err error) {
// Ignore error because default section name is never empty string. // Ignore error because default section name is never empty string.
name := DefaultSection name := DefaultSection
if f.options.Insensitive { if f.options.Insensitive || f.options.InsensitiveSections {
name = strings.ToLower(DefaultSection) name = strings.ToLower(DefaultSection)
} }
section, _ := f.NewSection(name) section, _ := f.NewSection(name)
@ -469,7 +469,7 @@ func (f *File) parse(reader io.Reader) (err error) {
inUnparseableSection = false inUnparseableSection = false
for i := range f.options.UnparseableSections { for i := range f.options.UnparseableSections {
if f.options.UnparseableSections[i] == name || if f.options.UnparseableSections[i] == name ||
(f.options.Insensitive && strings.EqualFold(f.options.UnparseableSections[i], name)) { ((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) {
inUnparseableSection = true inUnparseableSection = true
continue continue
} }

10
vendor/gopkg.in/ini.v1/section.go generated vendored
View File

@ -66,7 +66,7 @@ func (s *Section) SetBody(body string) {
func (s *Section) NewKey(name, val string) (*Key, error) { func (s *Section) NewKey(name, val string) (*Key, error) {
if len(name) == 0 { if len(name) == 0 {
return nil, errors.New("error creating new key: empty key name") return nil, errors.New("error creating new key: empty key name")
} else if s.f.options.Insensitive { } else if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
@ -109,7 +109,7 @@ func (s *Section) GetKey(name string) (*Key, error) {
if s.f.BlockMode { if s.f.BlockMode {
s.f.lock.RLock() s.f.lock.RLock()
} }
if s.f.options.Insensitive { if s.f.options.Insensitive || s.f.options.InsensitiveKeys {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
key := s.keys[name] key := s.keys[name]
@ -121,7 +121,7 @@ func (s *Section) GetKey(name string) (*Key, error) {
// Check if it is a child-section. // Check if it is a child-section.
sname := s.name sname := s.name
for { for {
if i := strings.LastIndex(sname, "."); i > -1 { if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
sname = sname[:i] sname = sname[:i]
sec, err := s.f.GetSection(sname) sec, err := s.f.GetSection(sname)
if err != nil { if err != nil {
@ -188,7 +188,7 @@ func (s *Section) ParentKeys() []*Key {
var parentKeys []*Key var parentKeys []*Key
sname := s.name sname := s.name
for { for {
if i := strings.LastIndex(sname, "."); i > -1 { if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 {
sname = sname[:i] sname = sname[:i]
sec, err := s.f.GetSection(sname) sec, err := s.f.GetSection(sname)
if err != nil { if err != nil {
@ -245,7 +245,7 @@ func (s *Section) DeleteKey(name string) {
// For example, "[parent.child1]" and "[parent.child12]" are child sections // For example, "[parent.child1]" and "[parent.child12]" are child sections
// of section "[parent]". // of section "[parent]".
func (s *Section) ChildSections() []*Section { func (s *Section) ChildSections() []*Section {
prefix := s.name + "." prefix := s.name + s.f.options.ChildSectionDelimiter
children := make([]*Section, 0, 3) children := make([]*Section, 0, 3)
for _, name := range s.f.sectionList { for _, name := range s.f.sectionList {
if strings.HasPrefix(name, prefix) { if strings.HasPrefix(name, prefix) {

62
vendor/gopkg.in/ini.v1/struct.go generated vendored
View File

@ -263,24 +263,21 @@ func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim stri
return nil return nil
} }
func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool) { func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) {
opts := strings.SplitN(tag, ",", 4) opts := strings.SplitN(tag, ",", 5)
rawName = opts[0] rawName = opts[0]
if len(opts) > 1 { for _, opt := range opts[1:] {
omitEmpty = opts[1] == "omitempty" omitEmpty = omitEmpty || (opt == "omitempty")
allowShadow = allowShadow || (opt == "allowshadow")
allowNonUnique = allowNonUnique || (opt == "nonunique")
extends = extends || (opt == "extends")
} }
if len(opts) > 2 { return rawName, omitEmpty, allowShadow, allowNonUnique, extends
allowShadow = opts[2] == "allowshadow"
}
if len(opts) > 3 {
allowNonUnique = opts[3] == "nonunique"
}
return rawName, omitEmpty, allowShadow, allowNonUnique
} }
// mapToField maps the given value to the matching field of the given section. // mapToField maps the given value to the matching field of the given section.
// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added. // The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int) error { func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
if val.Kind() == reflect.Ptr { if val.Kind() == reflect.Ptr {
val = val.Elem() val = val.Elem()
} }
@ -295,7 +292,7 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int)
continue continue
} }
rawName, _, allowShadow, allowNonUnique := parseTagOptions(tag) rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
fieldName := s.parseFieldName(tpField.Name, rawName) fieldName := s.parseFieldName(tpField.Name, rawName)
if len(fieldName) == 0 || !field.CanSet() { if len(fieldName) == 0 || !field.CanSet() {
continue continue
@ -303,12 +300,26 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int)
isStruct := tpField.Type.Kind() == reflect.Struct isStruct := tpField.Type.Kind() == reflect.Struct
isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
if isAnonymous { if isAnonymousPtr {
field.Set(reflect.New(tpField.Type.Elem())) field.Set(reflect.New(tpField.Type.Elem()))
} }
if isAnonymous || isStruct || isStructPtr { if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) {
if isStructPtr && field.IsNil() {
field.Set(reflect.New(tpField.Type.Elem()))
}
fieldSection := s
if rawName != "" {
sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName
if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) {
fieldSection = secs[sectionIndex]
}
}
if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil {
return fmt.Errorf("map to field %q: %v", fieldName, err)
}
} else if isAnonymousPtr || isStruct || isStructPtr {
if secs, err := s.f.SectionsByName(fieldName); err == nil { if secs, err := s.f.SectionsByName(fieldName); err == nil {
if len(secs) <= sectionIndex { if len(secs) <= sectionIndex {
return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName) return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName)
@ -318,7 +329,7 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int)
if isStructPtr && field.IsNil() { if isStructPtr && field.IsNil() {
field.Set(reflect.New(tpField.Type.Elem())) field.Set(reflect.New(tpField.Type.Elem()))
} }
if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex); err != nil { if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil {
return fmt.Errorf("map to field %q: %v", fieldName, err) return fmt.Errorf("map to field %q: %v", fieldName, err)
} }
continue continue
@ -357,7 +368,7 @@ func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (
typ := val.Type().Elem() typ := val.Type().Elem()
for i, sec := range secs { for i, sec := range secs {
elem := reflect.New(typ) elem := reflect.New(typ)
if err = sec.mapToField(elem, isStrict, i); err != nil { if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil {
return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err) return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err)
} }
@ -387,7 +398,7 @@ func (s *Section) mapTo(v interface{}, isStrict bool) error {
return nil return nil
} }
return s.mapToField(val, isStrict, 0) return s.mapToField(val, isStrict, 0, s.name)
} }
// MapTo maps section to given struct. // MapTo maps section to given struct.
@ -479,7 +490,7 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
_ = keyWithShadows.AddShadow(val) _ = keyWithShadows.AddShadow(val)
} }
} }
key = keyWithShadows *key = *keyWithShadows
return nil return nil
} }
@ -581,7 +592,7 @@ func (s *Section) reflectFrom(val reflect.Value) error {
continue continue
} }
rawName, omitEmpty, allowShadow, allowNonUnique := parseTagOptions(tag) rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag)
if omitEmpty && isEmptyValue(field) { if omitEmpty && isEmptyValue(field) {
continue continue
} }
@ -595,7 +606,14 @@ func (s *Section) reflectFrom(val reflect.Value) error {
continue continue
} }
if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) || if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) {
if err := s.reflectFrom(field); err != nil {
return fmt.Errorf("reflect from field %q: %v", fieldName, err)
}
continue
}
if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) ||
(tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
// Note: The only error here is section doesn't exist. // Note: The only error here is section doesn't exist.
sec, err := s.f.GetSection(fieldName) sec, err := s.f.GetSection(fieldName)

7
vendor/modules.txt generated vendored
View File

@ -551,11 +551,12 @@ github.com/spf13/pflag
# github.com/spf13/viper v1.7.0 # github.com/spf13/viper v1.7.0
## explicit ## explicit
github.com/spf13/viper github.com/spf13/viper
# github.com/spotinst/spotinst-sdk-go v1.76.0 # github.com/spotinst/spotinst-sdk-go v1.80.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
github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure
github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3
github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gcp github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/gcp
github.com/spotinst/spotinst-sdk-go/service/ocean github.com/spotinst/spotinst-sdk-go/service/ocean
github.com/spotinst/spotinst-sdk-go/service/ocean/providers/aws github.com/spotinst/spotinst-sdk-go/service/ocean/providers/aws
@ -570,7 +571,7 @@ github.com/spotinst/spotinst-sdk-go/spotinst/util/jsonutil
github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil
github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates github.com/spotinst/spotinst-sdk-go/spotinst/util/uritemplates
github.com/spotinst/spotinst-sdk-go/spotinst/util/useragent github.com/spotinst/spotinst-sdk-go/spotinst/util/useragent
# github.com/stretchr/testify v1.6.1 # github.com/stretchr/testify v1.7.0
## explicit ## explicit
github.com/stretchr/testify/assert github.com/stretchr/testify/assert
github.com/stretchr/testify/require github.com/stretchr/testify/require
@ -827,7 +828,7 @@ gopkg.in/gcfg.v1/types
# gopkg.in/inf.v0 v0.9.1 # gopkg.in/inf.v0 v0.9.1
## explicit ## explicit
gopkg.in/inf.v0 gopkg.in/inf.v0
# gopkg.in/ini.v1 v1.57.0 # gopkg.in/ini.v1 v1.62.0
gopkg.in/ini.v1 gopkg.in/ini.v1
# gopkg.in/square/go-jose.v2 v2.3.1 # gopkg.in/square/go-jose.v2 v2.3.1
gopkg.in/square/go-jose.v2 gopkg.in/square/go-jose.v2