Skip to content

Commit

Permalink
add tool to generate instance type docs
Browse files Browse the repository at this point in the history
Displays both labels and the resources that we use for
bin-packing after overhead calculations.
  • Loading branch information
tzneal committed Jun 16, 2022
1 parent 205c4db commit 5a107ac
Show file tree
Hide file tree
Showing 3 changed files with 10,157 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ deflake:
battletest: strongertests
go tool cover -html coverage.out -o coverage.html

verify: codegen docgen ## Verify code. Includes dependencies, linting, formatting, etc
verify: codegen ## Verify code. Includes dependencies, linting, formatting, etc
go mod tidy
go mod download
golangci-lint run
Expand Down Expand Up @@ -80,6 +80,7 @@ codegen: ## Generate code. Must be run if changes are made to ./pkg/apis/...

docgen: ## Generate docs
go run hack/docs/metrics_gen_docs.go pkg/ website/content/en/preview/tasks/metrics.md
go run hack/docs/instancetypes_gen_docs.go ${KARPENTER_SUBNET_SELECTOR} website/content/en/preview/AWS/instance-types.md

release: ## Generate release manifests and publish a versioned container image.
$(WITH_GOFLAGS) ./hack/release.sh
Expand Down
152 changes: 152 additions & 0 deletions hack/docs/instancetypes_gen_docs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package main

import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"github.com/aws/karpenter/pkg/apis/provisioning/v1alpha5"
"github.com/aws/karpenter/pkg/cloudprovider"
"github.com/aws/karpenter/pkg/cloudprovider/aws"
"github.com/aws/karpenter/pkg/cloudprovider/aws/apis/v1alpha1"
"github.com/aws/karpenter/pkg/utils/resources"
"github.com/samber/lo"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"log"
"os"
"sort"
"strings"
)

func main() {
flag.Parse()
if flag.NArg() != 2 {
log.Printf("Usage: %s subnet-discovery-tag-value path/to/markdown.md", os.Args[0])
os.Exit(1)
}

os.Setenv("AWS_SDK_LOAD_CONFIG", "true")
ctx := context.Background()

cp := aws.NewCloudProvider(ctx, cloudprovider.Options{
ClientSet: nil,
KubeClient: nil,
})
provider := v1alpha1.AWS{SubnetSelector: map[string]string{
"karpenter.sh/discovery": flag.Arg(0),
}}
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
if err := enc.Encode(provider); err != nil {
log.Fatalf("encoding provider, %s", err)
}
instanceTypes, err := cp.GetInstanceTypes(ctx, &v1alpha5.Provider{
Raw: buf.Bytes(),
Object: nil,
})
if err != nil {
log.Fatalf("listing instance types, %s", err)
}

outputFileName := flag.Arg(1)
f, err := os.Create(outputFileName)
if err != nil {
log.Fatalf("error creating output file %s, %s", outputFileName, err)
}

log.Println("writing output to", outputFileName)
fmt.Fprintf(f, `---
title: "Instance Types"
linkTitle: "Instance Types"
weight: 100
description: >
Evaluate Instance Type Resources
---
`)
fmt.Fprintln(f, "<!-- this document is generated from hack/docs/instancetypes_gen_docs.go -->")
fmt.Fprintln(f, `AWS instance types offer varying resources and can be selected by labels. In particular the
ephemeral-storage is the default value for an AL2 family assuming no specific block device mappings have been configured.`)

// generate a map of family -> instance types along with some other sorted lists. The sorted lists ensure we
// generate consistent docs every run.
families := map[string][]cloudprovider.InstanceType{}
labelNameMap := sets.String{}
resourceNameMap := sets.String{}
for _, it := range instanceTypes {
familyName := strings.Split(it.Name(), ".")[0]
families[familyName] = append(families[familyName], it)
for labelName := range it.Requirements() {
labelNameMap.Insert(labelName)
}
for resourceName := range it.Resources() {
resourceNameMap.Insert(string(resourceName))
}
}
familyNames := lo.Keys(families)
sort.Strings(familyNames)

// we don't want to show the zone label that was applied based on our credentials
delete(labelNameMap, v1.LabelTopologyZone)
labelNames := lo.Keys(labelNameMap)

sort.Strings(labelNames)
resourceNames := lo.Keys(resourceNameMap)
sort.Strings(resourceNames)

for _, familyName := range familyNames {
fmt.Fprintf(f, "## %s Family\n", familyName)

// sort the instance types within the family, we sort by CPU and memory which should be a pretty good ordering
sort.Slice(families[familyName], func(a, b int) bool {
lhs := families[familyName][a]
rhs := families[familyName][b]
lhsResources := lhs.Resources()
rhsResources := rhs.Resources()
if cpuCmp := resources.Cmp(*lhsResources.Cpu(), *rhsResources.Cpu()); cpuCmp != 0 {
return cpuCmp < 0
}
if memCmp := resources.Cmp(*lhsResources.Memory(), *rhsResources.Memory()); memCmp != 0 {
return memCmp < 0
}
return lhs.Name() < rhs.Name()
})

for _, it := range families[familyName] {
fmt.Fprintf(f, "### `%s`\n", it.Name())
minusOverhead := v1.ResourceList{}
for k, v := range it.Resources() {
if v.IsZero() {
continue
}
cp := v.DeepCopy()
cp.Sub(it.Overhead()[k])
minusOverhead[k] = cp
}
fmt.Fprintln(f, "#### Labels")
fmt.Fprintln(f, " | Label | Value |")
fmt.Fprintln(f, " |--|--|")
for _, label := range labelNames {
req, ok := it.Requirements()[label]
if !ok {
continue
}
if req.Values().Len() == 1 {
fmt.Fprintf(f, " |%s|%s|\n", label, req.Values().List()[0])
}
}
fmt.Fprintln(f, "#### Resources")
fmt.Fprintln(f, " | Resource | Quantity |")
fmt.Fprintln(f, " |--|--|")
for _, resourceName := range resourceNames {
quantity := it.Resources()[v1.ResourceName(resourceName)]
if quantity.IsZero() {
continue
}
fmt.Fprintf(f, " |%s|%s|\n", resourceName, quantity.String())
}
}
}
}
Loading

0 comments on commit 5a107ac

Please sign in to comment.