github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/amazoneks/render.go (about) 1 package amazoneks 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "path" 8 "text/template" 9 10 "github.com/go-kit/kit/log" 11 multierror "github.com/hashicorp/go-multierror" 12 "github.com/pkg/errors" 13 "github.com/replicatedhq/libyaml" 14 "github.com/replicatedhq/ship/pkg/api" 15 "github.com/replicatedhq/ship/pkg/lifecycle/render/inline" 16 "github.com/replicatedhq/ship/pkg/lifecycle/render/root" 17 "github.com/replicatedhq/ship/pkg/templates" 18 "github.com/spf13/afero" 19 ) 20 21 // Renderer is something that can render a terraform asset (that produces an EKS cluster) as part of a planner.Plan 22 type Renderer interface { 23 Execute( 24 rootFs root.Fs, 25 asset api.EKSAsset, 26 meta api.ReleaseMetadata, 27 templateContext map[string]interface{}, 28 configGroups []libyaml.ConfigGroup, 29 ) func(ctx context.Context) error 30 } 31 32 // LocalRenderer renders a terraform asset by writing generated terraform source code 33 type LocalRenderer struct { 34 BuilderBuilder *templates.BuilderBuilder 35 Fs afero.Afero 36 Inline inline.Renderer 37 Logger log.Logger 38 } 39 40 var _ Renderer = &LocalRenderer{} 41 42 func NewRenderer( 43 bb *templates.BuilderBuilder, 44 fs afero.Afero, 45 inline inline.Renderer, 46 logger log.Logger, 47 ) Renderer { 48 return &LocalRenderer{ 49 BuilderBuilder: bb, 50 Fs: fs, 51 Inline: inline, 52 Logger: logger, 53 } 54 } 55 56 func (r *LocalRenderer) Execute( 57 rootFs root.Fs, 58 asset api.EKSAsset, 59 meta api.ReleaseMetadata, 60 templateContext map[string]interface{}, 61 configGroups []libyaml.ConfigGroup, 62 ) func(ctx context.Context) error { 63 return func(ctx context.Context) error { 64 65 builder, err := r.BuilderBuilder.FullBuilder(meta, configGroups, templateContext) 66 if err != nil { 67 return errors.Wrap(err, "init builder") 68 } 69 70 asset, err = buildAsset(asset, builder) 71 if err != nil { 72 return errors.Wrap(err, "build asset") 73 } 74 75 contents, err := renderTerraformContents(asset) 76 if err != nil { 77 return errors.Wrap(err, "render tf config") 78 } 79 80 assetsPath := "amazon_eks.tf" 81 if asset.Dest != "" { 82 assetsPath = asset.Dest 83 } 84 85 // save the path to the kubeconfig that running the generated terraform will produce 86 templates.AddAmazonEKSPath(asset.ClusterName, 87 path.Join(path.Dir(assetsPath), "kubeconfig_"+asset.ClusterName)) 88 89 // write the inline spec 90 err = r.Inline.Execute( 91 rootFs, 92 api.InlineAsset{ 93 Contents: contents, 94 AssetShared: api.AssetShared{ 95 Dest: assetsPath, 96 Mode: asset.Mode, 97 }, 98 }, 99 meta, 100 templateContext, 101 configGroups, 102 )(ctx) 103 104 if err != nil { 105 return errors.Wrap(err, "write tf config") 106 } 107 return nil 108 } 109 } 110 111 func buildAsset(asset api.EKSAsset, builder *templates.Builder) (api.EKSAsset, error) { 112 var err error 113 var multiErr *multierror.Error 114 115 asset.ClusterName, err = builder.String(asset.ClusterName) 116 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build cluster_name")) 117 118 asset.Region, err = builder.String(asset.Region) 119 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build region")) 120 121 // build created vpc 122 if asset.CreatedVPC != nil { 123 asset.CreatedVPC.VPCCIDR, err = builder.String(asset.CreatedVPC.VPCCIDR) 124 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build vpc_cidr")) 125 126 for idx, zone := range asset.CreatedVPC.Zones { 127 asset.CreatedVPC.Zones[idx], err = builder.String(zone) 128 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build vpc zone %d", idx))) 129 } 130 for idx, subnet := range asset.CreatedVPC.PublicSubnets { 131 asset.CreatedVPC.PublicSubnets[idx], err = builder.String(subnet) 132 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build vpc public subnet %d", idx))) 133 } 134 for idx, subnet := range asset.CreatedVPC.PrivateSubnets { 135 asset.CreatedVPC.PrivateSubnets[idx], err = builder.String(subnet) 136 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build vpc private subnet zone %d", idx))) 137 } 138 } 139 140 // build existing vpc 141 if asset.ExistingVPC != nil { 142 asset.ExistingVPC.VPCID, err = builder.String(asset.ExistingVPC.VPCID) 143 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build vpc_id")) 144 145 for idx, subnet := range asset.ExistingVPC.PublicSubnets { 146 asset.ExistingVPC.PublicSubnets[idx], err = builder.String(subnet) 147 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build vpc public subnet %d", idx))) 148 } 149 for idx, subnet := range asset.ExistingVPC.PrivateSubnets { 150 asset.ExistingVPC.PrivateSubnets[idx], err = builder.String(subnet) 151 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build vpc private subnet zone %d", idx))) 152 } 153 } 154 155 // build autoscaling groups 156 for idx, group := range asset.AutoscalingGroups { 157 asset.AutoscalingGroups[idx].Name, err = builder.String(group.Name) 158 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build autoscaling group %d name", idx))) 159 160 asset.AutoscalingGroups[idx].GroupSize, err = builder.String(group.GroupSize) 161 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build autoscaling group %d group_size", idx))) 162 163 asset.AutoscalingGroups[idx].MachineType, err = builder.String(group.MachineType) 164 multiErr = multierror.Append(multiErr, errors.Wrap(err, fmt.Sprintf("build autoscaling group %d machine_type", idx))) 165 } 166 167 return asset, multiErr.ErrorOrNil() 168 } 169 170 func renderTerraformContents(asset api.EKSAsset) (string, error) { 171 templateString := "" 172 if asset.CreatedVPC != nil { 173 templateString = newVPCTempl 174 } else if asset.ExistingVPC != nil { 175 templateString = existingVPCTempl 176 } else { 177 return "", errors.New("a created or existing VPC must be provided") 178 } 179 180 templateString += workerTempl 181 t, err := template.New("eksTemplate").Parse(templateString) 182 if err != nil { 183 return "", err 184 } 185 return executeTemplate(t, asset) 186 } 187 188 func executeTemplate(t *template.Template, asset api.EKSAsset) (string, error) { 189 var tpl bytes.Buffer 190 if err := t.Execute(&tpl, asset); err != nil { 191 return "", err 192 } 193 194 return tpl.String(), nil 195 }