kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/configs/provider_requirements.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 6 version "github.com/hashicorp/go-version" 7 "github.com/hashicorp/hcl/v2" 8 "kubeform.dev/terraform-backend-sdk/addrs" 9 "github.com/zclconf/go-cty/cty" 10 ) 11 12 // RequiredProvider represents a declaration of a dependency on a particular 13 // provider version or source without actually configuring that provider. This 14 // is used in child modules that expect a provider to be passed in from their 15 // parent. 16 type RequiredProvider struct { 17 Name string 18 Source string 19 Type addrs.Provider 20 Requirement VersionConstraint 21 DeclRange hcl.Range 22 Aliases []addrs.LocalProviderConfig 23 } 24 25 type RequiredProviders struct { 26 RequiredProviders map[string]*RequiredProvider 27 DeclRange hcl.Range 28 } 29 30 func decodeRequiredProvidersBlock(block *hcl.Block) (*RequiredProviders, hcl.Diagnostics) { 31 attrs, diags := block.Body.JustAttributes() 32 if diags.HasErrors() { 33 return nil, diags 34 } 35 36 ret := &RequiredProviders{ 37 RequiredProviders: make(map[string]*RequiredProvider), 38 DeclRange: block.DefRange, 39 } 40 41 for name, attr := range attrs { 42 rp := &RequiredProvider{ 43 Name: name, 44 DeclRange: attr.Expr.Range(), 45 } 46 47 // Look for a single static string, in case we have the legacy version-only 48 // format in the configuration. 49 if expr, err := attr.Expr.Value(nil); err == nil && expr.Type().IsPrimitiveType() { 50 vc, reqDiags := decodeVersionConstraint(attr) 51 diags = append(diags, reqDiags...) 52 53 pType, err := addrs.ParseProviderPart(rp.Name) 54 if err != nil { 55 diags = append(diags, &hcl.Diagnostic{ 56 Severity: hcl.DiagError, 57 Summary: "Invalid provider name", 58 Detail: err.Error(), 59 Subject: attr.Expr.Range().Ptr(), 60 }) 61 continue 62 } 63 64 rp.Requirement = vc 65 rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType) 66 ret.RequiredProviders[name] = rp 67 68 continue 69 } 70 71 // verify that the local name is already localized or produce an error. 72 nameDiags := checkProviderNameNormalized(name, attr.Expr.Range()) 73 if nameDiags.HasErrors() { 74 diags = append(diags, nameDiags...) 75 continue 76 } 77 78 kvs, mapDiags := hcl.ExprMap(attr.Expr) 79 if mapDiags.HasErrors() { 80 diags = append(diags, &hcl.Diagnostic{ 81 Severity: hcl.DiagError, 82 Summary: "Invalid required_providers object", 83 Detail: "required_providers entries must be strings or objects.", 84 Subject: attr.Expr.Range().Ptr(), 85 }) 86 continue 87 } 88 89 LOOP: 90 for _, kv := range kvs { 91 key, keyDiags := kv.Key.Value(nil) 92 if keyDiags.HasErrors() { 93 diags = append(diags, keyDiags...) 94 continue 95 } 96 97 if key.Type() != cty.String { 98 diags = append(diags, &hcl.Diagnostic{ 99 Severity: hcl.DiagError, 100 Summary: "Invalid Attribute", 101 Detail: fmt.Sprintf("Invalid attribute value for provider requirement: %#v", key), 102 Subject: kv.Key.Range().Ptr(), 103 }) 104 continue 105 } 106 107 switch key.AsString() { 108 case "version": 109 vc := VersionConstraint{ 110 DeclRange: attr.Range, 111 } 112 113 constraint, valDiags := kv.Value.Value(nil) 114 if valDiags.HasErrors() || !constraint.Type().Equals(cty.String) { 115 diags = append(diags, &hcl.Diagnostic{ 116 Severity: hcl.DiagError, 117 Summary: "Invalid version constraint", 118 Detail: "Version must be specified as a string.", 119 Subject: kv.Value.Range().Ptr(), 120 }) 121 continue 122 } 123 124 constraintStr := constraint.AsString() 125 constraints, err := version.NewConstraint(constraintStr) 126 if err != nil { 127 // NewConstraint doesn't return user-friendly errors, so we'll just 128 // ignore the provided error and produce our own generic one. 129 diags = append(diags, &hcl.Diagnostic{ 130 Severity: hcl.DiagError, 131 Summary: "Invalid version constraint", 132 Detail: "This string does not use correct version constraint syntax.", 133 Subject: kv.Value.Range().Ptr(), 134 }) 135 continue 136 } 137 138 vc.Required = constraints 139 rp.Requirement = vc 140 141 case "source": 142 source, err := kv.Value.Value(nil) 143 if err != nil || !source.Type().Equals(cty.String) { 144 diags = append(diags, &hcl.Diagnostic{ 145 Severity: hcl.DiagError, 146 Summary: "Invalid source", 147 Detail: "Source must be specified as a string.", 148 Subject: kv.Value.Range().Ptr(), 149 }) 150 continue 151 } 152 153 fqn, sourceDiags := addrs.ParseProviderSourceString(source.AsString()) 154 if sourceDiags.HasErrors() { 155 hclDiags := sourceDiags.ToHCL() 156 // The diagnostics from ParseProviderSourceString don't contain 157 // source location information because it has no context to compute 158 // them from, and so we'll add those in quickly here before we 159 // return. 160 for _, diag := range hclDiags { 161 if diag.Subject == nil { 162 diag.Subject = kv.Value.Range().Ptr() 163 } 164 } 165 diags = append(diags, hclDiags...) 166 continue 167 } 168 169 rp.Source = source.AsString() 170 rp.Type = fqn 171 172 case "configuration_aliases": 173 exprs, listDiags := hcl.ExprList(kv.Value) 174 if listDiags.HasErrors() { 175 diags = append(diags, listDiags...) 176 continue 177 } 178 179 for _, expr := range exprs { 180 traversal, travDiags := hcl.AbsTraversalForExpr(expr) 181 if travDiags.HasErrors() { 182 diags = append(diags, travDiags...) 183 continue 184 } 185 186 addr, cfgDiags := ParseProviderConfigCompact(traversal) 187 if cfgDiags.HasErrors() { 188 diags = append(diags, &hcl.Diagnostic{ 189 Severity: hcl.DiagError, 190 Summary: "Invalid configuration_aliases value", 191 Detail: `Configuration aliases can only contain references to local provider configuration names in the format of provider.alias`, 192 Subject: kv.Value.Range().Ptr(), 193 }) 194 continue 195 } 196 197 if addr.LocalName != name { 198 diags = append(diags, &hcl.Diagnostic{ 199 Severity: hcl.DiagError, 200 Summary: "Invalid configuration_aliases value", 201 Detail: fmt.Sprintf(`Configuration aliases must be prefixed with the provider name. Expected %q, but found %q.`, name, addr.LocalName), 202 Subject: kv.Value.Range().Ptr(), 203 }) 204 continue 205 } 206 207 rp.Aliases = append(rp.Aliases, addr) 208 } 209 210 default: 211 diags = append(diags, &hcl.Diagnostic{ 212 Severity: hcl.DiagError, 213 Summary: "Invalid required_providers object", 214 Detail: `required_providers objects can only contain "version", "source" and "configuration_aliases" attributes. To configure a provider, use a "provider" block.`, 215 Subject: kv.Key.Range().Ptr(), 216 }) 217 break LOOP 218 } 219 220 } 221 222 if diags.HasErrors() { 223 continue 224 } 225 226 // We can add the required provider when there are no errors. 227 // If a source was not given, create an implied type. 228 if rp.Type.IsZero() { 229 pType, err := addrs.ParseProviderPart(rp.Name) 230 if err != nil { 231 diags = append(diags, &hcl.Diagnostic{ 232 Severity: hcl.DiagError, 233 Summary: "Invalid provider name", 234 Detail: err.Error(), 235 Subject: attr.Expr.Range().Ptr(), 236 }) 237 } else { 238 rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType) 239 } 240 } 241 242 ret.RequiredProviders[rp.Name] = rp 243 } 244 245 return ret, diags 246 }