Skip to content
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

docs: add tool to generate instance type docs #1946

Merged
merged 1 commit into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
160 changes: 160 additions & 0 deletions hack/docs/instancetypes_gen_docs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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/api/resource"
"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. The values provided
below are the resources available with some assumptions and after the instance overhead has been subtracted:
- `+"`blockDeviceMappings` are not configured"+`
- `+"`aws-eni-limited-pod-density` is assumed to be `true`"+`
- `+"`amiFamily` is set to the default of `AL2`")

Copy link
Contributor

Choose a reason for hiding this comment

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

Might be worth calling out the the resources have overhead taken out. Although it looks like ephemeral storage does not and maybe not CPU either? https://github.com/aws/karpenter/blob/205c4db5743f1f6824253e92304a5df2f875c90c/pkg/cloudprovider/aws/amifamily/al2.go#L94

Might also be worth calling out that pods are assuming the aws-eni-limited-pod-density = true

Copy link
Contributor Author

@tzneal tzneal Jun 16, 2022

Choose a reason for hiding this comment

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

SGTM. Good catch on overhead! I calculated the value with overhead subtracted and then ignored it 😆

// 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 := minusOverhead[v1.ResourceName(resourceName)]
if quantity.IsZero() {
continue
}
if v1.ResourceName(resourceName) == v1.ResourceEphemeralStorage {
i64, _ := quantity.AsInt64()
quantity = *resource.NewQuantity(i64, resource.BinarySI)
}
fmt.Fprintf(f, " |%s|%s|\n", resourceName, quantity.String())
}
}
}
}
Loading