package ng import ( "bytes" "context" "errors" "fmt" "strings" "text/template" "time" "github.com/aws/aws-k8s-tester/ec2config" "github.com/aws/aws-k8s-tester/pkg/fileutil" "go.uber.org/zap" "k8s.io/utils/exec" ) func (ts *tester) createConfigMap() error { if ts.cfg.EKSConfig.AddOnNodeGroups.Role.ARN == "" { return errors.New("empty AddOnNodeGroups.Role.ARN") } ts.cfg.Logger.Info("writing ConfigMap", zap.String("instance-role-arn", ts.cfg.EKSConfig.AddOnNodeGroups.Role.ARN)) body, p, err := ts.writeConfigMapAuth(ts.cfg.EKSConfig.AddOnNodeGroups.Role.ARN) if err != nil { return err } ts.cfg.Logger.Info("applying ConfigMap") fmt.Fprintf(ts.cfg.LogWriter, "\naws-auth ConfigMap:\n\n%s\n", body) var output []byte // might take several minutes for DNS to propagate waitDur := 5 * time.Minute retryStart := time.Now() for time.Since(retryStart) < waitDur { select { case <-ts.cfg.Stopc: return errors.New("create ConfigMap aborted") case <-time.After(5 * time.Second): } ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) output, err = exec.New().CommandContext( ctx, ts.cfg.EKSConfig.KubectlPath, "--kubeconfig="+ts.cfg.EKSConfig.KubeConfigPath, "apply", "--filename="+p, ).CombinedOutput() cancel() out := string(output) fmt.Fprintf(ts.cfg.LogWriter, "\n\"kubectl apply\" output:\n%s\n", out) if err == nil { break } // "configmap/aws-auth created" or "configmap/aws-auth unchanged" if strings.Contains(out, " created") || strings.Contains(out, " unchanged") { err = nil break } ts.cfg.Logger.Warn("create ConfigMap failed", zap.Error(err)) ts.cfg.EKSConfig.RecordStatus(fmt.Sprintf("create ConfigMap failed (%v)", err)) } if err != nil { return fmt.Errorf("'kubectl apply' failed %v (output %q)", err, string(output)) } ts.cfg.Logger.Info("created ConfigMap") ts.cfg.EKSConfig.Sync() return nil } // TODO: use client-go // https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html const configMapAuthTempl = `--- apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - rolearn: {{.NGInstanceRoleARN}} %s groups: %s ` const LinuxInstanceRBACGroups = `- system:bootstrappers - system:nodes ` const WindowsInstanceAdditionalRBACGroups = ` - eks:kube-proxy-windows` type configMapAuth struct { NGInstanceRoleARN string } func (ts *tester) writeConfigMapAuth(instanceRoleARN string) (body string, fpath string, err error) { kc := configMapAuth{NGInstanceRoleARN: instanceRoleARN} nodeRoleRBACGroupList := LinuxInstanceRBACGroups // If Windows nodes are present then add additional kube-proxy role if ts.hasWindowsNode() { nodeRoleRBACGroupList += WindowsInstanceAdditionalRBACGroups } tpl := template.Must(template.New("configMapAuthTempl").Parse(configMapAuthTempl)) buf := bytes.NewBuffer(nil) if err = tpl.Execute(buf, kc); err != nil { return "", "", err } // avoid '{{' conflicts with Go body = fmt.Sprintf(buf.String(), `username: system:node:{{EC2PrivateDNSName}}`, nodeRoleRBACGroupList) fpath, err = fileutil.WriteTempFile([]byte(body)) return body, fpath, err } // hasWindowsNode returns true if any Windows AMI is present in the the ASG to be created func (ts *tester) hasWindowsNode() bool { for _, cur := range ts.cfg.EKSConfig.AddOnNodeGroups.ASGs { if cur.AMIType == ec2config.AMITypeWindowsServerCore2019X8664 { return true } } return false }