diff --git a/cmd/version.go b/cmd/version.go index a1f3373..cb7bc57 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,6 +1,7 @@ package cmd import ( + "encoding/json" "fmt" "github.com/spf13/cobra" @@ -27,8 +28,8 @@ var versionCmd = &cobra.Command{ Date: date, Commit: commit, } - - fmt.Printf("karpenter-generate version info: %#v\n", kgVersion) + verBytes, _ := json.Marshal(kgVersion) + fmt.Println(string(verBytes)) }, } diff --git a/pkg/karpenter/ec2nodeclass.go b/pkg/karpenter/ec2nodeclass.go index 7c3d1a9..6dabb5d 100644 --- a/pkg/karpenter/ec2nodeclass.go +++ b/pkg/karpenter/ec2nodeclass.go @@ -1,6 +1,7 @@ package karpenter import ( + "encoding/base64" "strings" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" @@ -13,7 +14,8 @@ import ( type NodeGroup struct { *ekstypes.Nodegroup - LtData *ec2types.ResponseLaunchTemplateData + LT *ec2types.ResponseLaunchTemplateData // LT Generated by MNG and used by ASG + CustomLT *ec2types.ResponseLaunchTemplateData // Custom LT provided to MNG } var ( @@ -21,13 +23,32 @@ var ( Kind: "EC2NodeClass", APIVersion: awskarpenter.SchemeGroupVersion.Identifier(), } + CLUSTER_TAG map[string]string ) func NewNodeGroup(ng ekstypes.Nodegroup) (*NodeGroup, error) { + newNodegroup := NodeGroup{ + Nodegroup: &ng, + } + + CLUSTER_TAG = map[string]string{ + "kubernetes.io/cluster/" + *ng.ClusterName: "owned", + } + ec2Client := aws.NewEC2Client() asgClient := aws.NewAutoscalingClient() + if ng.LaunchTemplate != nil { + customLT, err := ec2Client.DescribeLaunchTemplateVersions( + *ng.LaunchTemplate.Id, + *ng.LaunchTemplate.Version) + if err != nil { + return nil, aws.FormatErrorAsMessageOnly(err) + } + newNodegroup.CustomLT = customLT[0].LaunchTemplateData + } + asg, err := asgClient.DescribeAutoScalingGroups(*ng.Resources.AutoScalingGroups[0].Name) if err != nil { return nil, aws.FormatErrorAsMessageOnly(err) @@ -40,10 +61,9 @@ func NewNodeGroup(ng ekstypes.Nodegroup) (*NodeGroup, error) { return nil, aws.FormatErrorAsMessageOnly(err) } - return &NodeGroup{ - Nodegroup: &ng, - LtData: lt[0].LaunchTemplateData, - }, nil + newNodegroup.LT = lt[0].LaunchTemplateData + + return &newNodegroup, nil } func (n *NodeGroup) GetEC2NodeClass() (awskarpenter.EC2NodeClass, error) { @@ -59,7 +79,8 @@ func (n NodeGroup) Name() string { } func (n NodeGroup) AmiID() string { - return *n.LtData.ImageId + // TODO: implement logic if user wants to preserve AMID from MNG?? + return *n.CustomLT.ImageId } func (n NodeGroup) NodeClassObjectMeta() metav1.ObjectMeta { @@ -81,6 +102,7 @@ func (n NodeGroup) NodeClassSpec() awskarpenter.EC2NodeClassSpec { Role: n.Role(), SubnetSelectorTerms: n.SubnetSelectorTerms(), SecurityGroupSelectorTerms: n.SecurityGroupSelectorTerms(), + UserData: n.UserData(), Tags: n.FilteredTags(), } } @@ -94,6 +116,16 @@ func (n NodeGroup) NodeClassSpec() awskarpenter.EC2NodeClassSpec { func (n NodeGroup) FilteredTags() map[string]string { filteredTags := map[string]string{} + if n.CustomLT != nil { + for _, tagspec := range n.CustomLT.TagSpecifications { + for _, tag := range tagspec.Tags { + if !strings.HasPrefix(*tag.Key, "aws:") { + filteredTags[*tag.Key] = *tag.Value + } + } + } + } + for key, val := range n.Tags { if !strings.HasPrefix(key, "aws:") { filteredTags[key] = val @@ -119,6 +151,10 @@ func (n NodeGroup) AMIFamily() *string { } } +func (n NodeGroup) IsCustomAMIFamily() bool { + return n.AmiType == ekstypes.AMITypesCustom +} + func (n NodeGroup) AMISelectorTerms() []awskarpenter.AMISelectorTerm { amiTerms := []awskarpenter.AMISelectorTerm{} amiTerms = append(amiTerms, awskarpenter.AMISelectorTerm{ @@ -146,22 +182,46 @@ func (n NodeGroup) SubnetSelectorTerms() []awskarpenter.SubnetSelectorTerm { func (n NodeGroup) SecurityGroupSelectorTerms() []awskarpenter.SecurityGroupSelectorTerm { sgTerms := []awskarpenter.SecurityGroupSelectorTerm{} - ltSG := n.LtData.SecurityGroupIds - // For LTs created by simple MNG - for _, intf := range n.LtData.NetworkInterfaces { - for _, sg := range intf.Groups { + // For customLaunchTemplates + if n.CustomLT != nil { + ltSG := n.CustomLT.SecurityGroupIds + for _, sg := range ltSG { sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ ID: sg, }) } } - // For customLaunchTemplates - for _, sg := range ltSG { - sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ - ID: sg, - }) + // TODO: Check how MNG handles SGs if Custom LT has Additional SGs + // For Cluster SG attached by MNG + for _, intf := range n.LT.NetworkInterfaces { + for _, sg := range intf.Groups { + sgTerms = append(sgTerms, awskarpenter.SecurityGroupSelectorTerm{ + ID: sg, + }) + } } + return sgTerms } + +// Returns UserData for nodegroup if Custom Launch Template is used with MNG +func (n NodeGroup) UserData() *string { + if n.CustomLT != nil { + // Additional UserData for non-custom AMIs + if !n.IsCustomAMIFamily() { + decodedUserData, _ := base64.StdEncoding.DecodeString(*n.CustomLT.UserData) + userData := string(decodedUserData) + return &userData + } + // UserData for custom AMIs + decodedUserData, _ := base64.StdEncoding.DecodeString(*n.CustomLT.UserData) + userData := string(decodedUserData) + return &userData + } + return nil +} + +// TODO: Implement logic for BlockDeviceMapping +// TODO: Implement logic for MetadataOptions diff --git a/pkg/karpenter/generate.go b/pkg/karpenter/generate.go index b1dbb7a..fbc4000 100644 --- a/pkg/karpenter/generate.go +++ b/pkg/karpenter/generate.go @@ -6,7 +6,6 @@ import ( "github.com/aws/aws-sdk-go-v2/service/eks/types" awskarpenter "github.com/aws/karpenter-provider-aws/pkg/apis/v1beta1" - "k8s.io/cli-runtime/pkg/printers" sigkarpenter "sigs.k8s.io/karpenter/pkg/apis/v1beta1" )