package couchctl import ( "flag" "fmt" "os" "time" "github.com/peterbourgon/ff/v3" "edge-infra.dev/pkg/edge/apis/meta" "edge-infra.dev/pkg/edge/constants/api/cluster" "edge-infra.dev/pkg/edge/constants/api/fleet" "edge-infra.dev/pkg/edge/datasync/couchdb" v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1" ) type Config struct { CompactRatio float64 RequeueTime time.Duration // Duration use to reconcile random errors ServerNotReady time.Duration // Duration use to wait for server to become ready DatabaseNotFound time.Duration // Duration use to reconcile for database not found IngressNotReady time.Duration // Duration use to reconcile for ingress not ready PollingInterval time.Duration // Duration use to check for resource updates ReplicationPollingInterval time.Duration // Duration use to check for new DB replication ReplicationChangesInterval time.Duration // Duration use to check for new DB replication EnablementWatchInterval time.Duration // Duration use to check for banner couchdb enablement ReplicationDBCreated time.Duration // Duration use to check db created by replication DatasyncDNSName string CouchDBPort string FleetType string ClusterType string BannerEdgeID string ProjectID string SiteID string // bslInfo.ID, only in stores CouchNamespace string CouchCTLNamespace string InterlockAPIURL string NodeUID string NodeClass string NodeRole string ReplicationEventFromSource bool ReconcileConcurrency int } func NewConfig() (*Config, error) { cfg := &Config{} fs := flag.NewFlagSet("couchctl", flag.ExitOnError) cfg.BindFlags(fs) if err := ff.Parse(fs, os.Args[1:], ff.WithEnvVarNoPrefix(), ff.WithIgnoreUndefined(true)); err != nil { return cfg, err } if err := cfg.Validate(); err != nil { return cfg, err } return cfg, nil } func (c *Config) BindFlags(fs *flag.FlagSet) { fs.Float64Var( &c.CompactRatio, "compact-ratio", 0, "compact db = sizes.file / sizes.active > compact-ratio, set to zero to disable", ) fs.IntVar( &c.ReconcileConcurrency, "reconcile-concurrency", 10, "reconcile concurrency for couch resources", ) fs.DurationVar( &c.RequeueTime, "requeue-time", 30*time.Second, "requeue on error", ) fs.DurationVar( &c.ServerNotReady, "server-not-ready", 30*time.Second, "server not ready requeue duration", ) fs.DurationVar( &c.DatabaseNotFound, "database-not-found", 30*time.Second, "database not found requeue duration", ) fs.DurationVar( &c.ReplicationDBCreated, "replication-db-created", 5*time.Second, "replication db created", ) fs.DurationVar( &c.IngressNotReady, "ingress-not-ready", 1*time.Minute, "Ingress not ready requeue duration", ) fs.DurationVar( &c.PollingInterval, "polling-interval", 2*time.Minute, "polling interval for couchdb resources etc..", ) fs.DurationVar( &c.ReplicationPollingInterval, "replication-polling-interval", 10*time.Minute, "polling interval for replication etc..", ) fs.DurationVar( &c.ReplicationChangesInterval, "replication-changes-interval", 30*time.Second, "polling interval for replication changes etc..", ) fs.DurationVar( &c.EnablementWatchInterval, "enablement-watch-interval", 2*time.Minute, "couchdb enablement watch interval, needed to start reconcilers", ) fs.StringVar( &c.DatasyncDNSName, "datasync-dns-name", "datasync-preprod.dev", "exact dns name know as shown in gcp, remove the ending dot(.). e.g.. 'datasync-preprod.dev', optional for stores", ) fs.StringVar( &c.CouchDBPort, "couchdb-port", couchdb.Port, "Couch DB port", ) fs.StringVar( &c.FleetType, "fleet-type", "", "Fleet Type", ) fs.StringVar( &c.ClusterType, "cluster-type", "", "Cluster Type", ) fs.StringVar( &c.BannerEdgeID, "banner-edge-id", "", "Cluster Banner Edge ID", ) fs.StringVar( &c.ProjectID, "project-id", "", "Cluster Project ID", ) fs.StringVar( &c.SiteID, "site-id", "", "BSL Info Site ID", ) fs.StringVar( &c.CouchNamespace, "couch-namespace", "data-sync-couchdb", "Couch DB namespace", ) fs.StringVar( &c.CouchCTLNamespace, "couchctl-namespace", "couchctl", "Couch CTL namespace", ) fs.StringVar( &c.InterlockAPIURL, "interlock-api-url", "interlock.interlock.svc.cluster.local", "Interlock API URL", ) fs.StringVar( &c.NodeUID, "node-uid", "", "Node UID", ) fs.StringVar( &c.NodeClass, "node-class", "", "Node Class", ) fs.StringVar( &c.NodeRole, "node-role", "", "Node Role", ) fs.BoolVar( &c.ReplicationEventFromSource, "replication-event-from-source", false, "Replication Event From Source", ) } func (c *Config) Validate() error { if c.FleetType == "" { return fmt.Errorf("missing FLEET_TYPE environment variable") } if c.ClusterType == "" { return fmt.Errorf("missing CLUSTER_TYPE environment variable") } if c.BannerEdgeID == "" { return fmt.Errorf("missing BANNER_EDGE_ID environment variable") } if c.ProjectID == "" { return fmt.Errorf("missing PROJECT_ID environment variable") } // some store don't have SiteID if c.RequeueTime == 0 { return fmt.Errorf("missing REQUEUE_TIME environment variable") } if c.ServerNotReady == 0 { return fmt.Errorf("missing SERVER_NOT_READY environment variable") } if c.DatabaseNotFound == 0 { return fmt.Errorf("missing DATABASE_NOT_FOUND environment variable") } if c.ReplicationDBCreated == 0 { return fmt.Errorf("missing REPLICATION_DB_CREATED environment variable") } if c.IngressNotReady == 0 { return fmt.Errorf("missing INGRESS_NOT_READY environment variable") } if c.PollingInterval == 0 { return fmt.Errorf("missing POLLING_INTERVAL environment variable") } if c.ReplicationPollingInterval == 0 { return fmt.Errorf("missing REPLICATION_POLLING_INTERVAL environment variable") } if c.ReplicationChangesInterval == 0 { return fmt.Errorf("missing REPLICATION_CHANGES_INTERVAL environment variable") } if c.EnablementWatchInterval == 0 { return fmt.Errorf("missing ENABLEMENT_WATCH_INTERVAL environment variable") } if c.CouchDBPort == "" { return fmt.Errorf("missing COUCHDB_PORT environment variable") } if c.DatasyncDNSName == "" { return fmt.Errorf("DATASYNC_DNS_NAME is not provided for couchdb masters") } if c.CouchNamespace == "" { return fmt.Errorf("missing COUCH_NAMESPACE environment variable") } if c.CouchCTLNamespace == "" { return fmt.Errorf("missing COUCHCTL_NAMESPACE environment variable") } if c.InterlockAPIURL == "" { return fmt.Errorf("missing INTERLOCK_API_URL environment variable") } if c.IsDSDS() { if c.NodeUID == "" { return fmt.Errorf("missing NODE_UID environment variable") } if c.NodeClass == "" { return fmt.Errorf("missing NODE_CLASS environment variable") } if c.NodeRole == "" { return fmt.Errorf("missing NODE_ROLE environment variable") } } return nil } func (c *Config) ReplicationDB() string { return couchdb.ReplicationDBPrefix + meta.Hash(c.BannerEdgeID) } func (c *Config) IsGKE() bool { return c.ClusterType == cluster.DSDS } func (c *Config) IsDSDS() bool { return c.ClusterType == cluster.DSDS } func (c *Config) IsGeneric() bool { return c.ClusterType == cluster.Generic } func (c *Config) IsStore() bool { return c.FleetType == fleet.Store || c.FleetType == fleet.BasicStore } func (c *Config) CloudURL() string { return fmt.Sprintf("https://%s.%s", c.BannerEdgeID, c.DatasyncDNSName) } func (c *Config) IsCPNode() bool { return v1ien.Role(c.NodeRole) == v1ien.ControlPlane }