github.com/crossplane/upjet@v1.3.0/pkg/pipeline/conversion_spoke.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package pipeline
     6  
     7  import (
     8  	"fmt"
     9  	"go/types"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"strings"
    14  
    15  	"github.com/muvaf/typewriter/pkg/wrapper"
    16  	"github.com/pkg/errors"
    17  
    18  	"github.com/crossplane/upjet/pkg/pipeline/templates"
    19  )
    20  
    21  var (
    22  	regexTypeFile = regexp.MustCompile(`zz_(.+)_types.go`)
    23  )
    24  
    25  // NewConversionSpokeGenerator returns a new ConversionSpokeGenerator.
    26  func NewConversionSpokeGenerator(pkg *types.Package, rootDir, group, version string) *ConversionSpokeGenerator {
    27  	return &ConversionSpokeGenerator{
    28  		LocalDirectoryPath: filepath.Join(rootDir, "apis", strings.ToLower(strings.Split(group, ".")[0])),
    29  		LicenseHeaderPath:  filepath.Join(rootDir, "hack", "boilerplate.go.txt"),
    30  		SpokeVersionsMap:   make(map[string][]string),
    31  		pkg:                pkg,
    32  		version:            version,
    33  	}
    34  }
    35  
    36  // ConversionSpokeGenerator generates conversion methods implementing the
    37  // conversion.Convertible interface on the CRD structs.
    38  type ConversionSpokeGenerator struct {
    39  	LocalDirectoryPath string
    40  	LicenseHeaderPath  string
    41  	SpokeVersionsMap   map[string][]string
    42  
    43  	pkg     *types.Package
    44  	version string
    45  }
    46  
    47  // Generate writes generated conversion.Convertible interface functions
    48  func (cg *ConversionSpokeGenerator) Generate(cfgs []*terraformedInput) error { //nolint:gocyclo
    49  	entries, err := os.ReadDir(cg.LocalDirectoryPath)
    50  	if err != nil {
    51  		return errors.Wrapf(err, "cannot list the directory entries for the source folder %s while generating the conversion.Convertible interface functions", cg.LocalDirectoryPath)
    52  	}
    53  
    54  	for _, e := range entries {
    55  		if !e.IsDir() || e.Name() == cg.version {
    56  			// we skip spoke generation for the current version as the assumption is
    57  			// the current CRD version is the hub version.
    58  			continue
    59  		}
    60  		trFile := wrapper.NewFile(cg.pkg.Path(), cg.pkg.Name(), templates.ConversionSpokeTemplate,
    61  			wrapper.WithGenStatement(GenStatement),
    62  			wrapper.WithHeaderPath(cg.LicenseHeaderPath),
    63  		)
    64  		filePath := filepath.Join(cg.LocalDirectoryPath, e.Name(), "zz_generated.conversion_spokes.go")
    65  		vars := map[string]any{
    66  			"APIVersion": e.Name(),
    67  		}
    68  
    69  		var resources []map[string]any
    70  		versionDir := filepath.Join(cg.LocalDirectoryPath, e.Name())
    71  		files, err := os.ReadDir(versionDir)
    72  		if err != nil {
    73  			return errors.Wrapf(err, "cannot list the directory entries for the source folder %s while looking for the generated types", versionDir)
    74  		}
    75  		for _, f := range files {
    76  			if f.IsDir() {
    77  				continue
    78  			}
    79  			m := regexTypeFile.FindStringSubmatch(f.Name())
    80  			if len(m) < 2 {
    81  				continue
    82  			}
    83  			c := findKindTerraformedInput(cfgs, m[1])
    84  			if c == nil {
    85  				// type may not be available in the new version =>
    86  				// no conversion is possible.
    87  				continue
    88  			}
    89  			resources = append(resources, map[string]any{
    90  				"CRD": map[string]string{
    91  					"Kind": c.Kind,
    92  				},
    93  			})
    94  			sk := fmt.Sprintf("%s.%s", c.ShortGroup, c.Kind)
    95  			cg.SpokeVersionsMap[sk] = append(cg.SpokeVersionsMap[sk], filepath.Base(versionDir))
    96  		}
    97  
    98  		vars["Resources"] = resources
    99  		if len(resources) == 0 {
   100  			continue
   101  		}
   102  		if err := trFile.Write(filePath, vars, os.ModePerm); err != nil {
   103  			return errors.Wrapf(err, "cannot write the generated conversion Hub functions file %s", filePath)
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  func findKindTerraformedInput(cfgs []*terraformedInput, name string) *terraformedInput {
   110  	for _, c := range cfgs {
   111  		if strings.EqualFold(name, c.Kind) {
   112  			return c
   113  		}
   114  	}
   115  	return nil
   116  }