github.com/jenkins-x/jx-api@v0.0.24/pkg/config/install_requirements.go (about) 1 package config 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/jenkins-x/jx-api/pkg/cloud" 8 "github.com/jenkins-x/jx-api/pkg/util" 9 "github.com/jenkins-x/jx-logging/pkg/log" 10 11 "io/ioutil" 12 "os" 13 "path" 14 "path/filepath" 15 "reflect" 16 "strings" 17 18 "github.com/ghodss/yaml" 19 "github.com/imdario/mergo" 20 "github.com/pkg/errors" 21 "github.com/vrischmann/envconfig" 22 23 v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1" 24 ) 25 26 var ( 27 // autoDNSSuffixes the DNS suffixes of any auto-DNS services 28 autoDNSSuffixes = []string{ 29 ".nip.io", 30 ".xip.io", 31 ".beesdns.com", 32 } 33 ) 34 35 const ( 36 // DefaultFailOnValidationError by default fail if validation fails when reading jx-requirements 37 DefaultFailOnValidationError = true 38 39 constTrue = "true" 40 constFalse = "false" 41 ) 42 43 const ( 44 // RequirementsConfigFileName is the name of the requirements configuration file 45 RequirementsConfigFileName = "jx-requirements.yml" 46 // RequirementsValuesFileName is the name of the requirements configuration file 47 RequirementsValuesFileName = "jx-requirements.values.yaml.gotmpl" 48 // RequirementDomainIssuerUsername contains the username used for basic auth when requesting a domain 49 RequirementDomainIssuerUsername = "JX_REQUIREMENT_DOMAIN_ISSUER_USERNAME" 50 // RequirementDomainIssuerPassword contains the password used for basic auth when requesting a domain 51 RequirementDomainIssuerPassword = "JX_REQUIREMENT_DOMAIN_ISSUER_PASSWORD" 52 // RequirementDomainIssuerURL contains the URL to the service used when requesting a domain 53 RequirementDomainIssuerURL = "JX_REQUIREMENT_DOMAIN_ISSUER_URL" 54 // RequirementClusterName is the cluster name 55 RequirementClusterName = "JX_REQUIREMENT_CLUSTER_NAME" 56 // RequirementProject is the cloudprovider project 57 RequirementProject = "JX_REQUIREMENT_PROJECT" 58 // RequirementZone zone the cluster is in 59 RequirementZone = "JX_REQUIREMENT_ZONE" 60 // RequirementEnvGitOwner the default git owner for environment repositories if none is specified explicitly 61 RequirementEnvGitOwner = "JX_REQUIREMENT_ENV_GIT_OWNER" 62 // RequirementEnvGitPublic sets the visibility of the environment repositories as private (subscription required for GitHub Organisations) 63 RequirementEnvGitPublic = "JX_REQUIREMENT_ENV_GIT_PUBLIC" 64 // RequirementGitPublic sets the visibility of the application repositories as private (subscription required for GitHub Organisations) 65 RequirementGitPublic = "JX_REQUIREMENT_GIT_PUBLIC" 66 // RequirementExternalDNSServiceAccountName the service account name for external dns 67 RequirementExternalDNSServiceAccountName = "JX_REQUIREMENT_EXTERNALDNS_SA_NAME" 68 // RequirementVaultName the name for vault 69 RequirementVaultName = "JX_REQUIREMENT_VAULT_NAME" 70 // RequirementVaultServiceAccountName the service account name for vault 71 RequirementVaultServiceAccountName = "JX_REQUIREMENT_VAULT_SA_NAME" 72 // RequirementVeleroServiceAccountName the service account name for velero 73 RequirementVeleroServiceAccountName = "JX_REQUIREMENT_VELERO_SA_NAME" 74 // RequirementVeleroTTL defines the time to live (TTL) for the Velero backups in minutes 75 RequirementVeleroTTL = "JX_REQUIREMENT_VELERO_TTL" 76 // RequirementVeleroSchedule defines the schedule of the Velero backups in cron notation 77 RequirementVeleroSchedule = "JX_REQUIREMENT_VELERO_SCHEDULE" 78 // RequirementVaultKeyringName the keyring name for vault 79 RequirementVaultKeyringName = "JX_REQUIREMENT_VAULT_KEYRING_NAME" 80 // RequirementVaultKeyName the key name for vault 81 RequirementVaultKeyName = "JX_REQUIREMENT_VAULT_KEY_NAME" 82 // RequirementVaultBucketName the vault name for vault 83 RequirementVaultBucketName = "JX_REQUIREMENT_VAULT_BUCKET_NAME" 84 // RequirementVaultRecreateBucket recreate the bucket that vault uses 85 RequirementVaultRecreateBucket = "JX_REQUIREMENT_VAULT_RECREATE_BUCKET" 86 // RequirementVaultDisableURLDiscovery override the default lookup of the Vault URL, could be incluster service or external ingress 87 RequirementVaultDisableURLDiscovery = "JX_REQUIREMENT_VAULT_DISABLE_URL_DISCOVERY" 88 // RequirementSecretStorageType the secret storage type 89 RequirementSecretStorageType = "JX_REQUIREMENT_SECRET_STORAGE_TYPE" 90 // RequirementKanikoServiceAccountName the service account name for kaniko 91 RequirementKanikoServiceAccountName = "JX_REQUIREMENT_KANIKO_SA_NAME" 92 // RequirementKaniko if kaniko is required 93 RequirementKaniko = "JX_REQUIREMENT_KANIKO" 94 // RequirementIngressTLSProduction use the lets encrypt production server 95 RequirementIngressTLSProduction = "JX_REQUIREMENT_INGRESS_TLS_PRODUCTION" 96 // RequirementChartRepository the helm chart repository for jx 97 RequirementChartRepository = "JX_REQUIREMENT_CHART_REPOSITORY" 98 // RequirementRegistry the container registry for jx 99 RequirementRegistry = "JX_REQUIREMENT_REGISTRY" 100 // RequirementRepository the artifact repository for jx 101 RequirementRepository = "JX_REQUIREMENT_REPOSITORY" 102 // RequirementWebhook the webhook handler for jx 103 RequirementWebhook = "JX_REQUIREMENT_WEBHOOK" 104 // RequirementStorageBackupEnabled if backup storage is required 105 RequirementStorageBackupEnabled = "JX_REQUIREMENT_STORAGE_BACKUP_ENABLED" 106 // RequirementStorageBackupURL backup storage url 107 RequirementStorageBackupURL = "JX_REQUIREMENT_STORAGE_BACKUP_URL" 108 // RequirementStorageLogsEnabled if log storage is required 109 RequirementStorageLogsEnabled = "JX_REQUIREMENT_STORAGE_LOGS_ENABLED" 110 // RequirementStorageLogsURL logs storage url 111 RequirementStorageLogsURL = "JX_REQUIREMENT_STORAGE_LOGS_URL" 112 // RequirementStorageReportsEnabled if report storage is required 113 RequirementStorageReportsEnabled = "JX_REQUIREMENT_STORAGE_REPORTS_ENABLED" 114 // RequirementStorageReportsURL report storage url 115 RequirementStorageReportsURL = "JX_REQUIREMENT_STORAGE_REPORTS_URL" 116 // RequirementStorageRepositoryEnabled if repository storage is required 117 RequirementStorageRepositoryEnabled = "JX_REQUIREMENT_STORAGE_REPOSITORY_ENABLED" 118 // RequirementStorageRepositoryURL repository storage url 119 RequirementStorageRepositoryURL = "JX_REQUIREMENT_STORAGE_REPOSITORY_URL" 120 // RequirementGkeProjectNumber is the gke project number 121 RequirementGkeProjectNumber = "JX_REQUIREMENT_GKE_PROJECT_NUMBER" 122 // RequirementGitAppEnabled if the github app should be used for access tokens 123 RequirementGitAppEnabled = "JX_REQUIREMENT_GITHUB_APP_ENABLED" 124 // RequirementGitAppURL contains the URL to the github app 125 RequirementGitAppURL = "JX_REQUIREMENT_GITHUB_APP_URL" 126 // RequirementDevEnvApprovers contains the optional list of users to populate the dev env's OWNERS with 127 RequirementDevEnvApprovers = "JX_REQUIREMENT_DEV_ENV_APPROVERS" 128 // RequirementVersionsGitRef contains the git ref of the version stream 129 RequirementVersionsGitRef = "JX_REQUIREMENT_VERSIONS_GIT_REF" 130 ) 131 132 const ( 133 // BootDeployNamespace environment variable for deployment namespace 134 BootDeployNamespace = "DEPLOY_NAMESPACE" 135 ) 136 137 // SecretStorageType is the type of storage used for secrets 138 type SecretStorageType string 139 140 const ( 141 // SecretStorageTypeVault specifies that we use vault to store secrets 142 SecretStorageTypeVault SecretStorageType = "vault" 143 // SecretStorageTypeLocal specifies that we use the local file system in 144 // `~/.jx/localSecrets` to store secrets 145 SecretStorageTypeLocal SecretStorageType = "local" 146 ) 147 148 // SecretStorageTypeValues the string values for the secret storage 149 var SecretStorageTypeValues = []string{string(SecretStorageTypeLocal), string(SecretStorageTypeVault)} 150 151 // WebhookType is the type of a webhook strategy 152 type WebhookType string 153 154 const ( 155 // WebhookTypeNone if we have yet to define a webhook 156 WebhookTypeNone WebhookType = "" 157 // WebhookTypeProw specifies that we use prow for webhooks 158 // see: https://github.com/kubernetes/test-infra/tree/master/prow 159 WebhookTypeProw WebhookType = "prow" 160 // WebhookTypeLighthouse specifies that we use lighthouse for webhooks 161 // see: https://github.com/jenkins-x/lighthouse 162 WebhookTypeLighthouse WebhookType = "lighthouse" 163 // WebhookTypeJenkins specifies that we use jenkins webhooks 164 WebhookTypeJenkins WebhookType = "jenkins" 165 ) 166 167 // WebhookTypeValues the string values for the webhook types 168 var WebhookTypeValues = []string{string(WebhookTypeJenkins), string(WebhookTypeLighthouse), string(WebhookTypeProw)} 169 170 // RepositoryType is the type of a repository we use to store artifacts (jars, tarballs, npm packages etc) 171 type RepositoryType string 172 173 const ( 174 // RepositoryTypeUnknown if we have yet to configure a repository 175 RepositoryTypeUnknown RepositoryType = "" 176 // RepositoryTypeArtifactory if you wish to use Artifactory as the artifact repository 177 RepositoryTypeArtifactory RepositoryType = "artifactory" 178 // RepositoryTypeBucketRepo if you wish to use bucketrepo as the artifact repository. see https://github.com/jenkins-x/bucketrepo 179 RepositoryTypeBucketRepo RepositoryType = "bucketrepo" 180 // RepositoryTypeNone if you do not wish to install an artifact repository 181 RepositoryTypeNone RepositoryType = "none" 182 // RepositoryTypeNexus if you wish to use Sonatype Nexus as the artifact repository 183 RepositoryTypeNexus RepositoryType = "nexus" 184 ) 185 186 // RepositoryTypeValues the string values for the repository types 187 var RepositoryTypeValues = []string{string(RepositoryTypeNone), string(RepositoryTypeBucketRepo), string(RepositoryTypeNexus), string(RepositoryTypeArtifactory)} 188 189 const ( 190 // DefaultProfileFile location of profle config 191 DefaultProfileFile = "profile.yaml" 192 // OpenSourceProfile constant for OSS profile 193 OpenSourceProfile = "oss" 194 // CloudBeesProfile constant for CloudBees profile 195 CloudBeesProfile = "cloudbees" 196 ) 197 198 // Overrideable at build time - see Makefile 199 var ( 200 // DefaultVersionsURL default version stream url 201 DefaultVersionsURL = "https://github.com/jenkins-x/jenkins-x-versions.git" 202 // DefaultVersionsRef default version stream ref 203 DefaultVersionsRef = "master" 204 // DefaultBootRepository default git repo for boot 205 DefaultBootRepository = "https://github.com/jenkins-x/jenkins-x-boot-config.git" 206 // LatestVersionStringsBucket optional bucket name to search in for latest version strings 207 LatestVersionStringsBucket = "" 208 // BinaryDownloadBaseURL the base URL for downloading the binary from - will always have "VERSION/jx-OS-ARCH.EXTENSION" appended to it when used 209 BinaryDownloadBaseURL = "https://github.com/jenkins-x/jx/releases/download/v" 210 // TLSDocURL the URL presented by `jx step verify preinstall` for documentation on configuring TLS 211 TLSDocURL = "https://jenkins-x.io/docs/getting-started/setup/boot/#ingress" 212 ) 213 214 // EnvironmentConfig configures the organisation and repository name of the git repositories for environments 215 type EnvironmentConfig struct { 216 // Key is the key of the environment configuration 217 Key string `json:"key,omitempty"` 218 // Owner is the git user or organisation for the repository 219 Owner string `json:"owner,omitempty"` 220 // Repository is the name of the repository within the owner 221 Repository string `json:"repository,omitempty"` 222 // GitServer is the URL of the git server 223 GitServer string `json:"gitServer,omitempty"` 224 // GitKind is the kind of git server (github, bitbucketserver etc) 225 GitKind string `json:"gitKind,omitempty"` 226 // Ingress contains ingress specific requirements 227 Ingress IngressConfig `json:"ingress,omitempty"` 228 // RemoteCluster specifies this environment runs on a remote cluster to the development cluster 229 RemoteCluster bool `json:"remoteCluster,omitempty"` 230 // PromotionStrategy what kind of promotion strategy to use 231 PromotionStrategy v1.PromotionStrategyType `json:"promotionStrategy,omitempty"` 232 // URLTemplate is the template to use for your environment's exposecontroller generated URLs 233 URLTemplate string `json:"urlTemplate,omitempty"` 234 } 235 236 // IngressConfig contains dns specific requirements 237 type IngressConfig struct { 238 // DNS is enabled 239 ExternalDNS bool `json:"externalDNS"` 240 // CloudDNSSecretName secret name which contains the service account for external-dns and cert-manager issuer to 241 // access the Cloud DNS service to resolve a DNS challenge 242 CloudDNSSecretName string `json:"cloud_dns_secret_name,omitempty"` 243 // Domain to expose ingress endpoints 244 Domain string `json:"domain"` 245 // IgnoreLoadBalancer if the nginx-controller LoadBalancer service should not be used to detect and update the 246 // domain if you are using a dynamic domain resolver like `.nip.io` rather than a real DNS configuration. 247 // With this flag enabled the `Domain` value will be used and never re-created based on the current LoadBalancer IP address. 248 IgnoreLoadBalancer bool `json:"ignoreLoadBalancer,omitempty"` 249 // Exposer the exposer used to expose ingress endpoints. Defaults to "Ingress" 250 Exposer string `json:"exposer,omitempty"` 251 // NamespaceSubDomain the sub domain expression to expose ingress. Defaults to ".jx." 252 NamespaceSubDomain string `json:"namespaceSubDomain"` 253 // TLS enable automated TLS using certmanager 254 TLS TLSConfig `json:"tls"` 255 // DomainIssuerURL contains a URL used to retrieve a Domain 256 DomainIssuerURL string `json:"domainIssuerURL,omitempty" envconfig:"JX_REQUIREMENT_DOMAIN_ISSUER_URL"` 257 } 258 259 // BuildPackConfig contains build pack info 260 type BuildPackConfig struct { 261 // Location contains location config 262 BuildPackLibrary *BuildPackLibrary `json:"buildPackLibrary,omitempty"` 263 } 264 265 // BuildPackLibrary contains buildpack location 266 type BuildPackLibrary struct { 267 // Name 268 Name string `json:"name,omitempty"` 269 // GitURL 270 GitURL string `json:"gitURL,omitempty"` 271 // GitRef 272 GitRef string `json:"gitRef,omitempty"` 273 } 274 275 // TLSConfig contains TLS specific requirements 276 type TLSConfig struct { 277 // TLS enabled 278 Enabled bool `json:"enabled"` 279 // Email address to register with services like LetsEncrypt 280 Email string `json:"email"` 281 // Production false uses self-signed certificates from the LetsEncrypt staging server, true enables the production 282 // server which incurs higher rate limiting https://letsencrypt.org/docs/rate-limits/ 283 Production bool `json:"production"` 284 // SecretName the name of the secret which contains the TLS certificate 285 SecretName string `json:"secretName,omitempty"` 286 } 287 288 // JxInstallProfile contains the jx profile info 289 type JxInstallProfile struct { 290 InstallType string 291 } 292 293 // StorageEntryConfig contains dns specific requirements for a kind of storage 294 type StorageEntryConfig struct { 295 // Enabled if the storage is enabled 296 Enabled bool `json:"enabled"` 297 // URL the cloud storage bucket URL such as 'gs://mybucket' or 's3://foo' or `azblob://thingy' 298 // see https://jenkins-x.io/architecture/storage/ 299 URL string `json:"url"` 300 } 301 302 // StorageConfig contains dns specific requirements 303 type StorageConfig struct { 304 // Logs for storing build logs 305 Logs StorageEntryConfig `json:"logs"` 306 // Tests for storing test results, coverage + code quality reports 307 Reports StorageEntryConfig `json:"reports"` 308 // Repository for storing repository artifacts 309 Repository StorageEntryConfig `json:"repository"` 310 // Backup for backing up kubernetes resource 311 Backup StorageEntryConfig `json:"backup"` 312 } 313 314 // AzureConfig contains Azure specific requirements 315 type AzureConfig struct { 316 // RegistrySubscription the registry subscription for defaulting the container registry. 317 // Not used if you specify a Registry explicitly 318 RegistrySubscription string `json:"registrySubscription,omitempty"` 319 } 320 321 // GKEConfig contains GKE specific requirements 322 type GKEConfig struct { 323 // ProjectNumber the unique project number GKE assigns to a project (required for workload identity). 324 ProjectNumber string `json:"projectNumber,omitempty" envconfig:"JX_REQUIREMENT_GKE_PROJECT_NUMBER"` 325 } 326 327 // ClusterConfig contains cluster specific requirements 328 type ClusterConfig struct { 329 // AzureConfig the azure specific configuration 330 AzureConfig *AzureConfig `json:"azure,omitempty"` 331 // ChartRepository the repository URL to deploy charts to 332 ChartRepository string `json:"chartRepository,omitempty" envconfig:"JX_REQUIREMENT_CHART_REPOSITORY"` 333 // GKEConfig the gke specific configuration 334 GKEConfig *GKEConfig `json:"gke,omitempty"` 335 // EnvironmentGitOwner the default git owner for environment repositories if none is specified explicitly 336 EnvironmentGitOwner string `json:"environmentGitOwner,omitempty" envconfig:"JX_REQUIREMENT_ENV_GIT_OWNER"` 337 // EnvironmentGitPublic determines whether jx boot create public or private git repos for the environments 338 EnvironmentGitPublic bool `json:"environmentGitPublic,omitempty" envconfig:"JX_REQUIREMENT_ENV_GIT_PUBLIC"` 339 // GitPublic determines whether jx boot create public or private git repos for the applications 340 GitPublic bool `json:"gitPublic,omitempty" envconfig:"JX_REQUIREMENT_GIT_PUBLIC"` 341 // Provider the kubernetes provider (e.g. gke) 342 Provider string `json:"provider,omitempty"` 343 // Namespace the namespace to install the dev environment 344 Namespace string `json:"namespace,omitempty"` 345 // ProjectID the cloud project ID e.g. on GCP 346 ProjectID string `json:"project,omitempty" envconfig:"JX_REQUIREMENT_PROJECT"` 347 // ClusterName the logical name of the cluster 348 ClusterName string `json:"clusterName,omitempty" envconfig:"JX_REQUIREMENT_CLUSTER_NAME"` 349 // VaultName the name of the vault if using vault for secrets 350 // Deprecated 351 VaultName string `json:"vaultName,omitempty"` 352 // Region the cloud region being used 353 Region string `json:"region,omitempty"` 354 // Zone the cloud zone being used 355 Zone string `json:"zone,omitempty" envconfig:"JX_REQUIREMENT_ZONE"` 356 // GitName is the name of the default git service 357 GitName string `json:"gitName,omitempty"` 358 // GitKind is the kind of git server (github, bitbucketserver etc) 359 GitKind string `json:"gitKind,omitempty"` 360 // GitServer is the URL of the git server 361 GitServer string `json:"gitServer,omitempty"` 362 // ExternalDNSSAName the service account name for external dns 363 ExternalDNSSAName string `json:"externalDNSSAName,omitempty" envconfig:"JX_REQUIREMENT_EXTERNALDNS_SA_NAME"` 364 // Registry the host name of the container registry 365 Registry string `json:"registry,omitempty" envconfig:"JX_REQUIREMENT_REGISTRY"` 366 // VaultSAName the service account name for vault 367 // Deprecated 368 VaultSAName string `json:"vaultSAName,omitempty"` 369 // KanikoSAName the service account name for kaniko 370 KanikoSAName string `json:"kanikoSAName,omitempty" envconfig:"JX_REQUIREMENT_KANIKO_SA_NAME"` 371 // HelmMajorVersion contains the major helm version number. Assumes helm 2.x with no tiller if no value specified 372 HelmMajorVersion string `json:"helmMajorVersion,omitempty"` 373 // DevEnvApprovers contains an optional list of approvers to populate the initial OWNERS file in the dev env repo 374 DevEnvApprovers []string `json:"devEnvApprovers,omitempty"` 375 // DockerRegistryOrg the default organisation used for container images 376 DockerRegistryOrg string `json:"dockerRegistryOrg,omitempty"` 377 // StrictPermissions lets you decide how to boot the cluster when it comes to permissions 378 // If it's false, cluster wide permissions will be used, normal, namespaced permissions will be used otherwise 379 // and extra steps will be necessary to get the cluster working 380 StrictPermissions bool `json:"strictPermissions,omitempty"` 381 } 382 383 // VaultConfig contains Vault configuration for Boot 384 type VaultConfig struct { 385 // Name the name of the Vault if using Jenkins X managed Vault instance. 386 // Cannot be used in conjunction with the URL attribute 387 Name string `json:"name,omitempty"` 388 389 Bucket string `json:"bucket,omitempty" envconfig:"JX_REQUIREMENT_VAULT_BUCKET_NAME"` 390 RecreateBucket bool `json:"recreateBucket,omitempty"` 391 392 Keyring string `json:"keyring,omitempty" envconfig:"JX_REQUIREMENT_VAULT_KEYRING_NAME"` 393 Key string `json:"key,omitempty" envconfig:"JX_REQUIREMENT_VAULT_KEY_NAME"` 394 395 // DisableURLDiscovery allows us to optionally override the default lookup of the Vault URL, could be incluster service or external ingress 396 DisableURLDiscovery bool `json:"disableURLDiscovery,omitempty"` 397 398 // AWSConfig describes the AWS specific configuration needed for the Vault Operator. 399 AWSConfig *VaultAWSConfig `json:"aws,omitempty"` 400 401 // AzureConfig describes the Azure specific configuration needed for the Vault Operator. 402 AzureConfig *VaultAzureConfig `json:"azure,omitempty"` 403 404 // URL specifies the URL of an Vault instance to use for secret storage. 405 // Needs to be specified together with the Service Account and namespace to use for connecting to Vault. 406 // This cannot be used in conjunction with the Name attribute. 407 URL string `json:"url,omitempty"` 408 409 // ServiceAccount is the name of the Kubernetes service account allowed to authenticate against Vault. 410 ServiceAccount string `json:"serviceAccount,omitempty" envconfig:"JX_REQUIREMENT_VAULT_SA_NAME"` 411 412 // Namespace of the Kubernetes service account allowed to authenticate against Vault. 413 Namespace string `json:"namespace,omitempty"` 414 415 // SecretEngineMountPoint is the secret engine mount point to be used for writing data into the KV engine of Vault. 416 // If not specified the 'secret' is used. 417 SecretEngineMountPoint string `json:"secretEngineMountPoint,omitempty"` 418 419 // KubernetesAuthPath is the auth path of used for this cluster 420 // If not specified the 'kubernetes' is used. 421 KubernetesAuthPath string `json:"kubernetesAuthPath,omitempty"` 422 } 423 424 // VaultAWSConfig contains all the Vault configuration needed by Vault to be deployed in AWS 425 type VaultAWSConfig struct { 426 VaultAWSUnsealConfig 427 AutoCreate bool `json:"autoCreate,omitempty"` 428 DynamoDBTable string `json:"dynamoDBTable,omitempty"` 429 DynamoDBRegion string `json:"dynamoDBRegion,omitempty"` 430 ProvidedIAMUsername string `json:"iamUserName,omitempty"` 431 } 432 433 // VaultAWSUnsealConfig contains references to existing AWS resources that can be used to install Vault 434 type VaultAWSUnsealConfig struct { 435 KMSKeyID string `json:"kmsKeyId,omitempty"` 436 KMSRegion string `json:"kmsRegion,omitempty"` 437 S3Bucket string `json:"s3Bucket,omitempty"` 438 S3Prefix string `json:"s3Prefix,omitempty"` 439 S3Region string `json:"s3Region,omitempty"` 440 } 441 442 // VaultAzureConfig contains all the Vault configuration needed by Vault to be deployed in Azure 443 type VaultAzureConfig struct { 444 TenantID string `json:"tenantId,omitempty"` 445 VaultName string `json:"vaultName,omitempty"` 446 KeyName string `json:"keyName,omitempty"` 447 StorageAccountName string `json:"storageAccountName,omitempty"` 448 ContainerName string `json:"containerName,omitempty"` 449 } 450 451 // UnmarshalJSON method handles the rename of EnvironmentGitPrivate to EnvironmentGitPublic. 452 func (t *ClusterConfig) UnmarshalJSON(data []byte) error { 453 // need a type alias to go into infinite loop 454 type Alias ClusterConfig 455 aux := &struct { 456 *Alias 457 }{ 458 Alias: (*Alias)(t), 459 } 460 461 if err := json.Unmarshal(data, &aux); err != nil { 462 return err 463 } 464 465 var raw map[string]json.RawMessage 466 err := json.Unmarshal(data, &raw) 467 if err != nil { 468 return err 469 } 470 471 _, gitPublicSet := raw["environmentGitPublic"] 472 private, gitPrivateSet := raw["environmentGitPrivate"] 473 474 if gitPrivateSet && gitPublicSet { 475 return errors.New("found settings for EnvironmentGitPublic as well as EnvironmentGitPrivate in ClusterConfig, only EnvironmentGitPublic should be used") 476 } 477 478 if gitPrivateSet { 479 log.Logger().Warn("EnvironmentGitPrivate specified in Cluster EnvironmentGitPrivate is deprecated use EnvironmentGitPublic instead.") 480 privateString := string(private) 481 if privateString == constTrue { 482 t.EnvironmentGitPublic = false 483 } else { 484 t.EnvironmentGitPublic = true 485 } 486 } 487 return nil 488 } 489 490 // VersionStreamConfig contains version stream config 491 type VersionStreamConfig struct { 492 // URL of the version stream to use 493 URL string `json:"url"` 494 // Ref of the version stream to use 495 Ref string `json:"ref" envconfig:"JX_REQUIREMENT_VERSIONS_GIT_REF"` 496 } 497 498 // VeleroConfig contains the configuration for velero 499 type VeleroConfig struct { 500 // Namespace the namespace to install velero into 501 Namespace string `json:"namespace,omitempty"` 502 // ServiceAccount the cloud service account used to run velero 503 ServiceAccount string `json:"serviceAccount,omitempty" envconfig:"JX_REQUIREMENT_VELERO_SA_NAME"` 504 // Schedule of backups 505 Schedule string `json:"schedule" envconfig:"JX_REQUIREMENT_VELERO_SCHEDULE"` 506 // TimeToLive period for backups to be retained 507 TimeToLive string `json:"ttl" envconfig:"JX_REQUIREMENT_VELERO_TTL"` 508 } 509 510 // AutoUpdateConfig contains auto update config 511 type AutoUpdateConfig struct { 512 // Enabled autoupdate 513 Enabled bool `json:"enabled"` 514 // Schedule cron of auto updates 515 Schedule string `json:"schedule"` 516 } 517 518 // GithubAppConfig contains github app config 519 type GithubAppConfig struct { 520 // Enabled this determines whether this install should use the jenkins x github app for access tokens 521 Enabled bool `json:"enabled" envconfig:"JX_REQUIREMENT_GITHUB_APP_ENABLED"` 522 // Schedule cron of the github app token refresher 523 Schedule string `json:"schedule,omitempty"` 524 // URL contains a URL to the github app 525 URL string `json:"url,omitempty" envconfig:"JX_REQUIREMENT_GITHUB_APP_URL"` 526 } 527 528 // RequirementsValues contains the logical installation requirements in the `jx-requirements.yml` file as helm values 529 type RequirementsValues struct { 530 // RequirementsConfig contains the logical installation requirements 531 RequirementsConfig *RequirementsConfig `json:"jxRequirements,omitempty"` 532 } 533 534 // UserNameEmailConfig contains the user name and email of a user (e.g. pipeline user) 535 type UserNameEmailConfig struct { 536 // Username the username of the user 537 Username string `json:"username,omitempty"` 538 // Email the email address of the user 539 Email string `json:"email,omitempty"` 540 } 541 542 // RequirementsConfig contains the logical installation requirements in the `jx-requirements.yml` file when 543 // installing, configuring or upgrading Jenkins X via `jx boot` 544 type RequirementsConfig struct { 545 // AutoUpdate contains auto update config 546 AutoUpdate AutoUpdateConfig `json:"autoUpdate,omitempty"` 547 // BootConfigURL contains the url to which the dev environment is associated with 548 BootConfigURL string `json:"bootConfigURL,omitempty"` 549 // BuildPackConfig contains custom build pack settings 550 BuildPacks *BuildPackConfig `json:"buildPacks,omitempty"` 551 // Cluster contains cluster specific requirements 552 Cluster ClusterConfig `json:"cluster"` 553 // Environments the requirements for the environments 554 Environments []EnvironmentConfig `json:"environments,omitempty"` 555 // GithubApp contains github app config 556 GithubApp *GithubAppConfig `json:"githubApp,omitempty"` 557 // GitOps if enabled we will setup a webhook in the boot configuration git repository so that we can 558 // re-run 'jx boot' when changes merge to the master branch 559 GitOps bool `json:"gitops,omitempty"` 560 // Indicates if we are using helmfile and helm 3 to spin up environments. This is currently an experimental 561 // feature flag used to implement better Multi-Cluster support. See https://github.com/jenkins-x/jx/issues/6442 562 Helmfile bool `json:"helmfile,omitempty"` 563 // Kaniko whether to enable kaniko for building docker images 564 Kaniko bool `json:"kaniko,omitempty"` 565 // Ingress contains ingress specific requirements 566 Ingress IngressConfig `json:"ingress"` 567 // PipelineUser the user name and email used for running pipelines 568 PipelineUser *UserNameEmailConfig `json:"pipelineUser,omitempty"` 569 // Repository specifies what kind of artifact repository you wish to use for storing artifacts (jars, tarballs, npm modules etc) 570 Repository RepositoryType `json:"repository,omitempty" envconfig:"JX_REQUIREMENT_REPOSITORY"` 571 // SecretStorage how should we store secrets for the cluster 572 SecretStorage SecretStorageType `json:"secretStorage,omitempty" envconfig:"JX_REQUIREMENT_SECRET_STORAGE_TYPE"` 573 // Storage contains storage requirements 574 Storage StorageConfig `json:"storage"` 575 // Terraform specifies if we are managing the kubernetes cluster and cloud resources with Terraform 576 Terraform bool `json:"terraform,omitempty"` 577 // Vault the configuration for vault 578 Vault VaultConfig `json:"vault,omitempty"` 579 // Velero the configuration for running velero for backing up the cluster resources 580 Velero VeleroConfig `json:"velero,omitempty"` 581 // VersionStream contains version stream info 582 VersionStream VersionStreamConfig `json:"versionStream"` 583 // Webhook specifies what engine we should use for webhooks 584 Webhook WebhookType `json:"webhook,omitempty"` 585 } 586 587 // NewRequirementsConfig creates a default configuration file 588 func NewRequirementsConfig() *RequirementsConfig { 589 return &RequirementsConfig{ 590 SecretStorage: SecretStorageTypeLocal, 591 Webhook: WebhookTypeProw, 592 } 593 } 594 595 // LoadRequirementsConfig loads the project configuration if there is a project configuration file 596 // if there is not a file called `jx-requirements.yml` in the given dir we will scan up the parent 597 // directories looking for the requirements file as we often run 'jx' steps in sub directories. 598 func LoadRequirementsConfig(dir string, failOnValidationErrors bool) (*RequirementsConfig, string, error) { 599 absolute, err := filepath.Abs(dir) 600 if err != nil { 601 return nil, "", errors.Wrap(err, "creating absolute path") 602 } 603 for absolute != "" && absolute != "." && absolute != "/" { 604 fileName := filepath.Join(absolute, RequirementsConfigFileName) 605 absolute = filepath.Dir(absolute) 606 607 exists, err := util.FileExists(fileName) 608 if err != nil { 609 return nil, "", err 610 } 611 612 if !exists { 613 continue 614 } 615 616 config, err := LoadRequirementsConfigFile(fileName, failOnValidationErrors) 617 return config, fileName, err 618 } 619 return nil, "", errors.New("jx-requirements.yml file not found") 620 } 621 622 // LoadRequirementsConfigFile loads a specific project YAML configuration file 623 func LoadRequirementsConfigFile(fileName string, failOnValidationErrors bool) (*RequirementsConfig, error) { 624 config := &RequirementsConfig{} 625 _, err := os.Stat(fileName) 626 if err != nil { 627 return nil, errors.Wrapf(err, "checking if file %s exists", fileName) 628 } 629 630 data, err := ioutil.ReadFile(fileName) 631 if err != nil { 632 return nil, fmt.Errorf("failed to load file %s due to %s", fileName, err) 633 } 634 635 validationErrors, err := util.ValidateYaml(config, data) 636 if err != nil { 637 return nil, fmt.Errorf("failed to validate YAML file %s due to %s", fileName, err) 638 } 639 640 if len(validationErrors) > 0 { 641 log.Logger().Warnf("validation failures in YAML file %s: %s", fileName, strings.Join(validationErrors, ", ")) 642 643 if failOnValidationErrors { 644 return nil, fmt.Errorf("validation failures in YAML file %s:\n%s", fileName, strings.Join(validationErrors, "\n")) 645 } 646 } 647 648 err = yaml.Unmarshal(data, config) 649 if err != nil { 650 return nil, fmt.Errorf("failed to unmarshal YAML file %s due to %s", fileName, err) 651 } 652 653 config.addDefaults() 654 config.handleDeprecation() 655 return config, nil 656 } 657 658 // GetRequirementsConfigFromTeamSettings reads the BootRequirements string from TeamSettings and unmarshals it 659 func GetRequirementsConfigFromTeamSettings(settings *v1.TeamSettings) (*RequirementsConfig, error) { 660 if settings == nil { 661 return nil, nil 662 } 663 664 // TeamSettings does not have a real value for BootRequirements, so this is probably not a boot cluster. 665 if settings.BootRequirements == "" { 666 return nil, nil 667 } 668 669 config := &RequirementsConfig{} 670 data := []byte(settings.BootRequirements) 671 validationErrors, err := util.ValidateYaml(config, data) 672 if err != nil { 673 return config, fmt.Errorf("failed to validate requirements from team settings due to %s", err) 674 } 675 if len(validationErrors) > 0 { 676 return config, fmt.Errorf("validation failures in requirements from team settings:\n%s", strings.Join(validationErrors, "\n")) 677 } 678 err = yaml.Unmarshal(data, config) 679 if err != nil { 680 return config, fmt.Errorf("failed to unmarshal requirements from team settings due to %s", err) 681 } 682 return config, nil 683 } 684 685 // IsEmpty returns true if this configuration is empty 686 func (c *RequirementsConfig) IsEmpty() bool { 687 empty := &RequirementsConfig{} 688 return reflect.DeepEqual(empty, c) 689 } 690 691 // SaveConfig saves the configuration file to the given project directory 692 func (c *RequirementsConfig) SaveConfig(fileName string) error { 693 c.handleDeprecation() 694 data, err := yaml.Marshal(c) 695 if err != nil { 696 return err 697 } 698 err = ioutil.WriteFile(fileName, data, util.DefaultWritePermissions) 699 if err != nil { 700 return errors.Wrapf(err, "failed to save file %s", fileName) 701 } 702 703 if c.Helmfile { 704 y := RequirementsValues{ 705 RequirementsConfig: c, 706 } 707 data, err = yaml.Marshal(y) 708 if err != nil { 709 return err 710 } 711 712 err = ioutil.WriteFile(path.Join(path.Dir(fileName), RequirementsValuesFileName), data, util.DefaultWritePermissions) 713 if err != nil { 714 return errors.Wrapf(err, "failed to save file %s", RequirementsValuesFileName) 715 } 716 } 717 718 return nil 719 } 720 721 type environmentsSliceTransformer struct{} 722 723 // environmentsSliceTransformer.Transformer is handling the correct merge of two EnvironmentConfig slices 724 // so we can both append extra items and merge existing ones so we don't lose any data 725 func (t environmentsSliceTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { 726 if typ == reflect.TypeOf([]EnvironmentConfig{}) { 727 return func(dst, src reflect.Value) error { 728 d := dst.Interface().([]EnvironmentConfig) 729 s := src.Interface().([]EnvironmentConfig) 730 if dst.CanSet() { 731 for i, v := range s { 732 if i > len(d)-1 { 733 d = append(d, v) 734 } else { 735 nv := v 736 err := mergo.Merge(&d[i], &nv, mergo.WithOverride) 737 if err != nil { 738 return errors.Wrap(err, "error merging EnvironmentConfig slices") 739 } 740 } 741 } 742 dst.Set(reflect.ValueOf(d)) 743 } 744 return nil 745 } 746 } 747 return nil 748 } 749 750 // MergeSave attempts to merge the provided RequirementsConfig with the caller's data. 751 // It does so overriding values in the source struct with non-zero values from the provided struct 752 // it defines non-zero per property and not for a while embedded struct, meaning that nested properties 753 // in embedded structs should also be merged correctly. 754 // if a slice is added a transformer will be needed to handle correctly merging the contained values 755 func (c *RequirementsConfig) MergeSave(src *RequirementsConfig, requirementsFileName string) error { 756 err := mergo.Merge(c, src, mergo.WithOverride, mergo.WithTransformers(environmentsSliceTransformer{})) 757 if err != nil { 758 return errors.Wrap(err, "error merging jx-requirements.yml files") 759 } 760 err = c.SaveConfig(requirementsFileName) 761 if err != nil { 762 return errors.Wrapf(err, "error saving the merged jx-requirements.yml files to %s", requirementsFileName) 763 } 764 return nil 765 } 766 767 // EnvironmentMap creates a map of maps tree which can be used inside Go templates to access the environment 768 // configurations 769 func (c *RequirementsConfig) EnvironmentMap() map[string]interface{} { 770 answer := map[string]interface{}{} 771 for _, env := range c.Environments { 772 k := env.Key 773 if k == "" { 774 log.Logger().Warnf("missing 'key' for Environment requirements %#v", env) 775 continue 776 } 777 e := env 778 m, err := util.ToObjectMap(&e) 779 if err == nil { 780 ensureHasFields(m, "owner", "repository", "gitServer", "gitKind") 781 answer[k] = m 782 } else { 783 log.Logger().Warnf("failed to turn environment %s with value %#v into a map: %s\n", k, e, err.Error()) 784 } 785 } 786 return answer 787 } 788 789 // Environment looks up the environment configuration based on environment name 790 func (c *RequirementsConfig) Environment(name string) (*EnvironmentConfig, error) { 791 for _, env := range c.Environments { 792 if env.Key == name { 793 return &env, nil 794 } 795 } 796 return nil, fmt.Errorf("environment %q not found", name) 797 } 798 799 // ToMap converts this object to a map of maps for use in helm templating 800 func (c *RequirementsConfig) ToMap() (map[string]interface{}, error) { 801 m, err := util.ToObjectMap(c) 802 if m != nil { 803 ensureHasFields(m, "provider", "project", "environmentGitOwner", "gitops", "webhook") 804 } 805 return m, err 806 } 807 808 // IsCloudProvider returns true if the kubenretes provider is a cloud 809 func (c *RequirementsConfig) IsCloudProvider() bool { 810 p := c.Cluster.Provider 811 return p == cloud.GKE || p == cloud.AKS || p == cloud.AWS || p == cloud.EKS || p == cloud.ALIBABA 812 } 813 814 func ensureHasFields(m map[string]interface{}, keys ...string) { 815 for _, k := range keys { 816 _, ok := m[k] 817 if !ok { 818 m[k] = "" 819 } 820 } 821 } 822 823 // MissingRequirement returns an error if there is a missing property in the requirements 824 func MissingRequirement(property string, fileName string) error { 825 return fmt.Errorf("missing property: %s in file %s", property, fileName) 826 } 827 828 // IsLazyCreateSecrets returns a boolean whether secrets should be lazily created 829 func (c *RequirementsConfig) IsLazyCreateSecrets(flag string) (bool, error) { 830 if flag != "" { 831 switch flag { 832 case constTrue: 833 return true, nil 834 case constFalse: 835 return false, nil 836 default: 837 return false, errors.Errorf("invalid option for lazy-create: %s", flag) 838 } 839 } else if !c.Terraform { 840 return true, nil 841 } 842 843 // default to false 844 return false, nil 845 } 846 847 // addDefaults lets ensure any missing values have good defaults 848 func (c *RequirementsConfig) addDefaults() { 849 if c.Cluster.Namespace == "" { 850 c.Cluster.Namespace = "jx" 851 } 852 if c.Cluster.GitServer == "" { 853 c.Cluster.GitServer = "https://github.com" 854 } 855 if c.Cluster.GitKind == "" { 856 c.Cluster.GitKind = "github" 857 } 858 if c.Cluster.GitName == "" { 859 c.Cluster.GitName = c.Cluster.GitKind 860 } 861 if c.Ingress.NamespaceSubDomain == "" { 862 c.Ingress.NamespaceSubDomain = "-" + c.Cluster.Namespace + "." 863 } 864 if c.Webhook == WebhookTypeNone { 865 if c.Cluster.GitServer == "https://github.com" || c.Cluster.GitServer == "https://github.com/" { 866 c.Webhook = WebhookTypeProw 867 } else { 868 c.Webhook = WebhookTypeLighthouse 869 } 870 } 871 if c.Repository == "" { 872 c.Repository = "nexus" 873 } 874 } 875 876 func (c *RequirementsConfig) handleDeprecation() { 877 if c.Vault.Name != "" { 878 c.Cluster.VaultName = c.Vault.Name 879 } else { 880 c.Vault.Name = c.Cluster.VaultName 881 } 882 883 if c.Vault.ServiceAccount != "" { 884 c.Cluster.VaultSAName = c.Vault.ServiceAccount 885 } else { 886 c.Vault.ServiceAccount = c.Cluster.VaultSAName 887 } 888 } 889 890 // IsAutoDNSDomain returns true if the domain is configured to use an auto DNS sub domain like 891 // '.nip.io' or '.xip.io' 892 func (i *IngressConfig) IsAutoDNSDomain() bool { 893 for _, suffix := range autoDNSSuffixes { 894 if strings.HasSuffix(i.Domain, suffix) { 895 return true 896 } 897 } 898 return false 899 } 900 901 // OverrideRequirementsFromEnvironment allows properties to be overridden with environment variables 902 func (c *RequirementsConfig) OverrideRequirementsFromEnvironment(gkeProjectNumber func(projectId string) (string, error)) { 903 // struct members need to use explicit 'envconfig:"<var-name>"' unless there is a match between struct member navigation 904 // path and env variable name 905 err := envconfig.InitWithOptions(&c, envconfig.Options{AllOptional: true, LeaveNil: true, Prefix: "JX_REQUIREMENT"}) 906 if err != nil { 907 log.Logger().Errorf("Unable to init envconfig for override requirements: %s", err) 908 } 909 910 // RequirementIngressTLSProduction applies to more than one environment and needs to be handled explicitly 911 if "" != os.Getenv(RequirementIngressTLSProduction) { 912 useProduction := os.Getenv(RequirementIngressTLSProduction) 913 if envVarBoolean(useProduction) { 914 c.Ingress.TLS.Production = true 915 for _, e := range c.Environments { 916 e.Ingress.TLS.Production = true 917 } 918 } else { 919 c.Ingress.TLS.Production = false 920 for _, e := range c.Environments { 921 e.Ingress.TLS.Production = false 922 } 923 } 924 } 925 926 // StorageConfig is reused between multiple storage configuration types and needs to be handled explicitly 927 if "" != os.Getenv(RequirementStorageBackupEnabled) { 928 storageBackup := os.Getenv(RequirementStorageBackupEnabled) 929 if envVarBoolean(storageBackup) && "" != os.Getenv(RequirementStorageBackupURL) { 930 c.Storage.Backup.Enabled = true 931 c.Storage.Backup.URL = os.Getenv(RequirementStorageBackupURL) 932 } 933 } 934 935 if "" != os.Getenv(RequirementStorageLogsEnabled) { 936 storageLogs := os.Getenv(RequirementStorageLogsEnabled) 937 if envVarBoolean(storageLogs) && "" != os.Getenv(RequirementStorageLogsURL) { 938 c.Storage.Logs.Enabled = true 939 c.Storage.Logs.URL = os.Getenv(RequirementStorageLogsURL) 940 } 941 } 942 943 if "" != os.Getenv(RequirementStorageReportsEnabled) { 944 storageReports := os.Getenv(RequirementStorageReportsEnabled) 945 if envVarBoolean(storageReports) && "" != os.Getenv(RequirementStorageReportsURL) { 946 c.Storage.Reports.Enabled = true 947 c.Storage.Reports.URL = os.Getenv(RequirementStorageReportsURL) 948 } 949 } 950 951 if "" != os.Getenv(RequirementStorageRepositoryEnabled) { 952 storageRepository := os.Getenv(RequirementStorageRepositoryEnabled) 953 if envVarBoolean(storageRepository) && "" != os.Getenv(RequirementStorageRepositoryURL) { 954 c.Storage.Repository.Enabled = true 955 c.Storage.Repository.URL = os.Getenv(RequirementStorageRepositoryURL) 956 } 957 } 958 959 if "" != os.Getenv(RequirementDevEnvApprovers) { 960 rawApprovers := os.Getenv(RequirementDevEnvApprovers) 961 for _, approver := range strings.Split(rawApprovers, ",") { 962 c.Cluster.DevEnvApprovers = append(c.Cluster.DevEnvApprovers, strings.TrimSpace(approver)) 963 } 964 } 965 966 // set this if its not currently configured 967 if c.Cluster.Provider == "gke" { 968 if c.Cluster.GKEConfig == nil { 969 c.Cluster.GKEConfig = &GKEConfig{} 970 } 971 972 if c.Cluster.GKEConfig.ProjectNumber == "" { 973 if gkeProjectNumber != nil { 974 projectNumber, err := gkeProjectNumber(c.Cluster.ProjectID) 975 if err != nil { 976 log.Logger().Warnf("unable to determine gke project number - %s", err) 977 } 978 c.Cluster.GKEConfig.ProjectNumber = projectNumber 979 } 980 } 981 } 982 } 983 984 func envVarBoolean(value string) bool { 985 return value == constTrue || value == "yes" 986 }