1 // Copyright (c) 2016, 2018, 2025, Oracle and/or its affiliates. All rights reserved.
2 // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
3 4 package common
5 6 import (
7 "encoding/json"
8 "fmt"
9 "io/ioutil"
10 "net/http"
11 "os"
12 "path/filepath"
13 "regexp"
14 "strings"
15 "sync"
16 "time"
17 )
18 19 // Region type for regions
20 type Region string
21 22 const (
23 instanceMetadataRegionInfoURLV2 = "http://169.254.169.254/opc/v2/instance/regionInfo"
24 25 // Region Metadata Configuration File
26 regionMetadataCfgDirName = ".oci"
27 regionMetadataCfgFileName = "regions-config.json"
28 29 // Region Metadata Environment Variable
30 regionMetadataEnvVarName = "OCI_REGION_METADATA"
31 32 // Default Realm Environment Variable
33 defaultRealmEnvVarName = "OCI_DEFAULT_REALM"
34 35 //EndpointTemplateForRegionWithDot Environment Variable
36 EndpointTemplateForRegionWithDot = "https://{endpoint_service_name}.{region}"
37 38 // Region Metadata
39 regionIdentifierPropertyName = "regionIdentifier" // e.g. "ap-sydney-1"
40 realmKeyPropertyName = "realmKey" // e.g. "oc1"
41 realmDomainComponentPropertyName = "realmDomainComponent" // e.g. "oraclecloud.com"
42 regionKeyPropertyName = "regionKey" // e.g. "SYD"
43 44 // OciRealmSpecificServiceEndpointTemplateEnabledEnvVar is the environment variable name to enable the realm specific service endpoint template.
45 OciRealmSpecificServiceEndpointTemplateEnabledEnvVar = "OCI_REALM_SPECIFIC_SERVICE_ENDPOINT_TEMPLATE_ENABLED"
46 )
47 48 // External region metadata info flag, used to control adding these metadata region info only once.
49 var readCfgFile, readEnvVar, visitIMDS bool = true, true, false
50 51 // getRegionInfoFromInstanceMetadataService gets the region information
52 var getRegionInfoFromInstanceMetadataService = getRegionInfoFromInstanceMetadataServiceProd
53 54 // OciRealmSpecificServiceEndpointTemplateEnabled is the flag to enable the realm specific service endpoint template. This one has higher priority than the environment variable.
55 var OciRealmSpecificServiceEndpointTemplateEnabled *bool = nil
56 57 // reNonWord precomiles the regex once at the package scope
58 var reNonWord = regexp.MustCompile(`[^\w]`)
59 60 // OciSdkEnabledServicesMap is a list of services that are enabled, default is an empty list which means all services are enabled
61 var OciSdkEnabledServicesMap map[string]bool
62 63 // OciSdkEnabledServicesOnce is a sync.Once variable to ensure the OciSdkEnabledServicesMap is initialized only once
64 var OciSdkEnabledServicesOnce sync.Once
65 66 // OciSdkEnabledServicesMu is a mutex to protect access to the OciSdkEnabledServicesMap
67 var OciSdkEnabledServicesMu sync.RWMutex
68 69 // OciDeveloperToolConfigurationFilePathEnvVar is the environment variable name for the OCI Developer Tool Config File Path
70 const OciDeveloperToolConfigurationFilePathEnvVar = "OCI_DEVELOPER_TOOL_CONFIGURATION_FILE_PATH"
71 72 // OciAllowOnlyDeveloperToolConfigurationRegionsEnvVar is the environment variable name for the OCI Allow only Dev Tool Config Regions
73 const OciAllowOnlyDeveloperToolConfigurationRegionsEnvVar = "OCI_ALLOW_ONLY_DEVELOPER_TOOL_CONFIGURATION_REGIONS"
74 75 // defaultRealmForUnknownDeveloperToolConfigurationRegion is the default realm for unknown Developer Tool Configuration Regions
76 const defaultRealmForUnknownDeveloperToolConfigurationRegion = "oraclecloud.com"
77 78 // OciDeveloperToolConfigurationProvider is the provider name for the OCI Developer Tool Configuration file
79 var OciDeveloperToolConfigurationProvider string
80 81 // ociAllowOnlyDeveloperToolConfigurationRegions is the flag to enable the OCI Allow Only Developer Tool Configuration Regions. This one has lower priority than the environment variable.
82 var ociAllowOnlyDeveloperToolConfigurationRegions bool
83 84 var ociDeveloperToolConfigurationRegionSchemaList []map[string]string
85 86 // Endpoint returns a endpoint for a service
87 func (region Region) Endpoint(service string) string {
88 // Endpoint for dotted region
89 if strings.Contains(string(region), ".") {
90 return fmt.Sprintf("%s.%s", service, region)
91 }
92 return fmt.Sprintf("%s.%s.%s", service, region, region.SecondLevelDomain())
93 }
94 95 // EndpointForTemplate returns a endpoint for a service based on template, only unknown region name can fall back to "oc1", but not short code region name.
96 func (region Region) EndpointForTemplate(service string, serviceEndpointTemplate string) string {
97 if strings.Contains(string(region), ".") {
98 endpoint, error := region.EndpointForTemplateDottedRegion(service, serviceEndpointTemplate, "")
99 if error != nil {
100 Debugf("%v", error)
101 102 return ""
103 }
104 return endpoint
105 }
106 107 if serviceEndpointTemplate == "" {
108 return region.Endpoint(service)
109 }
110 111 // replace service prefix
112 endpoint := strings.Replace(serviceEndpointTemplate, "{serviceEndpointPrefix}", service, 1)
113 114 // replace region
115 endpoint = strings.Replace(endpoint, "{region}", string(region), 1)
116 117 // replace second level domain
118 endpoint = strings.Replace(endpoint, "{secondLevelDomain}", region.SecondLevelDomain(), 1)
119 120 return endpoint
121 }
122 123 // EndpointForTemplateDottedRegion returns a endpoint for a service based on the service name and EndpointTemplateForRegionWithDot template. If a service name is missing it is obtained from serviceEndpointTemplate and endpoint is constructed usingEndpointTemplateForRegionWithDot template.
124 func (region Region) EndpointForTemplateDottedRegion(service string, serviceEndpointTemplate string, endpointServiceName string) (string, error) {
125 if !strings.Contains(string(region), ".") {
126 var endpoint = ""
127 if serviceEndpointTemplate != "" {
128 endpoint = region.EndpointForTemplate(service, serviceEndpointTemplate)
129 return endpoint, nil
130 }
131 endpoint = region.EndpointForTemplate(service, "")
132 return endpoint, nil
133 }
134 135 if endpointServiceName != "" {
136 endpoint := strings.Replace(EndpointTemplateForRegionWithDot, "{endpoint_service_name}", endpointServiceName, 1)
137 endpoint = strings.Replace(endpoint, "{region}", string(region), 1)
138 Debugf("Constructing endpoint from service name %s and region %s. Endpoint: %s", endpointServiceName, region, endpoint)
139 return endpoint, nil
140 }
141 if serviceEndpointTemplate != "" {
142 var endpoint = ""
143 res := strings.Split(serviceEndpointTemplate, "//")
144 if len(res) > 1 {
145 res = strings.Split(res[1], ".")
146 if len(res) > 1 {
147 endpoint = strings.Replace(EndpointTemplateForRegionWithDot, "{endpoint_service_name}", res[0], 1)
148 endpoint = strings.Replace(endpoint, "{region}", string(region), 1)
149 Debugf("Constructing endpoint from service endpoint template %s and region %s. Endpoint: %s", serviceEndpointTemplate, region, endpoint)
150 } else {
151 return endpoint, fmt.Errorf("Endpoint service name not present in endpoint template")
152 }
153 } else {
154 return endpoint, fmt.Errorf("invalid serviceEndpointTemplates. ServiceEndpointTemplate should start with https://")
155 }
156 return endpoint, nil
157 }
158 return "", fmt.Errorf("EndpointForTemplateDottedRegion function requires endpointServiceName or serviceEndpointTemplate, no endpointServiceName or serviceEndpointTemplate provided")
159 }
160 161 func (region Region) SecondLevelDomain() string {
162 if realmID, ok := regionRealm[region]; ok {
163 if secondLevelDomain, ok := realm[realmID]; ok {
164 return secondLevelDomain
165 }
166 }
167 if value, ok := os.LookupEnv(defaultRealmEnvVarName); ok {
168 return value
169 }
170 Debugf("cannot find realm for region : %s, return default realm value.", region)
171 if _, ok := realm["oc1"]; !ok {
172 return defaultRealmForUnknownDeveloperToolConfigurationRegion
173 }
174 return realm["oc1"]
175 }
176 177 // RealmID is used for getting realmID from region, if no region found, directly throw error
178 func (region Region) RealmID() (string, error) {
179 if realmID, ok := regionRealm[region]; ok {
180 return realmID, nil
181 }
182 183 return "", fmt.Errorf("cannot find realm for region : %s", region)
184 }
185 186 // StringToRegion convert a string to Region type
187 func StringToRegion(stringRegion string) (r Region) {
188 regionStr := strings.ToLower(stringRegion)
189 // check for PLC related regions
190 if checkAllowOnlyDeveloperToolConfigurationRegions() && (checkDeveloperToolConfigurationFile() || len(ociDeveloperToolConfigurationRegionSchemaList) != 0) {
191 Debugf("Developer Tool config detected and OCI_ALLOW_ONLY_DEVELOPER_TOOL_CONFIGURATION_REGIONS is set to True, SDK will only use regions defined for Developer Tool Configuration Regions")
192 setRegionMetadataFromDeveloperToolConfigurationFile(&stringRegion)
193 if len(ociDeveloperToolConfigurationRegionSchemaList) != 0 {
194 resetRegionInfo()
195 bulkAddRegionSchema(ociDeveloperToolConfigurationRegionSchemaList)
196 }
197 r = Region(stringRegion)
198 if _, ok := regionRealm[r]; !ok {
199 Logf("You're using the %s Developer Tool configuration file, the region you're targeting is not declared in this config file. Please check if this is the correct region you're targeting or contact the %s cloud provider for help. If you want to target both OCI regions and %s regions, please set the OCI_ALLOW_ONLY_DEVELOPER_TOOL_CONFIGURATION_REGIONS env var to False.", OciDeveloperToolConfigurationProvider, OciDeveloperToolConfigurationProvider, regionStr)
200 }
201 return r
202 }
203 204 // check if short region name provided
205 if region, ok := shortNameRegion[regionStr]; ok {
206 r = region
207 return
208 }
209 // check if normal region name provided
210 potentialRegion := Region(regionStr)
211 if _, ok := regionRealm[potentialRegion]; ok {
212 r = potentialRegion
213 return
214 }
215 216 Debugf("region named: %s, is not recognized from hard-coded region list, will check Region metadata info", stringRegion)
217 r = checkAndAddRegionMetadata(stringRegion)
218 219 return
220 }
221 222 // canStringBeRegion test if the string can be a region, if it can, returns the string as is, otherwise it
223 // returns an error
224 var blankRegex = regexp.MustCompile(`\s`)
225 226 func canStringBeRegion(stringRegion string) (region string, err error) {
227 if blankRegex.MatchString(stringRegion) || stringRegion == "" {
228 return "", fmt.Errorf("region can not be empty or have spaces")
229 }
230 return stringRegion, nil
231 }
232 233 // check region info from original map
234 func checkAndAddRegionMetadata(region string) Region {
235 switch {
236 case setRegionMetadataFromCfgFile(®ion):
237 case setRegionMetadataFromEnvVar(®ion):
238 case setRegionFromInstanceMetadataService(®ion):
239 default:
240 //err := fmt.Errorf("failed to get region metadata information.")
241 return Region(region)
242 }
243 return Region(region)
244 }
245 246 // EnableInstanceMetadataServiceLookup provides the interface to lookup IMDS region info
247 func EnableInstanceMetadataServiceLookup() {
248 Debugf("Set visitIMDS 'true' to enable IMDS Lookup.")
249 visitIMDS = true
250 }
251 252 // setRegionMetadataFromEnvVar checks if region metadata env variable is provided, once it's there, parse and added it
253 // to region map, and it can make sure the env var can only be visited once.
254 // Once successfully find the expected region(region name or short code), return true, region name will be stored in
255 // the input pointer.
256 func setRegionMetadataFromEnvVar(region *string) bool {
257 if !readEnvVar {
258 Debugf("metadata region env variable had already been checked, no need to check again.")
259 return false //no need to check it again.
260 }
261 // Mark readEnvVar Flag as false since it has already been visited.
262 readEnvVar = false
263 // check from env variable
264 if jsonStr, existed := os.LookupEnv(regionMetadataEnvVarName); existed {
265 Debugf("Raw content of region metadata env var:", jsonStr)
266 var regionSchema map[string]string
267 if err := json.Unmarshal([]byte(jsonStr), ®ionSchema); err != nil {
268 Debugf("Can't unmarshal env var, the error info is", err)
269 return false
270 }
271 // check if the specified region is in the env var.
272 if checkSchemaItems(regionSchema) {
273 // set mapping table
274 addRegionSchema(regionSchema)
275 if regionSchema[regionKeyPropertyName] == *region ||
276 regionSchema[regionIdentifierPropertyName] == *region {
277 *region = regionSchema[regionIdentifierPropertyName]
278 return true
279 }
280 }
281 return false
282 }
283 Debugf("The Region Metadata Schema wasn't set in env variable - OCI_REGION_METADATA.")
284 return false
285 }
286 287 func setRegionMetadataFromCfgFile(region *string) bool {
288 if setRegionMetadataFromDeveloperToolConfigurationFile(region) {
289 return true
290 }
291 if setRegionMetadataFromRegionCfgFile(region) {
292 return true
293 }
294 return false
295 }
296 297 // setRegionMetadataFromCfgFile checks if region metadata config file is provided, once it's there, parse and add all
298 // the valid regions to region map, the configuration file can only be visited once.
299 // Once successfully find the expected region(region name or short code), return true, region name will be stored in
300 // the input pointer.
301 func setRegionMetadataFromRegionCfgFile(region *string) bool {
302 if !readCfgFile {
303 Debugf("metadata region config file had already been checked, no need to check again.")
304 return false //no need to check it again.
305 }
306 // Mark readCfgFile Flag as false since it has already been visited.
307 readCfgFile = false
308 homeFolder := getHomeFolder()
309 configFile := filepath.Join(homeFolder, regionMetadataCfgDirName, regionMetadataCfgFileName)
310 if jsonArr, ok := readAndParseConfigFile(&configFile); ok {
311 added := false
312 for _, jsonItem := range jsonArr {
313 if checkSchemaItems(jsonItem) {
314 addRegionSchema(jsonItem)
315 if jsonItem[regionKeyPropertyName] == *region ||
316 jsonItem[regionIdentifierPropertyName] == *region {
317 *region = jsonItem[regionIdentifierPropertyName]
318 added = true
319 }
320 }
321 }
322 return added
323 }
324 return false
325 }
326 327 // setRegionMetadataFromDeveloperToolConfigurationFile checks if Developer Tool config file is provided, once it's there, parse and add all
328 // The default location of the Developer Tool config file is ~/.oci/developer-tool-configuration.json. It will also check the environment variable
329 // the valid regions to region map, the configuration file can only be visited once.
330 // Once successfully find the expected region(region name or short code), return true, region name will be stored in
331 // the input pointer.
332 func setRegionMetadataFromDeveloperToolConfigurationFile(region *string) bool {
333 if jsonArr, ok := readAndParseDeveloperToolConfigurationFile(); ok {
334 added := false
335 if jsonArr["regions"] == nil {
336 return false
337 }
338 var regionJSON []map[string]string
339 originalJSONContent, err := json.Marshal(jsonArr["regions"])
340 if err != nil {
341 return false
342 }
343 err = json.Unmarshal(originalJSONContent, ®ionJSON)
344 if err != nil {
345 return false
346 }
347 348 if IsEnvVarTrue(OciAllowOnlyDeveloperToolConfigurationRegionsEnvVar) {
349 resetRegionInfo()
350 }
351 for _, jsonItem := range regionJSON {
352 if checkSchemaItems(jsonItem) {
353 addRegionSchema(jsonItem)
354 if jsonItem[regionKeyPropertyName] == *region ||
355 jsonItem[regionIdentifierPropertyName] == *region {
356 *region = jsonItem[regionIdentifierPropertyName]
357 added = true
358 }
359 }
360 }
361 return added
362 }
363 return false
364 }
365 366 func readAndParseConfigFile(configFileName *string) (fileContent []map[string]string, ok bool) {
367 if content, err := ioutil.ReadFile(*configFileName); err == nil {
368 Debugf("Raw content of region metadata config file content:", string(content[:]))
369 if err := json.Unmarshal(content, &fileContent); err != nil {
370 Debugf("Can't unmarshal config file, the error info is", err)
371 return
372 }
373 ok = true
374 return
375 }
376 Debugf("No Region Metadata Config File provided.")
377 return
378 }
379 380 func readAndParseDeveloperToolConfigurationFile() (fileContent map[string]interface{}, ok bool) {
381 homeFolder := getHomeFolder()
382 configFileName := filepath.Join(homeFolder, regionMetadataCfgDirName, "developer-tool-configuration.json")
383 if path := os.Getenv(OciDeveloperToolConfigurationFilePathEnvVar); path != "" {
384 configFileName = path
385 }
386 if content, err := ioutil.ReadFile(configFileName); err == nil {
387 Debugf("Raw content of Developer Tool config file content:", string(content[:]))
388 if err := json.Unmarshal(content, &fileContent); err != nil {
389 Debugf("Can't unmarshal env var, the error info is", err)
390 return
391 }
392 ok = true
393 return
394 }
395 Debugf("No Developer Tool Config File provided.")
396 return
397 }
398 399 func checkDeveloperToolConfigurationFile() bool {
400 homeFolder := getHomeFolder()
401 configFileName := filepath.Join(homeFolder, regionMetadataCfgDirName, "developer-tool-configuration.json")
402 if path := os.Getenv(OciDeveloperToolConfigurationFilePathEnvVar); path != "" {
403 configFileName = path
404 }
405 if _, err := os.Stat(configFileName); err == nil {
406 return true
407 }
408 return false
409 }
410 411 // check map regionRealm's region name, if it's already there, no need to add it.
412 func addRegionSchema(regionSchema map[string]string) {
413 r := Region(strings.ToLower(regionSchema[regionIdentifierPropertyName]))
414 if _, ok := regionRealm[r]; !ok {
415 // set mapping table
416 shortNameRegion[regionSchema[regionKeyPropertyName]] = r
417 realm[regionSchema[realmKeyPropertyName]] = regionSchema[realmDomainComponentPropertyName]
418 regionRealm[r] = regionSchema[realmKeyPropertyName]
419 return
420 }
421 Debugf("Region {} has already been added, no need to add again.", regionSchema[regionIdentifierPropertyName])
422 }
423 424 // AddRegionSchemaForPlc add region schema to region map
425 func AddRegionSchemaForPlc(regionSchema map[string]string) {
426 ociDeveloperToolConfigurationRegionSchemaList = append(ociDeveloperToolConfigurationRegionSchemaList, regionSchema)
427 addRegionSchema(regionSchema)
428 // if !IsEnvVarTrue(OciPlcRegionExclusiveEnvVar) {
429 // addRegionSchema(regionSchema)
430 // return
431 // }
432 // Debugf("Plc region coexist is not enabled, remove exisiting OCI region schema and add PLC region schema.")
433 // resetRegionInfo()
434 // bulkAddRegionSchema(ociPlcRegionSchemaList)
435 }
436 437 func resetRegionInfo() {
438 shortNameRegion = make(map[string]Region)
439 realm = make(map[string]string)
440 regionRealm = make(map[Region]string)
441 }
442 443 func bulkAddRegionSchema(regionSchemaList []map[string]string) {
444 for _, regionSchema := range regionSchemaList {
445 if checkSchemaItems(regionSchema) {
446 addRegionSchema(regionSchema)
447 }
448 }
449 }
450 451 // check region schema content if all the required contents are provided
452 func checkSchemaItems(regionSchema map[string]string) bool {
453 if checkSchemaItem(regionSchema, regionIdentifierPropertyName) &&
454 checkSchemaItem(regionSchema, realmKeyPropertyName) &&
455 checkSchemaItem(regionSchema, realmDomainComponentPropertyName) &&
456 checkSchemaItem(regionSchema, regionKeyPropertyName) {
457 return true
458 }
459 return false
460 }
461 462 // check region schema item is valid, if so, convert it to lower case.
463 func checkSchemaItem(regionSchema map[string]string, key string) bool {
464 if val, ok := regionSchema[key]; ok {
465 if val != "" {
466 regionSchema[key] = strings.ToLower(val)
467 return true
468 }
469 Debugf("Region metadata schema {} is provided,but content is empty.", key)
470 return false
471 }
472 Debugf("Region metadata schema {} is not provided, please update the content", key)
473 return false
474 }
475 476 // setRegionFromInstanceMetadataService checks if region metadata can be provided from InstanceMetadataService.
477 // Once successfully find the expected region(region name or short code), return true, region name will be stored in
478 // the input pointer.
479 // setRegionFromInstanceMetadataService will only be checked on the instance, by default it will not be enabled unless
480 // user explicitly enable it.
481 func setRegionFromInstanceMetadataService(region *string) bool {
482 // example of content:
483 // {
484 // "realmKey" : "oc1",
485 // "realmDomainComponent" : "oraclecloud.com",
486 // "regionKey" : "YUL",
487 // "regionIdentifier" : "ca-montreal-1"
488 // }
489 // Mark visitIMDS Flag as false since it has already been visited.
490 if !visitIMDS {
491 Debugf("check from IMDS is disabled or IMDS had already been successfully visited, no need to check again.")
492 return false
493 }
494 content, err := getRegionInfoFromInstanceMetadataService()
495 if err != nil {
496 Debugf("Failed to get instance metadata. Error: %v", err)
497 return false
498 }
499 500 // Mark visitIMDS Flag as false since we have already successfully get the region info from IMDS.
501 visitIMDS = false
502 503 var regionInfo map[string]string
504 err = json.Unmarshal(content, ®ionInfo)
505 if err != nil {
506 Debugf("Failed to unmarshal the response content: %v \nError: %v", string(content), err)
507 return false
508 }
509 510 if checkSchemaItems(regionInfo) {
511 addRegionSchema(regionInfo)
512 if regionInfo[regionKeyPropertyName] == *region ||
513 regionInfo[regionIdentifierPropertyName] == *region {
514 *region = regionInfo[regionIdentifierPropertyName]
515 }
516 } else {
517 Debugf("Region information is not valid.")
518 return false
519 }
520 521 return true
522 }
523 524 // getRegionInfoFromInstanceMetadataServiceProd calls instance metadata service and get the region information
525 func getRegionInfoFromInstanceMetadataServiceProd() ([]byte, error) {
526 request, _ := http.NewRequest(http.MethodGet, instanceMetadataRegionInfoURLV2, nil)
527 request.Header.Add("Authorization", "Bearer Oracle")
528 529 client := &http.Client{
530 Timeout: time.Second * 10,
531 }
532 resp, err := client.Do(request)
533 if err != nil {
534 return nil, fmt.Errorf("failed to call instance metadata service. Error: %v", err)
535 }
536 537 statusCode := resp.StatusCode
538 539 defer resp.Body.Close()
540 541 content, err := ioutil.ReadAll(resp.Body)
542 if err != nil {
543 return nil, fmt.Errorf("failed to get region information from response body. Error: %v", err)
544 }
545 546 if statusCode != http.StatusOK {
547 err = fmt.Errorf("HTTP Get failed: URL: %s, Status: %s, Message: %s",
548 instanceMetadataRegionInfoURLV2, resp.Status, string(content))
549 return nil, err
550 }
551 552 return content, nil
553 }
554 555 // TemplateParamForPerRealmEndpoint is a template parameter for per-realm endpoint.
556 type TemplateParamForPerRealmEndpoint struct {
557 Template string
558 EndsWithDot bool
559 }
560 561 // SetMissingTemplateParams function will parse the {} template in client host and replace with empty string.
562 func SetMissingTemplateParams(client *BaseClient) {
563 templateRegex := regexp.MustCompile(`{.*?}`)
564 templates := templateRegex.FindAllString(client.Host, -1)
565 for _, template := range templates {
566 client.Host = strings.Replace(client.Host, template, "", -1)
567 }
568 }
569 570 func getOciSdkEnabledServicesMap() map[string]bool {
571 var enabledMap = make(map[string]bool)
572 if jsonArr, ok := readAndParseDeveloperToolConfigurationFile(); ok {
573 if jsonArr["provider"] != nil {
574 OciDeveloperToolConfigurationProvider = jsonArr["provider"].(string)
575 }
576 if jsonArr["allowOnlyDeveloperToolConfigurationRegions"] != nil && jsonArr["allowOnlyDeveloperToolConfigurationRegions"] == false {
577 ociAllowOnlyDeveloperToolConfigurationRegions = jsonArr["allowOnlyDeveloperToolConfigurationRegions"].(bool)
578 }
579 if jsonArr["services"] == nil {
580 return enabledMap
581 }
582 serviesJSON, ok := jsonArr["services"].([]interface{})
583 if !ok {
584 return enabledMap
585 }
586 re, _ := regexp.Compile(`[^\w]`)
587 for _, jsonItem := range serviesJSON {
588 serviceName := strings.ToLower(fmt.Sprint(jsonItem))
589 serviceName = re.ReplaceAllString(serviceName, "")
590 enabledMap[serviceName] = true
591 }
592 }
593 return enabledMap
594 }
595 596 // AddServiceToEnabledServicesMap adds the service to the enabledServiceMap
597 // The service name will auto transit to lower case and remove all the non-word characters.
598 // Concurrency (goroutine-safe). The map is initialized with sync.Once, and writes are protected by a RWMutex.
599 func AddServiceToEnabledServicesMap(serviceName string) {
600 OciSdkEnabledServicesOnce.Do(func() {
601 OciSdkEnabledServicesMap = getOciSdkEnabledServicesMap()
602 if OciSdkEnabledServicesMap == nil {
603 OciSdkEnabledServicesMap = make(map[string]bool)
604 }
605 })
606 serviceName = strings.ToLower(serviceName)
607 serviceName = reNonWord.ReplaceAllString(serviceName, "")
608 609 OciSdkEnabledServicesMu.Lock()
610 defer OciSdkEnabledServicesMu.Unlock()
611 OciSdkEnabledServicesMap[serviceName] = true
612 }
613 614 // CheckForEnabledServices checks if the service is enabled in the enabledServiceMap.
615 // It will first check if the map is initialized, if not, it will initialize the map.
616 // If the map is empty, it means all the services are enabled.
617 // If the map is not empty, it means only the services in the map and value is true are enabled.
618 // Concurrency (goroutine-safe). Initialization uses sync.Once and reads are protected by a RWMutex.
619 func CheckForEnabledServices(serviceName string) bool {
620 OciSdkEnabledServicesOnce.Do(func() {
621 OciSdkEnabledServicesMap = getOciSdkEnabledServicesMap()
622 if OciSdkEnabledServicesMap == nil {
623 OciSdkEnabledServicesMap = make(map[string]bool)
624 }
625 })
626 serviceName = strings.ToLower(serviceName)
627 serviceName = reNonWord.ReplaceAllString(serviceName, "")
628 629 OciSdkEnabledServicesMu.RLock()
630 defer OciSdkEnabledServicesMu.RUnlock()
631 632 if len(OciSdkEnabledServicesMap) == 0 {
633 return true
634 }
635 allowed, ok := OciSdkEnabledServicesMap[serviceName]
636 if !ok {
637 return false
638 }
639 return allowed
640 }
641 642 // CheckAllowOnlyDeveloperToolConfigurationRegions checks if only developer tool configuration regions are allowed
643 // This function will first check if the OCI_ALLOW_ONLY_DEVELOPER_TOOL_CONFIGURATION_REGIONS environment variable is set.
644 // If it is set, it will return the value.
645 // If it is not set, it will return the value from the ociAllowOnlyDeveloperToolConfigurationRegions variable.
646 func checkAllowOnlyDeveloperToolConfigurationRegions() bool {
647 if val, ok := os.LookupEnv("OCI_ALLOW_ONLY_DEVELOPER_TOOL_CONFIGURATION_REGIONS"); ok {
648 return val == "true"
649 }
650 return ociAllowOnlyDeveloperToolConfigurationRegions
651 }
652