...

Source file src/edge-infra.dev/pkg/edge/controllers/bannerctl/config.go

Documentation: edge-infra.dev/pkg/edge/controllers/bannerctl

     1  package bannerctl
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"errors"
     7  	"flag"
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/peterbourgon/ff/v3"
    12  
    13  	bsltypes "edge-infra.dev/pkg/edge/api/bsl/types"
    14  	"edge-infra.dev/pkg/edge/bsl"
    15  	"edge-infra.dev/pkg/edge/gcpinfra/constants"
    16  	"edge-infra.dev/pkg/lib/gcp/cloudsql"
    17  	"edge-infra.dev/pkg/lib/logging"
    18  )
    19  
    20  var (
    21  	// ErrNoBillingAccount occurs when no billing account is provided
    22  	ErrNoBillingAccount = errors.New("no billing account provided")
    23  	// ErrNoFolderID occurs when no folder id is provided
    24  	ErrNoFolderID = errors.New("no folder id provided")
    25  	// ErrNoDomain occurs when no domain name is provided
    26  	ErrNoDomain = errors.New("no domain provided")
    27  	// ErrNoDatasyncDNSName occurs when no datasync dns name is provided
    28  	ErrNoDatasyncDNSName = errors.New("datasync dns name is not provided")
    29  
    30  	// ErrNoDatasyncDNSZone occurs when no datasync dns zone is provided
    31  	ErrNoDatasyncDNSZone = errors.New("datasync dns zone is not provided")
    32  )
    33  
    34  // Config represents bannerctl configuration
    35  type Config struct {
    36  	ProjectBootstrapping     *ProjectBootstrappingConfig
    37  	ForemanProjectID         string
    38  	PlatInfraProjectID       string
    39  	EdgeAPI                  string
    40  	TotpSecret               string
    41  	Domain                   string
    42  	DatasyncDNSName          string
    43  	DatasyncDNSZone          string
    44  	MetricsAddr              string
    45  	DB                       *sql.DB
    46  	DatabaseName             string
    47  	BSLAccessKey             bsl.AccessKey
    48  	BSLConfig                bsltypes.BSPConfig
    49  	GCPRegion                string
    50  	GCPZone                  string
    51  	GCPForemanProjectNumber  string
    52  	EdgeSecOptInCompliance   bool
    53  	EdgeSecMaxLeasePeriod    string
    54  	EdgeSecMaxValidityPeriod string
    55  
    56  	RequeueTime     time.Duration
    57  	IntervalTime    time.Duration
    58  	ResourceTimeout time.Duration
    59  }
    60  
    61  // ProjectBootstrappingConfig represents required configuration for bannerctl to
    62  // bootstrap GCP projects for Edge:
    63  // - billing account for new projects
    64  // - folder id to add project to
    65  type ProjectBootstrappingConfig struct {
    66  	BillingAccount string
    67  	FolderID       string
    68  }
    69  
    70  // NewConfig adds the flags required for bannerctl to a FlagSet, which is
    71  // parsed by peterbourgon/ff to add support for environment variables,
    72  // returning a valid Config struct and the created FlagSet so it can be
    73  // bound to other consumers
    74  func NewConfig(args []string) (Config, *flag.FlagSet, error) {
    75  	// default values
    76  	config := Config{
    77  		ProjectBootstrapping: &ProjectBootstrappingConfig{},
    78  	}
    79  
    80  	fs := flag.NewFlagSet("bannerctl", flag.ExitOnError)
    81  
    82  	var SQLUser, SQLConnection string
    83  
    84  	fs.StringVar(
    85  		&config.ProjectBootstrapping.FolderID,
    86  		"gcp-tenants-folder-id",
    87  		"",
    88  		"folder id to store created gcp projects in",
    89  	)
    90  	fs.StringVar(
    91  		&config.ProjectBootstrapping.BillingAccount,
    92  		"gcp-billing-account",
    93  		constants.DefaultBillingAccountID,
    94  		"billing account id to use for created gcp projects",
    95  	)
    96  	fs.StringVar(
    97  		&config.ForemanProjectID,
    98  		"gcp-foreman-project-id",
    99  		"",
   100  		"foreman gcp project id that bannerctl will be running from -- not required "+
   101  			"for enterprise cluster deployments",
   102  	)
   103  	fs.StringVar(
   104  		&config.PlatInfraProjectID,
   105  		"gcp-plat-infra-project-id",
   106  		"",
   107  		"platform infrastructure (dns, artifact registry, etc) project",
   108  	)
   109  	fs.StringVar(
   110  		&config.EdgeAPI,
   111  		"edge-api",
   112  		"",
   113  		"api for edge env",
   114  	)
   115  	fs.StringVar(
   116  		&config.TotpSecret,
   117  		"totp-secret-key",
   118  		"",
   119  		"totp secret for validating totp token",
   120  	)
   121  	fs.StringVar(
   122  		&config.Domain,
   123  		"domain",
   124  		"",
   125  		"top level edge domain name",
   126  	)
   127  	fs.StringVar(
   128  		&config.DatasyncDNSName,
   129  		"datasync-dns-name",
   130  		"",
   131  		"exact dns name know as shown in gcp, remove the ending dot(.). e.g.. 'datasync-preprod.dev'",
   132  	)
   133  	fs.StringVar(
   134  		&config.DatasyncDNSZone,
   135  		"datasync-dns-zone",
   136  		"",
   137  		"datasync dns zone as shown in gcp console. e.g.. 'edge-preprod-datasync-dns-zone'",
   138  	)
   139  	fs.StringVar(
   140  		&config.MetricsAddr,
   141  		"metrics-address",
   142  		":8080",
   143  		"Address to bind metrics endpoint (/metrics) to",
   144  	)
   145  	fs.StringVar(
   146  		&config.DatabaseName,
   147  		"sql-db-name",
   148  		"",
   149  		"SQL Database Name",
   150  	)
   151  	fs.StringVar(
   152  		&SQLUser,
   153  		"sql-user",
   154  		"",
   155  		"SQL User",
   156  	)
   157  	fs.StringVar(
   158  		&SQLConnection,
   159  		"sql-connection-name",
   160  		"",
   161  		"SQL Connection Name",
   162  	)
   163  
   164  	fs.StringVar(&config.BSLAccessKey.SecretKey,
   165  		"edge-bsl-secret-key",
   166  		"",
   167  		"secret key for access the bsl",
   168  	)
   169  
   170  	fs.StringVar(&config.BSLAccessKey.SharedKey,
   171  		"edge-bsl-shared-key",
   172  		"",
   173  		"shared key for access the bsl",
   174  	)
   175  
   176  	fs.StringVar(
   177  		&config.BSLConfig.Endpoint,
   178  		"bsl-endpoint",
   179  		"",
   180  		"bsl endpoint for a specific environment",
   181  	)
   182  
   183  	fs.StringVar(
   184  		&config.BSLConfig.Root,
   185  		"bsl-root-org",
   186  		"",
   187  		"bsl root org",
   188  	)
   189  
   190  	fs.StringVar(
   191  		&config.BSLConfig.OrganizationPrefix,
   192  		"bsp-organization-prefix",
   193  		"",
   194  		"bsl organization prefix for a specific environment",
   195  	)
   196  	fs.StringVar(
   197  		&config.GCPRegion,
   198  		"gcp-region",
   199  		"",
   200  		"GCP region - ex: us-east1",
   201  	)
   202  	fs.StringVar(
   203  		&config.GCPZone,
   204  		"gcp-zone",
   205  		"",
   206  		"GCP zone - ex: a/b/c",
   207  	)
   208  
   209  	fs.StringVar(
   210  		&config.GCPForemanProjectNumber,
   211  		"gcp-foreman-project-number",
   212  		"",
   213  		"GCP Project Number for all Foreman Projects",
   214  	)
   215  
   216  	fs.BoolVar(
   217  		&config.EdgeSecOptInCompliance,
   218  		"edge-sec-opt-in-compliance",
   219  		false,
   220  		"Edge Security compliance setting",
   221  	)
   222  
   223  	fs.StringVar(
   224  		&config.EdgeSecMaxLeasePeriod,
   225  		"edge-sec-max-lease-period",
   226  		"",
   227  		"Maximum secret lease period",
   228  	)
   229  
   230  	fs.StringVar(
   231  		&config.EdgeSecMaxValidityPeriod,
   232  		"edge-sec-max-validity-period",
   233  		"",
   234  		"Maximum secret validity period",
   235  	)
   236  
   237  	fs.DurationVar(
   238  		&config.IntervalTime,
   239  		"interval-time",
   240  		2*time.Minute,
   241  		"interval for processing",
   242  	)
   243  	fs.DurationVar(
   244  		&config.RequeueTime,
   245  		"requeue-time",
   246  		20*time.Second,
   247  		"requeue interval for errors",
   248  	)
   249  	fs.DurationVar(
   250  		&config.ResourceTimeout,
   251  		"resource-timeout",
   252  		20*time.Second,
   253  		"timeout for kms resource",
   254  	)
   255  
   256  	if err := ff.Parse(fs, args[1:], ff.WithEnvVarNoPrefix()); err != nil {
   257  		return Config{}, &flag.FlagSet{}, err
   258  	}
   259  
   260  	// Connect bannerctl to database
   261  	log := logging.NewLogger().WithName("bannerctl")
   262  	db, err := cloudsql.GCPPostgresConnection(SQLConnection).
   263  		DBName(config.DatabaseName).
   264  		Username(SQLUser).
   265  		NewConnection()
   266  	// Log database errors, without exiting, for alerting purposes. Infra status recording is not a critical feature.
   267  	if err != nil {
   268  		log.Error(err, "could not create sql connection")
   269  	} else {
   270  		var pingCtx, cancel = context.WithTimeout(context.Background(), 3*time.Second)
   271  		if err := db.PingContext(pingCtx); err != nil {
   272  			log.Error(err, "could not ping sql connection")
   273  		} else {
   274  			config.DB = db
   275  		}
   276  		cancel()
   277  	}
   278  	log.Info("Infra status recording feature", "enabled", config.DB != nil)
   279  
   280  	if err := config.Validate(); err != nil {
   281  		return Config{}, &flag.FlagSet{}, err
   282  	}
   283  
   284  	return config, fs, nil
   285  }
   286  
   287  // Validation checks for configuration
   288  func (f Config) Validate() error {
   289  	if f.Domain == "" {
   290  		return fmt.Errorf("%w", ErrNoDomain)
   291  	}
   292  	if f.DatasyncDNSName == "" {
   293  		return ErrNoDatasyncDNSName
   294  	}
   295  	if f.DatasyncDNSZone == "" {
   296  		return ErrNoDatasyncDNSZone
   297  	}
   298  
   299  	if f.BSLAccessKey.SecretKey == "" {
   300  		return fmt.Errorf("misisng bsl access secret key")
   301  	}
   302  
   303  	if f.BSLAccessKey.SharedKey == "" {
   304  		return fmt.Errorf("missing bsl access shared key")
   305  	}
   306  
   307  	if f.BSLConfig.Endpoint == "" {
   308  		return fmt.Errorf("missing bsl endpoint")
   309  	}
   310  
   311  	if f.BSLConfig.Root == "" {
   312  		return fmt.Errorf("missing bsl root org")
   313  	}
   314  
   315  	if f.BSLConfig.OrganizationPrefix == "" {
   316  		return fmt.Errorf("missing bsl org prefix")
   317  	}
   318  	if f.GCPRegion == "" {
   319  		return fmt.Errorf("missing gcp region")
   320  	}
   321  	if f.GCPZone == "" {
   322  		return fmt.Errorf("missing gcp zone")
   323  	}
   324  	if f.GCPForemanProjectNumber == "" {
   325  		return fmt.Errorf("missing gcp foreman project number")
   326  	}
   327  	if f.EdgeSecMaxLeasePeriod == "" {
   328  		return fmt.Errorf("missing edge security max secret lease period")
   329  	}
   330  	if f.EdgeSecMaxValidityPeriod == "" {
   331  		return fmt.Errorf("missing edge security max secret validity period")
   332  	}
   333  
   334  	if f.IntervalTime == 0 {
   335  		return fmt.Errorf("missing INTERVAL_TIME environment variable")
   336  	}
   337  	if f.RequeueTime == 0 {
   338  		return fmt.Errorf("missing REQUEUE_TIME environment variable")
   339  	}
   340  	if f.ResourceTimeout == 0 {
   341  		return fmt.Errorf("missing RESOURCE_TIMEOUT environment variable")
   342  	}
   343  
   344  	return f.ProjectBootstrapping.Validate()
   345  }
   346  
   347  // Validate validates the config required for bootstrapping tenant projects.
   348  // If it isn't enabled, it performs no validation.
   349  func (f ProjectBootstrappingConfig) Validate() error {
   350  	if f.BillingAccount == "" {
   351  		return fmt.Errorf("%w", ErrNoBillingAccount)
   352  	}
   353  
   354  	if f.FolderID == "" {
   355  		return fmt.Errorf("%w", ErrNoFolderID)
   356  	}
   357  
   358  	return nil
   359  }
   360  

View as plain text