diff --git a/.golangci.yml b/.golangci.yml index 4232a8c..32ac564 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -23,6 +23,18 @@ linters: - unconvert - unused + settings: + goconst: + # Default is 3, which flags many genuinely-incidental repeats. + # Bump to 4 so only repeated strings that clearly warrant a constant get flagged. + min-occurrences: 4 + # Skip strings that are too generic to meaningfully name as constants. + ignore-string-values: + - "true" + - "false" + - "Config" + - "name" + exclusions: rules: # Test files can have repeated strings for readability diff --git a/pkg/argocd/config.go b/pkg/argocd/config.go index bb78e5d..556a119 100644 --- a/pkg/argocd/config.go +++ b/pkg/argocd/config.go @@ -2,6 +2,11 @@ package argocd import "time" +const ( + defaultChartVersion = "9.4.1" + defaultNamespace = "argocd" +) + // Config holds configuration for Argo CD installation type Config struct { // Version is the Argo CD chart version to install. @@ -27,9 +32,9 @@ type Config struct { // DefaultConfig returns the default Argo CD configuration func DefaultConfig() Config { return Config{ - Version: "9.4.1", // Chart version that installs Argo CD v3.3.0 - Namespace: "argocd", - ReleaseName: "argocd", + Version: defaultChartVersion, // Chart version that installs Argo CD v3.3.0 + Namespace: defaultNamespace, + ReleaseName: defaultNamespace, Timeout: 5 * time.Minute, Values: map[string]any{ // Run in insecure mode since TLS is terminated at the gateway diff --git a/pkg/argocd/helm.go b/pkg/argocd/helm.go index 8602c5c..565f9d5 100644 --- a/pkg/argocd/helm.go +++ b/pkg/argocd/helm.go @@ -17,9 +17,10 @@ import ( ) const ( - repoName = "argo" - repoURL = "https://argoproj.github.io/argo-helm" - chartName = "argo/argo-cd" + repoName = "argo" + repoURL = "https://argoproj.github.io/argo-helm" + chartName = "argo/argo-cd" + versionUnknown = "unknown" ) // shouldSkipUpgrade determines if a Helm upgrade can be skipped because @@ -38,10 +39,10 @@ func shouldSkipUpgrade(current *release.Release, targetVersion string) bool { } // getCurrentVersion safely extracts the chart version from a Helm release. -// Returns "unknown" if the release, chart, or metadata is nil. +// Returns versionUnknown if the release, chart, or metadata is nil. func getCurrentVersion(current *release.Release) string { if current == nil || current.Chart == nil || current.Chart.Metadata == nil { - return "unknown" + return versionUnknown } return current.Chart.Metadata.Version } diff --git a/pkg/argocd/install.go b/pkg/argocd/install.go index e6c6a5c..6bf1df3 100644 --- a/pkg/argocd/install.go +++ b/pkg/argocd/install.go @@ -17,6 +17,11 @@ import ( "github.com/nebari-dev/nebari-infrastructure-core/pkg/status" ) +const ( + argoCDServerDeployment = "argocd-server" + localGitopsVolumeName = "local-gitops" +) + // Install installs Argo CD on a Kubernetes cluster // This is the main entry point called from cmd/nic/deploy.go // If cfg.GitRepository is a local file:// path, the directory is mounted into the repo-server pod. @@ -311,7 +316,7 @@ func waitForArgoCDReadyWithLister(ctx context.Context, listDeployments Deploymen // waitForArgoCDReady waits for Argo CD deployments to be ready using a Kubernetes client. func waitForArgoCDReady(ctx context.Context, client *kubernetes.Clientset, namespace string, timeout time.Duration) error { requiredDeployments := []string{ - "argocd-server", + argoCDServerDeployment, "argocd-repo-server", } @@ -346,7 +351,7 @@ func addLocalGitopsMount(ctx context.Context, values map[string]any, localPath s // Append to existing volumes (handle both []map[string]any and []any from YAML parsing) newVolume := map[string]any{ - "name": "local-gitops", + "name": localGitopsVolumeName, "hostPath": map[string]any{ "path": localPath, "type": "Directory", @@ -356,7 +361,7 @@ func addLocalGitopsMount(ctx context.Context, values map[string]any, localPath s // Append to existing volumeMounts newMount := map[string]any{ - "name": "local-gitops", + "name": localGitopsVolumeName, "mountPath": localPath, "readOnly": true, } diff --git a/pkg/argocd/secrets.go b/pkg/argocd/secrets.go index 0cdb5a2..b45e51f 100644 --- a/pkg/argocd/secrets.go +++ b/pkg/argocd/secrets.go @@ -18,6 +18,8 @@ const ( gitRepoType = "git" // gitTokenUsername is the username used with token-based auth for git providers (GitHub, GitLab, etc.) gitTokenUsername = "git" + // argoCDSecretTypeRepository is the value for the argocd.argoproj.io/secret-type label on repository secrets. + argoCDSecretTypeRepository = "repository" ) // ConfigureGitRepoAccess configures Argo CD to access the GitOps repository @@ -59,7 +61,7 @@ func ConfigureGitRepoAccess(ctx context.Context, client kubernetes.Interface, cf Name: "gitops-repo-creds", Namespace: namespace, Labels: map[string]string{ - "argocd.argoproj.io/secret-type": "repository", + "argocd.argoproj.io/secret-type": argoCDSecretTypeRepository, }, }, Type: corev1.SecretTypeOpaque, @@ -109,7 +111,7 @@ func ConfigureOCIAccess(ctx context.Context, client kubernetes.Interface, namesp Name: "docker-oci-repo", Namespace: namespace, Labels: map[string]string{ - "argocd.argoproj.io/secret-type": "repository", + "argocd.argoproj.io/secret-type": argoCDSecretTypeRepository, }, }, Type: corev1.SecretTypeOpaque, diff --git a/pkg/argocd/writer.go b/pkg/argocd/writer.go index 241014e..e745cc7 100644 --- a/pkg/argocd/writer.go +++ b/pkg/argocd/writer.go @@ -25,7 +25,12 @@ import ( //go:embed templates var templates embed.FS -const templateDir = "templates" +const ( + templateDir = "templates" + // certificateIssuerSelfSigned is the cert-manager Issuer used when the user has + // not configured a real ACME provider. + certificateIssuerSelfSigned = "selfsigned-issuer" +) // TemplateData holds the dynamic values for template processing type TemplateData struct { @@ -111,10 +116,10 @@ func NewTemplateData(cfg *config.NebariConfig, settings provider.InfraSettings) } } } else { - data.CertificateIssuer = "selfsigned-issuer" + data.CertificateIssuer = certificateIssuerSelfSigned } } else { - data.CertificateIssuer = "selfsigned-issuer" + data.CertificateIssuer = certificateIssuerSelfSigned } // Default domain if not set diff --git a/pkg/provider/aws/kubeconfig.go b/pkg/provider/aws/kubeconfig.go index e3111b8..bc1669c 100644 --- a/pkg/provider/aws/kubeconfig.go +++ b/pkg/provider/aws/kubeconfig.go @@ -7,6 +7,16 @@ import ( "github.com/goccy/go-yaml" ) +// AWS IAM Authenticator constants used to build EKS kubeconfigs. +// See: https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html +const ( + execAPIVersion = "client.authentication.k8s.io/v1beta1" + eksSubcommand = "eks" + eksGetTokenCmd = "get-token" + clusterNameFlag = "--cluster-name" + regionFlag = "--region" +) + // KubeconfigCluster represents the cluster section of kubeconfig type KubeconfigCluster struct { Server string `yaml:"server"` @@ -109,14 +119,14 @@ func buildKubeconfig(clusterName, endpoint, caData, region string) ([]byte, erro Name: clusterName, User: KubeconfigUser{ Exec: KubeconfigExec{ - APIVersion: "client.authentication.k8s.io/v1beta1", - Command: "aws", + APIVersion: execAPIVersion, + Command: ProviderName, Args: []string{ - "eks", - "get-token", - "--cluster-name", + eksSubcommand, + eksGetTokenCmd, + clusterNameFlag, clusterName, - "--region", + regionFlag, region, }, }, diff --git a/pkg/provider/aws/longhorn.go b/pkg/provider/aws/longhorn.go index e4eac4c..3145c25 100644 --- a/pkg/provider/aws/longhorn.go +++ b/pkg/provider/aws/longhorn.go @@ -23,6 +23,10 @@ import ( "github.com/nebari-dev/nebari-infrastructure-core/pkg/status" ) +// nodeStorageLabel is the node label Longhorn uses to identify nodes that should +// host its storage components. +const nodeStorageLabel = "node.longhorn.io/storage" + const ( longhornRepoName = "longhorn" longhornRepoURL = "https://charts.longhorn.io" @@ -247,14 +251,14 @@ func longhornHelmValues(cfg *Config) map[string]any { if cfg.Longhorn != nil && cfg.Longhorn.DedicatedNodes { settings["createDefaultDiskLabeledNodes"] = true - nodeSelector := map[string]string{"node.longhorn.io/storage": "true"} + nodeSelector := map[string]string{nodeStorageLabel: "true"} if cfg.Longhorn.NodeSelector != nil { nodeSelector = cfg.Longhorn.NodeSelector } tolerations := []map[string]string{ { - "key": "node.longhorn.io/storage", + "key": nodeStorageLabel, "operator": "Exists", "effect": "NoSchedule", }, diff --git a/pkg/provider/aws/tofu.go b/pkg/provider/aws/tofu.go index e6fd1ad..f34fe36 100644 --- a/pkg/provider/aws/tofu.go +++ b/pkg/provider/aws/tofu.go @@ -7,6 +7,13 @@ import "embed" //go:embed all:templates var tofuTemplates embed.FS +// Security group rule keys for Longhorn webhook traffic between the EKS control plane +// and worker nodes. +const ( + longhornWebhookAdmissionKey = "longhorn_webhook_admission" + longhornWebhookConversionKey = "longhorn_webhook_conversion" +) + type TFVars struct { Region string `json:"region"` ProjectName string `json:"project_name,omitempty"` @@ -100,7 +107,7 @@ func (c *Config) toTFVars(projectName string) TFVars { if c.LonghornEnabled() { vars.NodeSGAdditionalRules = map[string]any{ - "longhorn_webhook_admission": map[string]any{ + longhornWebhookAdmissionKey: map[string]any{ "description": "Cluster API to Longhorn admission webhook", "protocol": "tcp", "from_port": 9502, @@ -108,7 +115,7 @@ func (c *Config) toTFVars(projectName string) TFVars { "type": "ingress", "source_cluster_security_group": true, }, - "longhorn_webhook_conversion": map[string]any{ + longhornWebhookConversionKey: map[string]any{ "description": "Cluster API to Longhorn conversion webhook", "protocol": "tcp", "from_port": 9501, diff --git a/pkg/provider/hetzner/config.go b/pkg/provider/hetzner/config.go index 56e7699..349fbb1 100644 --- a/pkg/provider/hetzner/config.go +++ b/pkg/provider/hetzner/config.go @@ -7,6 +7,9 @@ import ( "strings" ) +// allCIDRs is the default CIDR allowlist that opens access to the entire internet. +const allCIDRs = "0.0.0.0/0" + // Config holds Hetzner-specific provider configuration. // Parsed from the "hetzner_cloud" key in nebari-config.yaml. type Config struct { @@ -116,7 +119,7 @@ func (c *Config) SSHAllowedNetworks() []string { if c.Network != nil && len(c.Network.SSHAllowedCIDRs) > 0 { return c.Network.SSHAllowedCIDRs } - return []string{"0.0.0.0/0"} + return []string{allCIDRs} } // APIAllowedNetworks returns the configured API CIDR ranges, defaulting to 0.0.0.0/0. @@ -124,7 +127,7 @@ func (c *Config) APIAllowedNetworks() []string { if c.Network != nil && len(c.Network.APIAllowedCIDRs) > 0 { return c.Network.APIAllowedCIDRs } - return []string{"0.0.0.0/0"} + return []string{allCIDRs} } // IsExplicitK3sVersion returns true if the kubernetes_version already contains diff --git a/pkg/provider/local/provider.go b/pkg/provider/local/provider.go index 1a25440..9ceb062 100644 --- a/pkg/provider/local/provider.go +++ b/pkg/provider/local/provider.go @@ -16,6 +16,15 @@ import ( "github.com/nebari-dev/nebari-infrastructure-core/pkg/status" ) +const ( + // ProviderName is the identifier for the local provider. + ProviderName = "local" + // defaultStorageClass is the StorageClass name K3s installs by default. + defaultStorageClass = "standard" + // defaultMetalLBAddressPool is the address range for the default MetalLB pool. + defaultMetalLBAddressPool = "192.168.1.100-192.168.1.110" +) + // Provider implements the local K3s provider type Provider struct{} @@ -26,7 +35,7 @@ func NewProvider() *Provider { // Name returns the provider name func (p *Provider) Name() string { - return "local" + return ProviderName } // Validate validates the local configuration @@ -36,7 +45,7 @@ func (p *Provider) Validate(ctx context.Context, projectName string, clusterConf defer span.End() span.SetAttributes( - attribute.String("provider", "local"), + attribute.String("provider", ProviderName), attribute.String("project_name", projectName), ) @@ -119,7 +128,7 @@ func (p *Provider) Deploy(ctx context.Context, projectName string, clusterConfig defer span.End() span.SetAttributes( - attribute.String("provider", "local"), + attribute.String("provider", ProviderName), attribute.String("project_name", projectName), ) @@ -153,7 +162,7 @@ func (p *Provider) Destroy(ctx context.Context, projectName string, _ *config.Cl defer span.End() span.SetAttributes( - attribute.String("provider", "local"), + attribute.String("provider", ProviderName), attribute.String("project_name", projectName), ) @@ -171,7 +180,7 @@ func (p *Provider) GetKubeconfig(ctx context.Context, projectName string, cluste defer span.End() span.SetAttributes( - attribute.String("provider", "local"), + attribute.String("provider", ProviderName), attribute.String("cluster_name", projectName), ) @@ -273,9 +282,9 @@ func (p *Provider) Summary(clusterConfig *config.ClusterConfig) map[string]strin // tests), we return valid defaults. func (p *Provider) InfraSettings(cfg *config.ClusterConfig) provider.InfraSettings { settings := provider.InfraSettings{ - StorageClass: "standard", + StorageClass: defaultStorageClass, NeedsMetalLB: true, - MetalLBAddressPool: "192.168.1.100-192.168.1.110", + MetalLBAddressPool: defaultMetalLBAddressPool, SupportsLocalGitOps: true, }