Skip to content

Instance sorting #138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
110 changes: 30 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,87 +241,24 @@ Suite Flags:


Global Flags:
--cache-dir string Directory to save the pricing and instance type caches (default "~/.ec2-instance-selector/")
--cache-ttl int Cache TTLs in hours for pricing and instance type caches. Setting the cache to 0 will turn off caching and cleanup any on-disk caches. (default 168)
-h, --help Help
--max-results int The maximum number of instance types that match your criteria to return (default 20)
-o, --output string Specify the output format (table, table-wide, one-line, simple) (default "simple")
--profile string AWS CLI profile to use for credentials and config
-r, --region string AWS Region to use for API requests (NOTE: if not passed in, uses AWS SDK default precedence)
-v, --verbose Verbose - will print out full instance specs
--version Prints CLI version
--cache-dir string Directory to save the pricing and instance type caches (default "~/.ec2-instance-selector/")
--cache-ttl int Cache TTLs in hours for pricing and instance type caches. Setting the cache to 0 will turn off caching and cleanup any on-disk caches. (default 168)
-h, --help Help
--max-results int The maximum number of instance types that match your criteria to return (default 20)
-o, --output string Specify the output format (table, table-wide, one-line, simple) (default "simple")
--profile string AWS CLI profile to use for credentials and config
-r, --region string AWS Region to use for API requests (NOTE: if not passed in, uses AWS SDK default precedence)
--sort-direction string Specify the direction to sort in (ascending, descending) (default "ascending")
--sort-filter string Specify the field to sort by (on-demand-price, spot-price, vcpu, memory, instance-type-name) (default "instance-type-name")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: what do you think about changing this to sort-by ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I agree with this change. sort-by makes more intuitive sense for what the purpose of the flag is.

-v, --verbose Verbose - will print out full instance specs
--version Prints CLI version
```


### Go Library

This is a minimal example of using the instance selector go package directly:

**NOTE:** The example below is intended for `v3+`. For versions `v2.3.1` and earlier, refer to the following dropdown:
<details>
<summary>Example for v2.3.1</summary>

```go
package main

import (
"fmt"

"github.com/aws/amazon-ec2-instance-selector/v2/pkg/bytequantity"
"github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
)

func main() {
// Load an AWS session by looking at shared credentials or environment variables
// https://docs.aws.amazon.com/sdk-for-go/api/aws/session/
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-2"),
})
if err != nil {
fmt.Printf("Oh no, AWS session credentials cannot be found: %v", err)
return
}

// Instantiate a new instance of a selector with the AWS session
instanceSelector := selector.New(sess)

// Instantiate an int range filter to specify min and max vcpus
vcpusRange := selector.IntRangeFilter{
LowerBound: 2,
UpperBound: 4,
}
// Instantiate a byte quantity range filter to specify min and max memory in GiB
memoryRange := selector.ByteQuantityRangeFilter{
LowerBound: bytequantity.FromGiB(2),
UpperBound: bytequantity.FromGiB(4),
}
// Create a string for the CPU Architecture so that it can be passed as a pointer
// when creating the Filter struct
cpuArch := "x86_64"

// Create a Filter struct with criteria you would like to filter
// The full struct definition can be found here for all of the supported filters:
// https://github.com/aws/amazon-ec2-instance-selector/blob/main/pkg/selector/types.go
filters := selector.Filters{
VCpusRange: &vcpusRange,
MemoryRange: &memoryRange,
CPUArchitecture: &cpuArch,
}

// Pass the Filter struct to the Filter function of your selector instance
instanceTypesSlice, err := instanceSelector.Filter(filters)
if err != nil {
fmt.Printf("Oh no, there was an error :( %v", err)
return
}
// Print the returned instance types slice
fmt.Println(instanceTypesSlice)
}
```
</details>

**cmd/examples/example1.go**
```go#cmd/examples/example1.go
package main
Expand Down Expand Up @@ -374,19 +311,32 @@ func main() {
}

// Pass the Filter struct to the FilteredInstanceTypes function of your
// selector instance to get a list of filtered instance types and their details
// selector instance to get a list of filtered instance types and their details.
instanceTypesSlice, err := instanceSelector.FilterInstanceTypes(filters)
if err != nil {
fmt.Printf("Oh no, there was an error getting instance types: %v", err)
return
}

// Pass in your list of instance type details to the appropriate output function
// in order to format the instance types as printable strings.
maxResults := 100
// Pass in the list of instance type details to the SortInstanceTypes function
// if you wish to sort the instances based on set filters.
sortFilter := "instance-type-name"
sortDirection := "ascending"
instanceTypesSlice, err = instanceSelector.SortInstanceTypes(instanceTypesSlice, &sortFilter, &sortDirection)
if err != nil {
fmt.Printf("Oh no, there was an error sorting instance types: %v", err)
return
}

// Truncate results and format them for output with your desired formatting function.
// All formatting functions can be found here:
// https://github.com/aws/amazon-ec2-instance-selector/blob/main/pkg/selector/outputs/outputs.go
// Examples of formatted outputs can be found here:
// https://github.com/aws/amazon-ec2-instance-selector#examples
maxResults := 10
instanceTypesSlice, _, err = outputs.TruncateResults(&maxResults, instanceTypesSlice)
if err != nil {
fmt.Printf("Oh no, there was an error truncating instnace types: %v", err)
fmt.Printf("Oh no, there was an error truncating instance types: %v", err)
return
}
instanceTypes := outputs.SimpleInstanceTypeOutput(instanceTypesSlice)
Expand All @@ -403,7 +353,7 @@ func main() {
$ git clone https://github.com/aws/amazon-ec2-instance-selector.git
$ cd amazon-ec2-instance-selector/
$ go run cmd/examples/example1.go
[c4.large c5.large c5a.large c5ad.large c5d.large c6i.large t2.medium t3.medium t3.small t3a.medium t3a.small]
[c4.large c5.large c5a.large c5ad.large c5d.large c6a.large c6i.large c6id.large t2.medium t3.medium]
```

## Building
Expand Down
52 changes: 46 additions & 6 deletions cmd/examples/example1.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"encoding/json"
"fmt"

"github.com/aws/amazon-ec2-instance-selector/v2/pkg/bytequantity"
"github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/sorter"
"github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector"
"github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs"
"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -38,33 +40,71 @@ func main() {
// when creating the Filter struct
cpuArch := "x86_64"

priceRange := selector.Float64RangeFilter{
LowerBound: 0,
UpperBound: 100,
}

// Create a Filter struct with criteria you would like to filter
// The full struct definition can be found here for all of the supported filters:
// https://github.com/aws/amazon-ec2-instance-selector/blob/main/pkg/selector/types.go
filters := selector.Filters{
VCpusRange: &vcpusRange,
MemoryRange: &memoryRange,
CPUArchitecture: &cpuArch,
PricePerHour: &priceRange,
}

// Pass the Filter struct to the FilteredInstanceTypes function of your
// selector instance to get a list of filtered instance types and their details
// selector instance to get a list of filtered instance types and their details.
instanceTypesSlice, err := instanceSelector.FilterInstanceTypes(filters)
if err != nil {
fmt.Printf("Oh no, there was an error getting instance types: %v", err)
return
}

// Pass in your list of instance type details to the appropriate output function
// in order to format the instance types as printable strings.
maxResults := 100
// Truncate results and format them for output with your desired formatting function.
// All formatting functions can be found here:
// https://github.com/aws/amazon-ec2-instance-selector/blob/main/pkg/selector/outputs/outputs.go
// Examples of formatted outputs can be found here:
// https://github.com/aws/amazon-ec2-instance-selector#examples
maxResults := 10
instanceTypesSlice, _, err = outputs.TruncateResults(&maxResults, instanceTypesSlice)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we really need this for outputs. It's trivial to truncate a slice. It's not clear why this returns 3 values too. What error could be returned from a truncation?

Copy link
Contributor Author

@digocorbellini digocorbellini Jul 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we really need this for outputs. It's trivial to truncate a slice.

That is a good point. I am going to move TruncateResults to being a private function for the CLI tool to use.

It's not clear why this returns 3 values too. What error could be returned from a truncation?

The 3 values are the truncated list, the number of truncated items, and an error. The error exists to catch invalid maxResults such as negative numbers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this type of func, I'd drop the error and do something reasonable.

For example,

TruncateResults(-1, 5results) => [], 5
TruncateResults(0, 5results) => [], 5
TruncateResults(10, 5results) => [1,2,3,4,5], 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that is a very good idea. I'll change the output of TruncateResults to only output the truncated list and the number of truncated items while ensuring that a reasonable output occurs with previously "invalid" maxResults values.

if err != nil {
fmt.Printf("Oh no, there was an error truncating instnace types: %v", err)
fmt.Printf("Oh no, there was an error truncating instance types: %v", err)
return
}
instanceTypes := outputs.SimpleInstanceTypeOutput(instanceTypesSlice)

instanceTypesSlice[3].OnDemandPricePerHour = nil
instanceTypesSlice[8].OnDemandPricePerHour = nil

// Pass in the list of instance type details to the SortInstanceTypes function
// if you wish to sort the instances based on set filters.
sortFilter := "on-demand-price"
sortDirection := "descending"
sortedTypes, err := instanceSelector.SortInstanceTypes(instanceTypesSlice, &sortFilter, &sortDirection)
if err != nil {
fmt.Printf("Oh no, there was an error sorting instance types: %v", err)
return
}
instanceTypes := outputs.TableOutputWide(sortedTypes)

jsonTypeIndent, err := json.MarshalIndent(instanceTypesSlice[0], "", " ")
fmt.Println(string(jsonTypeIndent))
fmt.Println()

// Print the returned instance types slice
fmt.Println(instanceTypes)

// TODO: remove this. This is for testing purposes
fmt.Println()

// jsonType, err := json.Marshal(instanceTypesSlice[0])
// fmt.Println(string(jsonType) + "\n")

sorter, err := sorter.NewSorter(instanceTypesSlice, "$.OndemandPricePerHour", sortDirection)
sorter.Sort()
types := sorter.InstanceTypes()
str := outputs.TableOutputWide(types)
fmt.Printf(str[0])
}
Loading