github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/googlegke/render.go (about) 1 package googlegke 2 3 import ( 4 "bytes" 5 "context" 6 "path" 7 "text/template" 8 9 "github.com/Masterminds/sprig/v3" 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 GKE cluster) as part of a planner.Plan 22 type Renderer interface { 23 Execute( 24 rootFs root.Fs, 25 asset api.GKEAsset, 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 Logger log.Logger 35 BuilderBuilder *templates.BuilderBuilder 36 Inline inline.Renderer 37 Fs afero.Afero 38 } 39 40 var _ Renderer = &LocalRenderer{} 41 42 func NewRenderer( 43 logger log.Logger, 44 bb *templates.BuilderBuilder, 45 inline inline.Renderer, 46 fs afero.Afero, 47 ) Renderer { 48 return &LocalRenderer{ 49 Logger: logger, 50 BuilderBuilder: bb, 51 Inline: inline, 52 Fs: fs, 53 } 54 } 55 56 func (r *LocalRenderer) Execute( 57 rootFs root.Fs, 58 asset api.GKEAsset, 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 := "google_gke.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.AddGoogleGKEPath(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.GKEAsset, builder *templates.Builder) (api.GKEAsset, error) { 112 var err error 113 var multiErr *multierror.Error 114 115 asset.Credentials, err = builder.String(asset.Credentials) 116 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build credentials")) 117 asset.Project, err = builder.String(asset.Project) 118 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build project")) 119 120 asset.Region, err = builder.String(asset.Region) 121 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build region")) 122 123 asset.ClusterName, err = builder.String(asset.ClusterName) 124 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build cluster_name")) 125 126 asset.Zone, err = builder.String(asset.Zone) 127 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build zone")) 128 129 asset.InitialNodeCount, err = builder.String(asset.InitialNodeCount) 130 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build initial_node_count")) 131 132 asset.MachineType, err = builder.String(asset.MachineType) 133 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build machine_type")) 134 135 asset.AdditionalZones, err = builder.String(asset.AdditionalZones) 136 multiErr = multierror.Append(multiErr, errors.Wrap(err, "build additional_zones")) 137 138 // NOTE: items not configurable by the end user include MinMasterVersion 139 return asset, multiErr.ErrorOrNil() 140 } 141 142 func renderTerraformContents(asset api.GKEAsset) (string, error) { 143 var templateString string 144 if shouldRenderProviderTempl(asset) { 145 templateString += providerTempl 146 } 147 templateString += clusterTempl 148 t, err := template.New("gkeTemplate"). 149 Funcs(sprig.TxtFuncMap()). 150 Parse(templateString) 151 if err != nil { 152 return "", err 153 } 154 return executeTemplate(t, asset) 155 } 156 157 func executeTemplate(t *template.Template, asset api.GKEAsset) (string, error) { 158 var data = struct { 159 api.GKEAsset 160 KubeConfigTmpl string 161 }{ 162 asset, 163 kubeConfigTmpl, 164 } 165 var tpl bytes.Buffer 166 if err := t.Execute(&tpl, data); err != nil { 167 return "", err 168 } 169 170 return tpl.String(), nil 171 } 172 173 func shouldRenderProviderTempl(asset api.GKEAsset) bool { 174 return asset.Credentials != "" || asset.Project != "" || asset.Region != "" 175 }