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  }