go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers-sdk/v1/util/configure/configure.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package main
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"go/format"
    10  	"os"
    11  	"os/exec"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/rs/zerolog"
    17  	"github.com/rs/zerolog/log"
    18  	"github.com/spf13/cobra"
    19  	"go.mondoo.com/cnquery/logger"
    20  	"sigs.k8s.io/yaml"
    21  )
    22  
    23  type ProvidersConf struct {
    24  	Builtin []string `json:"builtin"`
    25  }
    26  
    27  var rootCmd = &cobra.Command{
    28  	Use:   "configure [-f config] [-o file]",
    29  	Short: "configure providers for cnquery",
    30  	Run: func(cmd *cobra.Command, args []string) {
    31  		confPath, err := cmd.Flags().GetString("file")
    32  		if err != nil {
    33  			log.Fatal().Err(err).Msg("Can't get --file")
    34  		}
    35  		outPath, err := cmd.Flags().GetString("output")
    36  		if err != nil {
    37  			log.Fatal().Err(err).Msg("Can't get --output")
    38  		}
    39  
    40  		raw, err := os.ReadFile(confPath)
    41  		if err != nil {
    42  			log.Fatal().Err(err).Str("path", confPath).Msg("failed to read config file")
    43  		}
    44  
    45  		var conf ProvidersConf
    46  		err = yaml.Unmarshal(raw, &conf)
    47  		if err != nil {
    48  			log.Fatal().Err(err).Str("path", confPath).Msg("failed to parse config file")
    49  		}
    50  
    51  		builtinGo, err := genBuiltinGo(conf)
    52  		if err != nil {
    53  			log.Fatal().Err(err).Str("path", confPath).Msg("failed to generate builtin go")
    54  		}
    55  
    56  		if err = os.WriteFile(outPath, builtinGo, 0o644); err != nil {
    57  			log.Fatal().Err(err).Str("path", outPath).Msg("failed to write output")
    58  		}
    59  		log.Info().Str("path", outPath).Strs("providers", conf.Builtin).Msg("(1/3) configured builtin providers")
    60  
    61  		buildProviders(conf.Builtin)
    62  		log.Info().Strs("providers", conf.Builtin).Msg("(2/3) built providers")
    63  
    64  		rewireDependencies(conf.Builtin)
    65  		log.Info().Str("path", outPath).Strs("providers", conf.Builtin).Msg("(3/3) rewired dependencies/files")
    66  	},
    67  }
    68  
    69  func genBuiltinGo(conf ProvidersConf) ([]byte, error) {
    70  	var imports string
    71  	var infos string
    72  	var configs string
    73  
    74  	for _, provider := range conf.Builtin {
    75  		// imports cannot contain dashes
    76  		trimProvider := strings.Replace(provider, "-", "", -1)
    77  		imports += fmt.Sprintf("\t%sconf \"go.mondoo.com/cnquery/providers/%s/config\"\n", trimProvider, provider)
    78  		imports += fmt.Sprintf("\t%s \"go.mondoo.com/cnquery/providers/%s/provider\"\n", trimProvider, provider)
    79  		infos += fmt.Sprintf(
    80  			"//go:embed %s.resources.json\n"+
    81  				"var %sInfo []byte\n",
    82  			provider, trimProvider)
    83  		configs += fmt.Sprintf(`
    84  	builtinProviders[%sconf.Config.ID] = &builtinProvider{
    85  		Runtime: &RunningProvider{
    86  			Name:     %sconf.Config.Name,
    87  			ID:       %sconf.Config.ID,
    88  			Plugin:   %s.Init(),
    89  			Schema:   MustLoadSchema("%s", %sInfo),
    90  			isClosed: false,
    91  		},
    92  		Config: &%sconf.Config,
    93  	}
    94  `, trimProvider, trimProvider, trimProvider, trimProvider, provider, trimProvider, trimProvider)
    95  	}
    96  
    97  	res := fmt.Sprintf(template, imports, infos, configs)
    98  	return format.Source([]byte(res))
    99  }
   100  
   101  const template = `// Copyright (c) Mondoo, Inc.
   102  // SPDX-License-Identifier: BUSL-1.1
   103  //
   104  // This file is auto-generated by 'make providers/config'
   105  // and configured via 'providers.yaml'
   106  
   107  package providers
   108  
   109  import (
   110  	_ "embed"
   111  	// osconf "go.mondoo.com/cnquery/providers/os/config"
   112  	// os "go.mondoo.com/cnquery/providers/os/provider"
   113  %s)
   114  
   115  // //go:embed os/resources/os.resources.json
   116  // var osInfo []byte
   117  
   118  %s
   119  
   120  func init() {
   121  	// builtinProviders[osconf.Config.ID] = &builtinProvider{
   122  	// 	Runtime: &RunningProvider{
   123  	// 		Name:     osconf.Config.Name,
   124  	// 		ID:       osconf.Config.ID,
   125  	// 		Plugin:   os.Init(),
   126  	// 		Schema:   MustLoadSchema("os", osInfo),
   127  	// 		isClosed: false,
   128  	// 	},
   129  	// 	Config: &osconf.Config,
   130  	// }
   131  %s
   132  }
   133  `
   134  
   135  func buildProviders(providers []string) {
   136  	for i, provider := range providers {
   137  		cmd := exec.Command("make", "providers/build/"+provider)
   138  		log.Debug().Str("provider", provider).Msg("build provider " + strconv.Itoa(i+1) + "/" + strconv.Itoa(len(providers)))
   139  		if err := cmd.Run(); err != nil {
   140  			log.Error().Err(err).Str("provider", provider).Msg("failed to build provider")
   141  		}
   142  
   143  		// inefficient copy...
   144  		src := "providers/" + provider + "/resources/" + provider + ".resources.json"
   145  		raw, err := os.ReadFile(src)
   146  		if err != nil {
   147  			log.Fatal().Err(err).Str("src", src).Msg("failed to read resources json")
   148  		}
   149  
   150  		dst := "providers/" + provider + ".resources.json"
   151  		err = os.WriteFile(dst, raw, 0o644)
   152  		if err != nil {
   153  			log.Fatal().Err(err).Str("dst", dst).Msg("failed to write resources json")
   154  		}
   155  	}
   156  }
   157  
   158  var (
   159  	reBuiltinReplace = regexp.MustCompile(`replace go.mondoo.com/cnquery/providers/.* => ./providers/.*`)
   160  	reBuiltinDep     = regexp.MustCompile(`go.mondoo.com/cnquery/providers/.*`)
   161  )
   162  
   163  func rewireDependencies(providers []string) {
   164  	raw, err := os.ReadFile("go.mod")
   165  	if err != nil {
   166  		log.Fatal().Err(err).Msg("failed to read go.mod")
   167  	}
   168  
   169  	raws := string(raw)
   170  	raws = reBuiltinReplace.ReplaceAllString(raws, "")
   171  	raws = reBuiltinDep.ReplaceAllString(raws, "")
   172  
   173  	deps := ""
   174  	replace := ""
   175  	for _, provider := range providers {
   176  		_, err := os.Stat("providers/" + provider + "/go.mod")
   177  		if err != nil {
   178  			if errors.Is(err, os.ErrNotExist) {
   179  				log.Info().Str("provider", provider).Msg("skipping provider without go.mod")
   180  				continue
   181  			} else {
   182  				log.Fatal().Err(err).Str("provider", provider).Msg("failed to stat provider go.mod")
   183  			}
   184  		}
   185  		// we don't care about the specific version for dev
   186  		deps += "\n\tgo.mondoo.com/cnquery/providers/" + provider + " v0.0.0"
   187  		replace += "\nreplace go.mondoo.com/cnquery/providers/" + provider + " => ./providers/" + provider
   188  	}
   189  	if deps != "" {
   190  		raws = strings.Replace(raws, "require (", "require ("+deps, 1)
   191  		raws = strings.Replace(raws, "module go.mondoo.com/cnquery", "module go.mondoo.com/cnquery\n"+replace, 1)
   192  	}
   193  
   194  	err = os.WriteFile("go.mod", []byte(raws), 0o644)
   195  	if err != nil {
   196  		log.Fatal().Err(err).Msg("failed to write go.mod")
   197  	}
   198  
   199  	cmd := exec.Command("go", "mod", "tidy")
   200  	log.Debug().Msg("go mod tidy")
   201  	if err := cmd.Run(); err != nil {
   202  		log.Fatal().Err(err).Msg("failed to go mod tidy")
   203  	}
   204  }
   205  
   206  func init() {
   207  	rootCmd.Flags().StringP("file", "f", "providers.yaml", "config file for providers")
   208  	rootCmd.Flags().StringP("output", "o", "providers/builtin_dev.go", "output go-file for builtin dev providers")
   209  }
   210  
   211  func main() {
   212  	logger.CliCompactLogger(logger.LogOutputWriter)
   213  	zerolog.SetGlobalLevel(zerolog.DebugLevel)
   214  
   215  	if err := rootCmd.Execute(); err != nil {
   216  		fmt.Fprintln(os.Stderr, err)
   217  		os.Exit(1)
   218  	}
   219  }