github.com/sacloud/iaas-api-go@v1.12.0/helper/api/options.go (about)

     1  // Copyright 2022-2023 The sacloud/iaas-api-go Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package api
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"runtime"
    21  
    22  	client "github.com/sacloud/api-client-go"
    23  	"github.com/sacloud/api-client-go/profile"
    24  	"github.com/sacloud/iaas-api-go"
    25  	"github.com/sacloud/packages-go/envvar"
    26  )
    27  
    28  var UserAgent = fmt.Sprintf(
    29  	"sacloud/iaas-api-go/v%s (%s/%s; +https://github.com/sacloud/iaas-api-go) %s",
    30  	iaas.Version,
    31  	runtime.GOOS,
    32  	runtime.GOARCH,
    33  	client.DefaultUserAgent,
    34  )
    35  
    36  // CallerOptions iaas.APICallerを作成する際のオプション
    37  type CallerOptions struct {
    38  	*client.Options
    39  
    40  	APIRootURL  string
    41  	DefaultZone string
    42  	Zones       []string
    43  
    44  	TraceAPI      bool
    45  	FakeMode      bool
    46  	FakeStorePath string
    47  }
    48  
    49  // DefaultOption 環境変数、プロファイルからCallerOptionsを組み立てて返す
    50  //
    51  // プロファイルは環境変数`SAKURACLOUD_PROFILE`または`USACLOUD_PROFILE`でプロファイル名が指定されていればそちらを優先し、
    52  // 未指定の場合は通常のプロファイル処理(~/.usacloud/currentファイルから読み込み)される。
    53  // 同じ項目を複数箇所で指定していた場合、環境変数->プロファイルの順で上書きされたものが返される
    54  func DefaultOption() (*CallerOptions, error) {
    55  	return DefaultOptionWithProfile("")
    56  }
    57  
    58  // DefaultOptionWithProfile 環境変数、プロファイルからCallerOptionsを組み立てて返す
    59  //
    60  // プロファイルは引数を優先し、空の場合は環境変数`SAKURACLOUD_PROFILE`または`USACLOUD_PROFILE`が利用され、
    61  // それも空の場合は通常のプロファイル処理(~/.usacloud/currentファイルから読み込み)される。
    62  // 同じ項目を複数箇所で指定していた場合、環境変数->プロファイルの順で上書きされたものが返される
    63  func DefaultOptionWithProfile(profileName string) (*CallerOptions, error) {
    64  	options, err := client.DefaultOptionWithProfile(profileName)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	fromEnv := OptionsFromEnv()
    70  	fromEnv.Options = options
    71  
    72  	fromProfile, err := OptionsFromProfile(profileName)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	fromProfile.Options = options
    77  
    78  	defaults := &CallerOptions{
    79  		APIRootURL:  iaas.SakuraCloudAPIRoot,
    80  		DefaultZone: iaas.APIDefaultZone,
    81  		Zones:       iaas.SakuraCloudZones,
    82  		Options: &client.Options{
    83  			UserAgent: UserAgent,
    84  		},
    85  	}
    86  
    87  	return MergeOptions(defaults, fromEnv, fromProfile), nil
    88  }
    89  
    90  // OptionsFromEnv 環境変数からCallerOptionsを組み立てて返す
    91  func OptionsFromEnv() *CallerOptions {
    92  	return &CallerOptions{
    93  		Options:     client.OptionsFromEnv(),
    94  		APIRootURL:  envvar.StringFromEnv("SAKURACLOUD_API_ROOT_URL", ""),
    95  		DefaultZone: envvar.StringFromEnv("SAKURACLOUD_DEFAULT_ZONE", ""),
    96  		Zones:       envvar.StringSliceFromEnv("SAKURACLOUD_ZONES", []string{}),
    97  
    98  		TraceAPI: profile.EnableAPITrace(envvar.StringFromEnv("SAKURACLOUD_TRACE", "")),
    99  
   100  		FakeMode:      os.Getenv("SAKURACLOUD_FAKE_MODE") != "",
   101  		FakeStorePath: envvar.StringFromEnv("SAKURACLOUD_FAKE_STORE_PATH", ""),
   102  	}
   103  }
   104  
   105  // OptionsFromProfile 指定のプロファイルからCallerOptionsを組み立てて返す
   106  // プロファイル名に空文字が指定された場合はカレントプロファイルが利用される
   107  func OptionsFromProfile(profileName string) (*CallerOptions, error) {
   108  	options, err := client.OptionsFromProfile(profileName)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	config := options.ProfileConfigValue()
   113  	return &CallerOptions{
   114  		Options:       options,
   115  		APIRootURL:    config.APIRootURL,
   116  		DefaultZone:   config.DefaultZone,
   117  		Zones:         config.Zones,
   118  		TraceAPI:      config.EnableAPITrace(),
   119  		FakeMode:      config.FakeMode,
   120  		FakeStorePath: config.FakeStorePath,
   121  	}, nil
   122  }
   123  
   124  // MergeOptions 指定のCallerOptionsの非ゼロ値フィールドをoのコピーにマージして返す
   125  func MergeOptions(opts ...*CallerOptions) *CallerOptions {
   126  	merged := &CallerOptions{}
   127  	for _, opt := range opts {
   128  		if opt.Options != nil {
   129  			var opts []*client.Options
   130  			if merged.Options != nil {
   131  				opts = append(opts, merged.Options)
   132  			}
   133  			opts = append(opts, opt.Options)
   134  			merged.Options = client.MergeOptions(opts...)
   135  		}
   136  		if opt.APIRootURL != "" {
   137  			merged.APIRootURL = opt.APIRootURL
   138  		}
   139  		if opt.DefaultZone != "" {
   140  			merged.DefaultZone = opt.DefaultZone
   141  		}
   142  		if len(opt.Zones) > 0 {
   143  			merged.Zones = opt.Zones
   144  		}
   145  
   146  		// Note: bool値は一度trueにしたらMergeでfalseになることがない
   147  		if opt.TraceAPI {
   148  			merged.TraceAPI = true
   149  		}
   150  		if opt.FakeMode {
   151  			merged.FakeMode = true
   152  		}
   153  		if opt.FakeStorePath != "" {
   154  			merged.FakeStorePath = opt.FakeStorePath
   155  		}
   156  	}
   157  	return merged
   158  }