github.com/hashicorp/packer@v1.14.3/internal/hcp/registry/hcp.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package registry 5 6 import ( 7 "fmt" 8 9 "github.com/go-git/go-git/v5" 10 "github.com/hashicorp/hcl/v2" 11 "github.com/hashicorp/packer/hcl2template" 12 "github.com/hashicorp/packer/internal/hcp/env" 13 "github.com/hashicorp/packer/packer" 14 ) 15 16 // HCPConfigMode types specify the mode in which HCP configuration 17 // is defined for a given Packer build execution. 18 type HCPConfigMode int 19 20 const ( 21 // HCPConfigUnset mode is set when no HCP configuration has been found for the Packer execution. 22 HCPConfigUnset HCPConfigMode = iota 23 // HCPConfigEnabled mode is set when the HCP configuration is codified in the template. 24 HCPConfigEnabled 25 // HCPEnvEnabled mode is set when the HCP configuration is read from environment variables. 26 HCPEnvEnabled 27 ) 28 29 type bucketConfigurationOpts func(*Bucket) hcl.Diagnostics 30 31 // IsHCPEnabled returns true if HCP integration is enabled for a build 32 func IsHCPEnabled(cfg packer.Handler) bool { 33 // HCP_PACKER_REGISTRY is explicitly turned off 34 if env.IsHCPDisabled() { 35 return false 36 } 37 38 mode := HCPConfigUnset 39 40 switch config := cfg.(type) { 41 case *hcl2template.PackerConfig: 42 for _, build := range config.Builds { 43 if build.HCPPackerRegistry != nil { 44 mode = HCPConfigEnabled 45 } 46 } 47 if config.HCPPackerRegistry != nil { 48 mode = HCPConfigEnabled 49 } 50 } 51 52 // HCP_PACKER_BUCKET_NAME is set or HCP_PACKER_REGISTRY not toggled off 53 if mode == HCPConfigUnset && (env.HasPackerRegistryBucket() || env.IsHCPExplicitelyEnabled()) { 54 mode = HCPEnvEnabled 55 } 56 57 return mode != HCPConfigUnset 58 } 59 60 // createConfiguredBucket returns a bucket that can be used for connecting to the HCP Packer registry. 61 // Configuration for the bucket is obtained from the base iteration setting and any addition configuration 62 // options passed in as opts. All errors during configuration are collected and returned as Diagnostics. 63 func createConfiguredBucket(templateDir string, opts ...bucketConfigurationOpts) (*Bucket, hcl.Diagnostics) { 64 var diags hcl.Diagnostics 65 66 hasAuth, err := env.HasHCPAuth() 67 if err != nil { 68 diags = append(diags, &hcl.Diagnostic{ 69 Summary: "HCP authentication check failed", 70 Detail: fmt.Sprintf("Failed to check for HCP authentication, error: %s", err.Error()), 71 Severity: hcl.DiagError, 72 }) 73 } else if !hasAuth { 74 diags = append(diags, &hcl.Diagnostic{ 75 Summary: "HCP authentication information required", 76 Detail: fmt.Sprintf("HCP Authentication not configured, either set an HCP Client ID and secret using the environment variables %s and %s, place an HCP credential file in the default path (%s), or at a different path specified in the %s environment variable.", env.HCPClientID, env.HCPClientSecret, env.HCPDefaultCredFilePath, env.HCPCredFile), 77 Severity: hcl.DiagError, 78 }) 79 } 80 81 bucket := NewBucketWithVersion() 82 83 for _, opt := range opts { 84 if optDiags := opt(bucket); optDiags.HasErrors() { 85 diags = append(diags, optDiags...) 86 } 87 } 88 89 if bucket.Name == "" { 90 diags = append(diags, &hcl.Diagnostic{ 91 Summary: "Bucket name required", 92 Detail: "You must provide a bucket name for HCP Packer builds. " + 93 "You can set the HCP_PACKER_BUCKET_NAME environment variable. " + 94 "For HCL2 templates, the registry either uses the name of your " + 95 "template's build block, or you can set the bucket_name argument " + 96 "in an hcp_packer_registry block.", 97 Severity: hcl.DiagError, 98 }) 99 } 100 101 err = bucket.Version.Initialize() 102 if err != nil { 103 diags = append(diags, &hcl.Diagnostic{ 104 Summary: "Version initialization failed", 105 Detail: fmt.Sprintf("Initialization of the version failed with "+ 106 "the following error message: %s", err), 107 Severity: hcl.DiagError, 108 }) 109 } 110 return bucket, diags 111 } 112 113 func withPackerEnvConfiguration(bucket *Bucket) hcl.Diagnostics { 114 // Add default values for Packer settings configured via EnvVars. 115 // TODO look to break this up to be more explicit on what is loaded here. 116 bucket.LoadDefaultSettingsFromEnv() 117 118 return nil 119 } 120 121 // getGitSHA returns the HEAD commit for some template dir defined in baseDir. 122 // If the base directory is not under version control an error is returned. 123 func getGitSHA(baseDir string) (string, error) { 124 r, err := git.PlainOpenWithOptions(baseDir, &git.PlainOpenOptions{ 125 DetectDotGit: true, 126 }) 127 128 if err != nil { 129 return "", fmt.Errorf("Packer could not read the fingerprint from git.") 130 } 131 132 // The config can be used to retrieve user identity. for example, 133 // c.User.Email. Leaving in but commented because I'm not sure we care 134 // about this identity right now. - Megan 135 // 136 // c, err := r.ConfigScoped(config.GlobalScope) 137 // if err != nil { 138 // return "", fmt.Errorf("Error setting git scope", err) 139 // } 140 ref, err := r.Head() 141 if err != nil { 142 // If we get there, we're in a Git dir, but HEAD cannot be read. 143 // 144 // This may happen when there's no commit in the git dir. 145 return "", fmt.Errorf("Packer could not read a git SHA in directory %q: %s", baseDir, err) 146 } 147 148 // log.Printf("Author: %v, Commit: %v\n", c.User.Email, ref.Hash()) 149 150 return ref.Hash().String(), nil 151 }