github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/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 "github.com/iaas-resource-provision/iaas-rpc/internal/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 for _, kv := range kvs { 90 key, keyDiags := kv.Key.Value(nil) 91 if keyDiags.HasErrors() { 92 diags = append(diags, keyDiags...) 93 continue 94 } 95 96 if key.Type() != cty.String { 97 diags = append(diags, &hcl.Diagnostic{ 98 Severity: hcl.DiagError, 99 Summary: "Invalid Attribute", 100 Detail: fmt.Sprintf("Invalid attribute value for provider requirement: %#v", key), 101 Subject: kv.Key.Range().Ptr(), 102 }) 103 continue 104 } 105 106 switch key.AsString() { 107 case "version": 108 vc := VersionConstraint{ 109 DeclRange: attr.Range, 110 } 111 112 constraint, valDiags := kv.Value.Value(nil) 113 if valDiags.HasErrors() || !constraint.Type().Equals(cty.String) { 114 diags = append(diags, &hcl.Diagnostic{ 115 Severity: hcl.DiagError, 116 Summary: "Invalid version constraint", 117 Detail: "Version must be specified as a string.", 118 Subject: kv.Value.Range().Ptr(), 119 }) 120 continue 121 } 122 123 constraintStr := constraint.AsString() 124 constraints, err := version.NewConstraint(constraintStr) 125 if err != nil { 126 // NewConstraint doesn't return user-friendly errors, so we'll just 127 // ignore the provided error and produce our own generic one. 128 diags = append(diags, &hcl.Diagnostic{ 129 Severity: hcl.DiagError, 130 Summary: "Invalid version constraint", 131 Detail: "This string does not use correct version constraint syntax.", 132 Subject: kv.Value.Range().Ptr(), 133 }) 134 continue 135 } 136 137 vc.Required = constraints 138 rp.Requirement = vc 139 140 case "source": 141 source, err := kv.Value.Value(nil) 142 if err != nil || !source.Type().Equals(cty.String) { 143 diags = append(diags, &hcl.Diagnostic{ 144 Severity: hcl.DiagError, 145 Summary: "Invalid source", 146 Detail: "Source must be specified as a string.", 147 Subject: kv.Value.Range().Ptr(), 148 }) 149 continue 150 } 151 152 fqn, sourceDiags := addrs.ParseProviderSourceString(source.AsString()) 153 if sourceDiags.HasErrors() { 154 hclDiags := sourceDiags.ToHCL() 155 // The diagnostics from ParseProviderSourceString don't contain 156 // source location information because it has no context to compute 157 // them from, and so we'll add those in quickly here before we 158 // return. 159 for _, diag := range hclDiags { 160 if diag.Subject == nil { 161 diag.Subject = kv.Value.Range().Ptr() 162 } 163 } 164 diags = append(diags, hclDiags...) 165 continue 166 } 167 168 rp.Source = source.AsString() 169 rp.Type = fqn 170 171 case "configuration_aliases": 172 exprs, listDiags := hcl.ExprList(kv.Value) 173 if listDiags.HasErrors() { 174 diags = append(diags, listDiags...) 175 continue 176 } 177 178 for _, expr := range exprs { 179 traversal, travDiags := hcl.AbsTraversalForExpr(expr) 180 if travDiags.HasErrors() { 181 diags = append(diags, travDiags...) 182 continue 183 } 184 185 addr, cfgDiags := ParseProviderConfigCompact(traversal) 186 if cfgDiags.HasErrors() { 187 diags = append(diags, &hcl.Diagnostic{ 188 Severity: hcl.DiagError, 189 Summary: "Invalid configuration_aliases value", 190 Detail: `Configuration aliases can only contain references to local provider configuration names in the format of provider.alias`, 191 Subject: kv.Value.Range().Ptr(), 192 }) 193 continue 194 } 195 196 if addr.LocalName != name { 197 diags = append(diags, &hcl.Diagnostic{ 198 Severity: hcl.DiagError, 199 Summary: "Invalid configuration_aliases value", 200 Detail: fmt.Sprintf(`Configuration aliases must be prefixed with the provider name. Expected %q, but found %q.`, name, addr.LocalName), 201 Subject: kv.Value.Range().Ptr(), 202 }) 203 continue 204 } 205 206 rp.Aliases = append(rp.Aliases, addr) 207 } 208 209 default: 210 diags = append(diags, &hcl.Diagnostic{ 211 Severity: hcl.DiagError, 212 Summary: "Invalid required_providers object", 213 Detail: `required_providers objects can only contain "version", "source" and "configuration_aliases" attributes. To configure a provider, use a "provider" block.`, 214 Subject: kv.Key.Range().Ptr(), 215 }) 216 break 217 } 218 219 } 220 221 if diags.HasErrors() { 222 continue 223 } 224 225 // We can add the required provider when there are no errors. 226 // If a source was not given, create an implied type. 227 if rp.Type.IsZero() { 228 pType, err := addrs.ParseProviderPart(rp.Name) 229 if err != nil { 230 diags = append(diags, &hcl.Diagnostic{ 231 Severity: hcl.DiagError, 232 Summary: "Invalid provider name", 233 Detail: err.Error(), 234 Subject: attr.Expr.Range().Ptr(), 235 }) 236 } else { 237 rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType) 238 } 239 } 240 241 ret.RequiredProviders[rp.Name] = rp 242 } 243 244 return ret, diags 245 }