diff --git a/.golangci.toml b/.golangci.toml index fa3097e9..81912c45 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -78,3 +78,6 @@ [[issues.exclude-rules]] path = "providers/http/memcached/memcached_test.go" text = "`memcachedHosts` is a global variable" + [[issues.exclude-rules]] + path = "providers/dns/sakuracloud/client_test.go" + text = "cyclomatic complexity 13 of func `(TestDNSProvider_cleanupTXTRecord_concurrent|TestDNSProvider_addTXTRecord_concurrent)` is high" diff --git a/Gopkg.lock b/Gopkg.lock index 3e3a84d7..7335fc56 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -431,17 +431,18 @@ revision = "1031fa0ce2f20c1c0e1e1b51951d8ea02c84fa05" [[projects]] - branch = "master" - digest = "1:180e8ec2d3734b269a8a30b51dbca47fede2ce274fa76da2f00e664481cfb39e" + digest = "1:5a632dc3d841d803524bd442976a52e390e4294826ece65722cb77020879d156" name = "github.com/sacloud/libsacloud" packages = [ ".", "api", "sacloud", "sacloud/ostype", + "utils/mutexkv", ] pruneopts = "NUT" - revision = "108b1efe4b4d106fee6760bdf1847c4f92e1a92e" + revision = "a949b57af53e809207587f8c41571d81f140276e" + version = "v1.19.0" [[projects]] digest = "1:6bc0652ea6e39e22ccd522458b8bdd8665bf23bdc5a20eec90056e4dc7e273ca" diff --git a/Gopkg.toml b/Gopkg.toml index 1a22381f..cbed5bdf 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -82,7 +82,7 @@ name = "gopkg.in/ns1/ns1-go.v2" [[constraint]] - branch = "master" + version = "1.19.0" name = "github.com/sacloud/libsacloud" [[constraint]] diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index 5bdefabe..a2887aa0 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -1014,6 +1014,7 @@ func displayDNSHelp(name string) { fmt.Fprintln(w) fmt.Fprintln(w, `Additional Configuration:`) + fmt.Fprintln(w, ` - "SAKURACLOUD_HTTP_TIMEOUT": API request timeout`) fmt.Fprintln(w, ` - "SAKURACLOUD_POLLING_INTERVAL": Time between DNS propagation check`) fmt.Fprintln(w, ` - "SAKURACLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) fmt.Fprintln(w, ` - "SAKURACLOUD_TTL": The TTL of the TXT record used for the DNS challenge`) diff --git a/docs/content/dns/zz_gen_sakuracloud.md b/docs/content/dns/zz_gen_sakuracloud.md index a29f666e..098ac754 100644 --- a/docs/content/dns/zz_gen_sakuracloud.md +++ b/docs/content/dns/zz_gen_sakuracloud.md @@ -39,6 +39,7 @@ More information [here](/lego/dns/#configuration-and-credentials). | Environment Variable Name | Description | |--------------------------------|-------------| +| `SAKURACLOUD_HTTP_TIMEOUT` | API request timeout | | `SAKURACLOUD_POLLING_INTERVAL` | Time between DNS propagation check | | `SAKURACLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | | `SAKURACLOUD_TTL` | The TTL of the TXT record used for the DNS challenge | diff --git a/providers/dns/sakuracloud/client.go b/providers/dns/sakuracloud/client.go new file mode 100644 index 00000000..10b8a1f6 --- /dev/null +++ b/providers/dns/sakuracloud/client.go @@ -0,0 +1,106 @@ +package sakuracloud + +import ( + "fmt" + "net/http" + "strings" + + "github.com/go-acme/lego/challenge/dns01" + "github.com/sacloud/libsacloud/api" + "github.com/sacloud/libsacloud/sacloud" +) + +const sacloudAPILockKey = "lego/dns/sacloud" + +func (d *DNSProvider) addTXTRecord(fqdn, domain, value string, ttl int) error { + sacloud.LockByKey(sacloudAPILockKey) + defer sacloud.UnlockByKey(sacloudAPILockKey) + + zone, err := d.getHostedZone(domain) + if err != nil { + return fmt.Errorf("sakuracloud: %v", err) + } + + name := d.extractRecordName(fqdn, zone.Name) + + zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, ttl)) + _, err = d.client.Update(zone.ID, zone) + if err != nil { + return fmt.Errorf("sakuracloud: API call failed: %v", err) + } + + return nil +} + +func (d *DNSProvider) cleanupTXTRecord(fqdn, domain string) error { + sacloud.LockByKey(sacloudAPILockKey) + defer sacloud.UnlockByKey(sacloudAPILockKey) + + zone, err := d.getHostedZone(domain) + if err != nil { + return fmt.Errorf("sakuracloud: %v", err) + } + + records := d.findTxtRecords(fqdn, zone) + + for _, record := range records { + var updRecords []sacloud.DNSRecordSet + for _, r := range zone.Settings.DNS.ResourceRecordSets { + if !(r.Name == record.Name && r.Type == record.Type && r.RData == record.RData) { + updRecords = append(updRecords, r) + } + } + zone.Settings.DNS.ResourceRecordSets = updRecords + } + + _, err = d.client.Update(zone.ID, zone) + if err != nil { + return fmt.Errorf("sakuracloud: API call failed: %v", err) + } + return nil +} + +func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { + authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) + if err != nil { + return nil, err + } + + zoneName := dns01.UnFqdn(authZone) + + res, err := d.client.Reset().WithNameLike(zoneName).Find() + if err != nil { + if notFound, ok := err.(api.Error); ok && notFound.ResponseCode() == http.StatusNotFound { + return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS: %v", zoneName, err) + } + return nil, fmt.Errorf("API call failed: %v", err) + } + + for _, zone := range res.CommonServiceDNSItems { + if zone.Name == zoneName { + return &zone, nil + } + } + + return nil, fmt.Errorf("zone %s not found", zoneName) +} + +func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) []sacloud.DNSRecordSet { + recordName := d.extractRecordName(fqdn, zone.Name) + + var res []sacloud.DNSRecordSet + for _, record := range zone.Settings.DNS.ResourceRecordSets { + if record.Name == recordName && record.Type == "TXT" { + res = append(res, record) + } + } + return res +} + +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { + name := dns01.UnFqdn(fqdn) + if idx := strings.Index(name, "."+domain); idx != -1 { + return name[:idx] + } + return name +} diff --git a/providers/dns/sakuracloud/client_test.go b/providers/dns/sakuracloud/client_test.go new file mode 100644 index 00000000..e8a1c62a --- /dev/null +++ b/providers/dns/sakuracloud/client_test.go @@ -0,0 +1,320 @@ +package sakuracloud + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "testing" + + "github.com/sacloud/libsacloud/api" + "github.com/sacloud/libsacloud/sacloud" + "github.com/stretchr/testify/require" +) + +type simpleResponse struct { + *sacloud.DNS `json:"CommonServiceItem,omitempty"` +} + +type apiQuery struct { + Filter struct { + Name string `json:"Name"` + ProviderClass string `json:"Provider.Class"` + } `json:"Filter"` +} + +func fakeAPIServer(handler func(rw http.ResponseWriter, req *http.Request)) func() { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + + mux.HandleFunc("/is1a/api/cloud/1.1/commonserviceitem/", handler) + + backup := api.SakuraCloudAPIRoot + api.SakuraCloudAPIRoot = server.URL + return func() { + api.SakuraCloudAPIRoot = backup + } +} + +func TestDNSProvider_addTXTRecord(t *testing.T) { + searchResp := &api.SearchDNSResponse{} + tearDown := fakeAPIServer(func(rw http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + if len(searchResp.CommonServiceDNSItems) == 0 { + q := &apiQuery{} + if err := json.Unmarshal([]byte(req.URL.RawQuery), q); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + fakeZone := sacloud.CreateNewDNS(q.Filter.Name) + fakeZone.ID = 123456789012 + searchResp = &api.SearchDNSResponse{CommonServiceDNSItems: []sacloud.DNS{*fakeZone}} + } + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + case http.MethodPut: // Update + resp := &simpleResponse{} + if err := json.NewDecoder(req.Body).Decode(resp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + var items []sacloud.DNS + for _, v := range searchResp.CommonServiceDNSItems { + if resp.Name == v.Name { + items = append(items, *resp.DNS) + } else { + items = append(items, v) + } + } + searchResp.CommonServiceDNSItems = items + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + default: + http.Error(rw, "OOPS", http.StatusServiceUnavailable) + } + }) + defer tearDown() + + config := NewDefaultConfig() + config.Token = "token1" + config.Secret = "secret1" + + p, err := NewDNSProviderConfig(config) + require.NoError(t, err) + + err = p.addTXTRecord("test.example.com", "example.com", "dummyValue", 10) + require.NoError(t, err) + + updZone, err := p.getHostedZone("example.com") + require.NoError(t, err) + require.NotNil(t, updZone) + + require.Len(t, updZone.Settings.DNS.ResourceRecordSets, 1) +} + +func TestDNSProvider_cleanupTXTRecord(t *testing.T) { + searchResp := &api.SearchDNSResponse{} + + tearDown := fakeAPIServer(func(rw http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + if len(searchResp.CommonServiceDNSItems) == 0 { + q := &apiQuery{} + if err := json.Unmarshal([]byte(req.URL.RawQuery), q); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + fakeZone := sacloud.CreateNewDNS(q.Filter.Name) + fakeZone.ID = 123456789012 + fakeZone.CreateNewRecord("test", "TXT", "dummyValue", 10) + searchResp = &api.SearchDNSResponse{CommonServiceDNSItems: []sacloud.DNS{*fakeZone}} + } + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + case http.MethodPut: // Update + resp := &simpleResponse{} + if err := json.NewDecoder(req.Body).Decode(resp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + var items []sacloud.DNS + for _, v := range searchResp.CommonServiceDNSItems { + if resp.Name == v.Name { + items = append(items, *resp.DNS) + } else { + items = append(items, v) + } + } + searchResp.CommonServiceDNSItems = items + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + default: + http.Error(rw, "OOPS", http.StatusServiceUnavailable) + } + }) + defer tearDown() + + config := NewDefaultConfig() + config.Token = "token2" + config.Secret = "secret2" + + p, err := NewDNSProviderConfig(config) + require.NoError(t, err) + + err = p.cleanupTXTRecord("test.example.com", "example.com") + require.NoError(t, err) + + updZone, err := p.getHostedZone("example.com") + require.NoError(t, err) + require.NotNil(t, updZone) + + require.Len(t, updZone.Settings.DNS.ResourceRecordSets, 0) +} + +func TestDNSProvider_addTXTRecord_concurrent(t *testing.T) { + searchResp := &api.SearchDNSResponse{} + + tearDown := fakeAPIServer(func(rw http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + if len(searchResp.CommonServiceDNSItems) == 0 { + q := &apiQuery{} + if err := json.Unmarshal([]byte(req.URL.RawQuery), q); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + fakeZone := sacloud.CreateNewDNS(q.Filter.Name) + fakeZone.ID = 123456789012 + searchResp = &api.SearchDNSResponse{CommonServiceDNSItems: []sacloud.DNS{*fakeZone}} + } + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + case http.MethodPut: // Update + resp := &simpleResponse{} + if err := json.NewDecoder(req.Body).Decode(resp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + var items []sacloud.DNS + for _, v := range searchResp.CommonServiceDNSItems { + if resp.Name == v.Name { + items = append(items, *resp.DNS) + } else { + items = append(items, v) + } + } + searchResp.CommonServiceDNSItems = items + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + default: + http.Error(rw, "OOPS", http.StatusServiceUnavailable) + } + }) + defer tearDown() + + dummyRecordCount := 10 + + var providers []*DNSProvider + for i := 0; i < dummyRecordCount; i++ { + config := NewDefaultConfig() + config.Token = "token3" + config.Secret = "secret3" + + p, err := NewDNSProviderConfig(config) + require.NoError(t, err) + + providers = append(providers, p) + } + + var wg sync.WaitGroup + wg.Add(len(providers)) + + for i, p := range providers { + go func(fqdn string, client *DNSProvider) { + err := client.addTXTRecord(fqdn, "example.com", "dummyValue", 10) + require.NoError(t, err) + wg.Done() + }(fmt.Sprintf("test%d.example.com", i), p) + } + + wg.Wait() + + updZone, err := providers[0].getHostedZone("example.com") + require.NoError(t, err) + require.NotNil(t, updZone) + + require.Len(t, updZone.Settings.DNS.ResourceRecordSets, dummyRecordCount) +} + +func TestDNSProvider_cleanupTXTRecord_concurrent(t *testing.T) { + dummyRecordCount := 10 + + baseFakeZone := sacloud.CreateNewDNS("example.com") + baseFakeZone.ID = 123456789012 + for i := 0; i < dummyRecordCount; i++ { + baseFakeZone.AddRecord(baseFakeZone.CreateNewRecord(fmt.Sprintf("test%d", i), "TXT", "dummyValue", 10)) + } + + searchResp := &api.SearchDNSResponse{CommonServiceDNSItems: []sacloud.DNS{*baseFakeZone}} + + tearDown := fakeAPIServer(func(rw http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + case http.MethodPut: // Update + resp := &simpleResponse{} + if err := json.NewDecoder(req.Body).Decode(resp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + + var items []sacloud.DNS + for _, v := range searchResp.CommonServiceDNSItems { + if resp.Name == v.Name { + items = append(items, *resp.DNS) + } else { + items = append(items, v) + } + } + searchResp.CommonServiceDNSItems = items + + if err := json.NewEncoder(rw).Encode(searchResp); err != nil { + http.Error(rw, err.Error(), http.StatusServiceUnavailable) + } + default: + http.Error(rw, "OOPS", http.StatusServiceUnavailable) + } + }) + defer tearDown() + + fakeZone := sacloud.CreateNewDNS("example.com") + fakeZone.ID = 123456789012 + for i := 0; i < dummyRecordCount; i++ { + fakeZone.AddRecord(fakeZone.CreateNewRecord(fmt.Sprintf("test%d", i), "TXT", "dummyValue", 10)) + } + + var providers []*DNSProvider + for i := 0; i < dummyRecordCount; i++ { + config := NewDefaultConfig() + config.Token = "token4" + config.Secret = "secret4" + + p, err := NewDNSProviderConfig(config) + require.NoError(t, err) + providers = append(providers, p) + } + + var wg sync.WaitGroup + wg.Add(len(providers)) + + for i, p := range providers { + go func(fqdn string, client *DNSProvider) { + err := client.cleanupTXTRecord(fqdn, "example.com") + require.NoError(t, err) + wg.Done() + }(fmt.Sprintf("test%d.example.com", i), p) + } + + wg.Wait() + + updZone, err := providers[0].getHostedZone("example.com") + require.NoError(t, err) + require.NotNil(t, updZone) + + require.Len(t, updZone.Settings.DNS.ResourceRecordSets, 0) +} diff --git a/providers/dns/sakuracloud/sakuracloud.go b/providers/dns/sakuracloud/sakuracloud.go index a7ffc81e..87f55a0e 100644 --- a/providers/dns/sakuracloud/sakuracloud.go +++ b/providers/dns/sakuracloud/sakuracloud.go @@ -5,13 +5,11 @@ import ( "errors" "fmt" "net/http" - "strings" "time" "github.com/go-acme/lego/challenge/dns01" "github.com/go-acme/lego/platform/config/env" "github.com/sacloud/libsacloud/api" - "github.com/sacloud/libsacloud/sacloud" ) // Config is used to configure the creation of the DNSProvider @@ -21,6 +19,7 @@ type Config struct { PropagationTimeout time.Duration PollingInterval time.Duration TTL int + HTTPClient *http.Client } // NewDefaultConfig returns a default configuration for the DNSProvider @@ -29,13 +28,16 @@ func NewDefaultConfig() *Config { TTL: env.GetOrDefaultInt("SAKURACLOUD_TTL", dns01.DefaultTTL), PropagationTimeout: env.GetOrDefaultSecond("SAKURACLOUD_PROPAGATION_TIMEOUT", dns01.DefaultPropagationTimeout), PollingInterval: env.GetOrDefaultSecond("SAKURACLOUD_POLLING_INTERVAL", dns01.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("SAKURACLOUD_HTTP_TIMEOUT", 10*time.Second), + }, } } // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { config *Config - client *api.Client + client *api.DNSAPI } // NewDNSProvider returns a DNSProvider instance configured for SakuraCloud. @@ -67,58 +69,29 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("sakuracloud: AccessSecret is missing") } - client := api.NewClient(config.Token, config.Secret, "tk1a") + apiClient := api.NewClient(config.Token, config.Secret, "is1a") + if config.HTTPClient == nil { + apiClient.HTTPClient = http.DefaultClient + } else { + apiClient.HTTPClient = config.HTTPClient + } - return &DNSProvider{client: client, config: config}, nil + return &DNSProvider{ + client: apiClient.GetDNSAPI(), + config: config, + }, nil } // Present creates a TXT record to fulfill the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value := dns01.GetRecord(domain, keyAuth) - - zone, err := d.getHostedZone(domain) - if err != nil { - return fmt.Errorf("sakuracloud: %v", err) - } - - name := d.extractRecordName(fqdn, zone.Name) - - zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, d.config.TTL)) - _, err = d.client.GetDNSAPI().Update(zone.ID, zone) - if err != nil { - return fmt.Errorf("sakuracloud: API call failed: %v", err) - } - - return nil + return d.addTXTRecord(fqdn, domain, value, d.config.TTL) } // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _ := dns01.GetRecord(domain, keyAuth) - - zone, err := d.getHostedZone(domain) - if err != nil { - return fmt.Errorf("sakuracloud: %v", err) - } - - records := d.findTxtRecords(fqdn, zone) - - for _, record := range records { - var updRecords []sacloud.DNSRecordSet - for _, r := range zone.Settings.DNS.ResourceRecordSets { - if !(r.Name == record.Name && r.Type == record.Type && r.RData == record.RData) { - updRecords = append(updRecords, r) - } - } - zone.Settings.DNS.ResourceRecordSets = updRecords - } - - _, err = d.client.GetDNSAPI().Update(zone.ID, zone) - if err != nil { - return fmt.Errorf("sakuracloud: API call failed: %v", err) - } - - return nil + return d.cleanupTXTRecord(fqdn, domain) } // Timeout returns the timeout and interval to use when checking for DNS propagation. @@ -126,48 +99,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { - authZone, err := dns01.FindZoneByFqdn(dns01.ToFqdn(domain)) - if err != nil { - return nil, err - } - - zoneName := dns01.UnFqdn(authZone) - - res, err := d.client.GetDNSAPI().WithNameLike(zoneName).Find() - if err != nil { - if notFound, ok := err.(api.Error); ok && notFound.ResponseCode() == http.StatusNotFound { - return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS: %v", zoneName, err) - } - return nil, fmt.Errorf("API call failed: %v", err) - } - - for _, zone := range res.CommonServiceDNSItems { - if zone.Name == zoneName { - return &zone, nil - } - } - - return nil, fmt.Errorf("zone %s not found", zoneName) -} - -func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) []sacloud.DNSRecordSet { - recordName := d.extractRecordName(fqdn, zone.Name) - - var res []sacloud.DNSRecordSet - for _, record := range zone.Settings.DNS.ResourceRecordSets { - if record.Name == recordName && record.Type == "TXT" { - res = append(res, record) - } - } - return res -} - -func (d *DNSProvider) extractRecordName(fqdn, domain string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+domain); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/sakuracloud/sakuracloud.toml b/providers/dns/sakuracloud/sakuracloud.toml index b49506a8..e5950a32 100644 --- a/providers/dns/sakuracloud/sakuracloud.toml +++ b/providers/dns/sakuracloud/sakuracloud.toml @@ -13,6 +13,7 @@ Example = '''''' SAKURACLOUD_POLLING_INTERVAL = "Time between DNS propagation check" SAKURACLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" SAKURACLOUD_TTL = "The TTL of the TXT record used for the DNS challenge" + SAKURACLOUD_HTTP_TIMEOUT = "API request timeout" [Links] API = "https://developer.sakura.ad.jp/cloud/api/1.1/" diff --git a/vendor/github.com/sacloud/libsacloud/api/archive.go b/vendor/github.com/sacloud/libsacloud/api/archive.go index 561c2c9a..be13faa7 100644 --- a/vendor/github.com/sacloud/libsacloud/api/archive.go +++ b/vendor/github.com/sacloud/libsacloud/api/archive.go @@ -39,6 +39,7 @@ var ( archiveLatestStableWindows2016SQLServer2017Standard = []string{"os-windows", "distro-ver-2016", "windows-sqlserver", "sqlserver-2017", "edition-standard"} archiveLatestStableWindows2016SQLServerStandardAll = []string{"os-windows", "distro-ver-2016", "windows-sqlserver", "sqlserver-2016", "edition-standard", "windows-rds", "with-office"} archiveLatestStableWindows2016SQLServer2017StandardAll = []string{"os-windows", "distro-ver-2016", "windows-sqlserver", "sqlserver-2017", "edition-standard", "windows-rds", "with-office"} + archiveLatestStableWindows2019Tags = []string{"os-windows", "distro-ver-2019"} ) // NewArchiveAPI アーカイブAPI作成 @@ -361,6 +362,13 @@ func (api *ArchiveAPI) FindLatestStableWindows2016SQLServer2017StandardAll() (*s }) } +// FindLatestStableWindows2019 安定版最新のWindows2019パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2019() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2019Tags, map[string]interface{}{ + "Name": "Windows Server 2019 Datacenter Edition", + }) +} + // FindByOSType 指定のOS種別の安定版最新のパブリックアーカイブを取得 func (api *ArchiveAPI) FindByOSType(os ostype.ArchiveOSTypes) (*sacloud.Archive, error) { if f, ok := api.findFuncMapPerOSType[os]; ok { diff --git a/vendor/github.com/sacloud/libsacloud/api/base_api.go b/vendor/github.com/sacloud/libsacloud/api/base_api.go index 9840938c..a0a25f2c 100644 --- a/vendor/github.com/sacloud/libsacloud/api/base_api.go +++ b/vendor/github.com/sacloud/libsacloud/api/base_api.go @@ -138,7 +138,7 @@ func (api *baseAPI) filterBy(key string, value interface{}, multiple bool) *base if f, ok := state.Filter[key]; ok { if s, ok := f.(string); ok && s != "" { if v, ok := value.(string); ok { - state.Filter[key] = fmt.Sprintf("%s %s", s, v) + state.Filter[key] = fmt.Sprintf("%s%%20%s", s, v) return } } diff --git a/vendor/github.com/sacloud/libsacloud/api/client.go b/vendor/github.com/sacloud/libsacloud/api/client.go index 2bf63020..70d79709 100644 --- a/vendor/github.com/sacloud/libsacloud/api/client.go +++ b/vendor/github.com/sacloud/libsacloud/api/client.go @@ -45,6 +45,8 @@ type Client struct { RetryMax int // 503エラー時のリトライ待ち時間 RetryInterval time.Duration + // APIコール時に利用される*http.Client 未指定の場合http.DefaultClientが利用される + HTTPClient *http.Client } // NewClient APIクライアント作成 @@ -112,6 +114,7 @@ func (c *Client) isOkStatus(code int) bool { func (c *Client) newRequest(method, uri string, body interface{}) ([]byte, error) { var ( client = &retryableHTTPClient{ + Client: c.HTTPClient, retryMax: c.RetryMax, retryInterval: c.RetryInterval, } @@ -233,12 +236,15 @@ func newRequest(method, url string, body io.ReadSeeker) (*request, error) { } type retryableHTTPClient struct { - http.Client + *http.Client retryInterval time.Duration retryMax int } func (c *retryableHTTPClient) Do(req *request) (*http.Response, error) { + if c.Client == nil { + c.Client = http.DefaultClient + } for i := 0; ; i++ { if req.body != nil { @@ -278,6 +284,7 @@ type API struct { Bill *BillAPI // 請求情報API Bridge *BridgeAPI // ブリッジAPi CDROM *CDROMAPI // ISOイメージAPI + Coupon *CouponAPI // クーポンAPI Database *DatabaseAPI // データベースAPI Disk *DiskAPI // ディスクAPI DNS *DNSAPI // DNS API @@ -296,6 +303,7 @@ type API struct { NFS *NFSAPI // NFS API Note *NoteAPI // スタートアップスクリプトAPI PacketFilter *PacketFilterAPI // パケットフィルタAPI + ProxyLB *ProxyLBAPI // プロキシLBAPI PrivateHost *PrivateHostAPI // 専有ホストAPI Product *ProductAPI // 製品情報API Server *ServerAPI // サーバーAPI @@ -338,6 +346,11 @@ func (api *API) GetCDROMAPI() *CDROMAPI { return api.CDROM } +// GetCouponAPI クーポン情報API取得 +func (api *API) GetCouponAPI() *CouponAPI { + return api.Coupon +} + // GetDatabaseAPI データベースAPI取得 func (api *API) GetDatabaseAPI() *DatabaseAPI { return api.Database @@ -433,6 +446,11 @@ func (api *API) GetPacketFilterAPI() *PacketFilterAPI { return api.PacketFilter } +// GetProxyLBAPI プロキシLBAPI取得 +func (api *API) GetProxyLBAPI() *ProxyLBAPI { + return api.ProxyLB +} + // GetPrivateHostAPI 専有ホストAPI取得 func (api *API) GetPrivateHostAPI() *PrivateHostAPI { return api.PrivateHost @@ -567,6 +585,7 @@ func newAPI(client *Client) *API { Bill: NewBillAPI(client), Bridge: NewBridgeAPI(client), CDROM: NewCDROMAPI(client), + Coupon: NewCouponAPI(client), Database: NewDatabaseAPI(client), Disk: NewDiskAPI(client), DNS: NewDNSAPI(client), @@ -588,6 +607,7 @@ func newAPI(client *Client) *API { NFS: NewNFSAPI(client), Note: NewNoteAPI(client), PacketFilter: NewPacketFilterAPI(client), + ProxyLB: NewProxyLBAPI(client), PrivateHost: NewPrivateHostAPI(client), Product: &ProductAPI{ Server: NewProductServerAPI(client), diff --git a/vendor/github.com/sacloud/libsacloud/api/coupon.go b/vendor/github.com/sacloud/libsacloud/api/coupon.go new file mode 100644 index 00000000..6dcfb84f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/coupon.go @@ -0,0 +1,59 @@ +package api + +import ( + "encoding/json" + "fmt" + + "github.com/sacloud/libsacloud/sacloud" +) + +// CouponAPI クーポン情報API +type CouponAPI struct { + *baseAPI +} + +// NewCouponAPI クーポン情報API作成 +func NewCouponAPI(client *Client) *CouponAPI { + return &CouponAPI{ + &baseAPI{ + client: client, + apiRootSuffix: sakuraBillingAPIRootSuffix, + FuncGetResourceURL: func() string { + return "coupon" + }, + }, + } +} + +// CouponResponse クーポン情報レスポンス +type CouponResponse struct { + *sacloud.ResultFlagValue + // AllCount 件数 + AllCount int `json:",omitempty"` + // CountPerPage ページあたり件数 + CountPerPage int `json:",omitempty"` + // Page 現在のページ番号 + Page int `json:",omitempty"` + // Coupons クーポン情報 リスト + Coupons []*sacloud.Coupon +} + +// Find クーポン情報 全件取得 +func (api *CouponAPI) Find() ([]*sacloud.Coupon, error) { + authStatus, err := api.client.AuthStatus.Read() + if err != nil { + return nil, err + } + accountID := authStatus.Account.GetStrID() + + uri := fmt.Sprintf("%s/%s", api.getResourceURL(), accountID) + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + var res CouponResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return res.Coupons, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/load_balancer.go b/vendor/github.com/sacloud/libsacloud/api/load_balancer.go index 14042bd8..bd942830 100644 --- a/vendor/github.com/sacloud/libsacloud/api/load_balancer.go +++ b/vendor/github.com/sacloud/libsacloud/api/load_balancer.go @@ -39,6 +39,12 @@ type loadBalancerResponse struct { Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} } +type loadBalancerStatusResponse struct { + *sacloud.ResultFlagValue + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} + LoadBalancer *sacloud.LoadBalancerStatusResult `json:",omitempty"` +} + // LoadBalancerAPI ロードバランサーAPI type LoadBalancerAPI struct { *baseAPI @@ -231,3 +237,20 @@ func (api *LoadBalancerAPI) AsyncSleepWhileCopying(id int64, timeout time.Durati func (api *LoadBalancerAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { return api.baseAPI.applianceMonitorBy(id, "interface", 0, body) } + +// Status ステータス取得 +func (api *LoadBalancerAPI) Status(id int64) (*sacloud.LoadBalancerStatusResult, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/status", api.getResourceURL(), id) + res = &loadBalancerStatusResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + if res.LoadBalancer == nil { + return nil, nil + } + return res.LoadBalancer, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go b/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go index 6a298427..1edc0a24 100644 --- a/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go +++ b/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go @@ -473,3 +473,8 @@ func (api *MobileGatewayAPI) GetTrafficStatus(id int64) (*sacloud.TrafficStatus, } return res.TrafficStatus, nil } + +// MonitorBy 指定位置のインターフェースのアクティビティーモニター取得 +func (api *MobileGatewayAPI) MonitorBy(id int64, nicIndex int, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "interface", nicIndex, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/nfs.go b/vendor/github.com/sacloud/libsacloud/api/nfs.go index b65fa5ad..a90da251 100644 --- a/vendor/github.com/sacloud/libsacloud/api/nfs.go +++ b/vendor/github.com/sacloud/libsacloud/api/nfs.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "errors" "fmt" "time" @@ -95,6 +96,58 @@ func (api *NFSAPI) Create(value *sacloud.NFS) (*sacloud.NFS, error) { }) } +// CreateWithPlan プラン/サイズを指定してNFSを作成 +func (api *NFSAPI) CreateWithPlan(value *sacloud.CreateNFSValue, plan sacloud.NFSPlan, size sacloud.NFSSize) (*sacloud.NFS, error) { + + nfs := sacloud.NewNFS(value) + // get plan + plans, err := api.GetNFSPlans() + if err != nil { + return nil, err + } + if plans == nil { + return nil, errors.New("NFS plans not found") + } + + planID := plans.FindPlanID(plan, size) + if planID < 0 { + return nil, errors.New("NFS plans not found") + } + + nfs.Plan = sacloud.NewResource(planID) + nfs.Remark.SetRemarkPlanID(planID) + + return api.request(func(res *nfsResponse) error { + return api.create(api.createRequest(nfs), res) + }) +} + +// GetNFSPlans プラン一覧取得 +func (api *NFSAPI) GetNFSPlans() (*sacloud.NFSPlans, error) { + notes, err := api.client.Note.Reset().Find() + if err != nil { + return nil, err + } + for _, note := range notes.Notes { + if note.Class == sacloud.ENoteClass("json") && note.Name == "sys-nfs" { + rawPlans := note.Content + + var plans struct { + Plans *sacloud.NFSPlans `json:"plans"` + } + + err := json.Unmarshal([]byte(rawPlans), &plans) + if err != nil { + return nil, err + } + + return plans.Plans, nil + } + } + + return nil, nil +} + // Read 読み取り func (api *NFSAPI) Read(id int64) (*sacloud.NFS, error) { return api.request(func(res *nfsResponse) error { @@ -224,9 +277,9 @@ func (api *NFSAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRe return poll(handler, timeout) } -// MonitorNFS NFS固有項目アクティビティモニター取得 -func (api *NFSAPI) MonitorNFS(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { - return api.baseAPI.applianceMonitorBy(id, "nfs", 0, body) +// MonitorFreeDiskSize NFSディスク残量アクティビティモニター取得 +func (api *NFSAPI) MonitorFreeDiskSize(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "database", 0, body) } // MonitorInterface NICアクティビティーモニター取得 diff --git a/vendor/github.com/sacloud/libsacloud/api/proxylb.go b/vendor/github.com/sacloud/libsacloud/api/proxylb.go new file mode 100644 index 00000000..9e45596f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/proxylb.go @@ -0,0 +1,224 @@ +package api + +import ( + "encoding/json" // "strings" + "fmt" + + "github.com/sacloud/libsacloud/sacloud" +) + +//HACK: さくらのAPI側仕様: CommonServiceItemsの内容によってJSONフォーマットが異なるため +// DNS/ProxyLB/シンプル監視それぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchProxyLBResponse ProxyLB検索レスポンス +type SearchProxyLBResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // CommonServiceProxyLBItems ProxyLBリスト + CommonServiceProxyLBItems []sacloud.ProxyLB `json:"CommonServiceItems,omitempty"` +} + +type proxyLBRequest struct { + CommonServiceProxyLBItem *sacloud.ProxyLB `json:"CommonServiceItem,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type proxyLBResponse struct { + *sacloud.ResultFlagValue + *sacloud.ProxyLB `json:"CommonServiceItem,omitempty"` +} + +// ProxyLBAPI ProxyLB API +type ProxyLBAPI struct { + *baseAPI +} + +// NewProxyLBAPI ProxyLB API作成 +func NewProxyLBAPI(client *Client) *ProxyLBAPI { + return &ProxyLBAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "commonserviceitem" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Provider.Class", "proxylb") + return res + }, + }, + } +} + +// Find 検索 +func (api *ProxyLBAPI) Find() (*SearchProxyLBResponse, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchProxyLBResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *ProxyLBAPI) request(f func(*proxyLBResponse) error) (*sacloud.ProxyLB, error) { + res := &proxyLBResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.ProxyLB, nil +} + +func (api *ProxyLBAPI) createRequest(value *sacloud.ProxyLB) *proxyLBResponse { + return &proxyLBResponse{ProxyLB: value} +} + +// New 新規作成用パラメーター作成 +func (api *ProxyLBAPI) New(name string) *sacloud.ProxyLB { + return sacloud.CreateNewProxyLB(name) +} + +// Create 新規作成 +func (api *ProxyLBAPI) Create(value *sacloud.ProxyLB) (*sacloud.ProxyLB, error) { + return api.request(func(res *proxyLBResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *ProxyLBAPI) Read(id int64) (*sacloud.ProxyLB, error) { + return api.request(func(res *proxyLBResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *ProxyLBAPI) Update(id int64, value *sacloud.ProxyLB) (*sacloud.ProxyLB, error) { + return api.request(func(res *proxyLBResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// UpdateSetting 設定更新 +func (api *ProxyLBAPI) UpdateSetting(id int64, value *sacloud.ProxyLB) (*sacloud.ProxyLB, error) { + req := &sacloud.ProxyLB{ + // Settings + Settings: value.Settings, + } + return api.request(func(res *proxyLBResponse) error { + return api.update(id, api.createRequest(req), res) + }) +} + +// Delete 削除 +func (api *ProxyLBAPI) Delete(id int64) (*sacloud.ProxyLB, error) { + return api.request(func(res *proxyLBResponse) error { + return api.delete(id, nil, res) + }) +} + +// ChangePlan プラン変更 +func (api *ProxyLBAPI) ChangePlan(id int64, newPlan sacloud.ProxyLBPlan) (*sacloud.ProxyLB, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/plan", api.getResourceURL(), id) + ) + body := &sacloud.ProxyLB{} + body.SetPlan(newPlan) + realBody := map[string]interface{}{ + "CommonServiceItem": map[string]interface{}{ + "ServiceClass": body.ServiceClass, + }, + } + + return api.request(func(res *proxyLBResponse) error { + return api.baseAPI.request(method, uri, realBody, res) + }) +} + +type proxyLBCertificateResponse struct { + *sacloud.ResultFlagValue + ProxyLB *sacloud.ProxyLBCertificates `json:",omitempty"` +} + +// GetCertificates 証明書取得 +func (api *ProxyLBAPI) GetCertificates(id int64) (*sacloud.ProxyLBCertificates, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/proxylb/sslcertificate", api.getResourceURL(), id) + res = &proxyLBCertificateResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + if res.ProxyLB == nil { + return nil, nil + } + return res.ProxyLB, nil +} + +// SetCertificates 証明書設定 +func (api *ProxyLBAPI) SetCertificates(id int64, certs *sacloud.ProxyLBCertificates) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/proxylb/sslcertificate", api.getResourceURL(), id) + res = &proxyLBCertificateResponse{} + ) + err := api.baseAPI.request(method, uri, map[string]interface{}{ + "ProxyLB": certs, + }, res) + if err != nil { + return false, err + } + return true, nil +} + +// DeleteCertificates 証明書削除 +func (api *ProxyLBAPI) DeleteCertificates(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/proxylb/sslcertificate", api.getResourceURL(), id) + ) + return api.baseAPI.modify(method, uri, nil) +} + +type proxyLBHealthResponse struct { + *sacloud.ResultFlagValue + ProxyLB *sacloud.ProxyLBStatus `json:",omitempty"` +} + +// Health ヘルスチェックステータス取得 +func (api *ProxyLBAPI) Health(id int64) (*sacloud.ProxyLBStatus, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/health", api.getResourceURL(), id) + res = &proxyLBHealthResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + if res.ProxyLB == nil { + return nil, nil + } + return res.ProxyLB, nil +} + +// Monitor アクティビティーモニター取得 +func (api *ProxyLBAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "activity/proxylb", 0, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/proxylb_gen.go b/vendor/github.com/sacloud/libsacloud/api/proxylb_gen.go new file mode 100644 index 00000000..daa413d8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/proxylb_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [ProxyLBAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ProxyLBAPI) Reset() *ProxyLBAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ProxyLBAPI) Offset(offset int) *ProxyLBAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ProxyLBAPI) Limit(limit int) *ProxyLBAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ProxyLBAPI) Include(key string) *ProxyLBAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ProxyLBAPI) Exclude(key string) *ProxyLBAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ProxyLBAPI) FilterBy(key string, value interface{}) *ProxyLBAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProxyLBAPI) FilterMultiBy(key string, value interface{}) *ProxyLBAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ProxyLBAPI) WithNameLike(name string) *ProxyLBAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ProxyLBAPI) WithTag(tag string) *ProxyLBAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ProxyLBAPI) WithTags(tags []string) *ProxyLBAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProxyLBAPI) WithSizeGib(size int) *ProxyLBAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ProxyLBAPI) WithSharedScope() *ProxyLBAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ProxyLBAPI) WithUserScope() *ProxyLBAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ProxyLBAPI) SortBy(key string, reverse bool) *ProxyLBAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ProxyLBAPI) SortByName(reverse bool) *ProxyLBAPI { + api.sortByName(reverse) + return api +} + +// func (api *ProxyLBAPI) SortBySize(reverse bool) *ProxyLBAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ProxyLBAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ProxyLBAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ProxyLBAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ProxyLBAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ProxyLBAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ProxyLBAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProxyLBAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ProxyLBAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ProxyLBAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ProxyLBAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProxyLBAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ProxyLBAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ProxyLBAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ProxyLBAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ProxyLBAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ProxyLBAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *ProxyLBAPI) New() *sacloud.ProxyLB { +// return &sacloud.ProxyLB{} +// } + +// func (api *ProxyLBAPI) Create(value *sacloud.ProxyLB) (*sacloud.ProxyLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *ProxyLBAPI) Read(id string) (*sacloud.ProxyLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *ProxyLBAPI) Update(id string, value *sacloud.ProxyLB) (*sacloud.ProxyLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ProxyLBAPI) Delete(id string) (*sacloud.ProxyLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ProxyLBAPI) setStateValue(setFunc func(*sacloud.Request)) *ProxyLBAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *ProxyLBAPI) request(f func(*sacloud.Response) error) (*sacloud.ProxyLB, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.ProxyLB, nil +//} +// +//func (api *ProxyLBAPI) createRequest(value *sacloud.ProxyLB) *sacloud.Request { +// req := &sacloud.Request{} +// req.ProxyLB = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/sim.go b/vendor/github.com/sacloud/libsacloud/api/sim.go index e7be3d71..ec289691 100644 --- a/vendor/github.com/sacloud/libsacloud/api/sim.go +++ b/vendor/github.com/sacloud/libsacloud/api/sim.go @@ -204,6 +204,37 @@ func (api *SIMAPI) Logs(id int64, body interface{}) ([]sacloud.SIMLog, error) { return res.Logs, nil } +// GetNetworkOperator 通信キャリア 取得 +func (api *SIMAPI) GetNetworkOperator(id int64) (*sacloud.SIMNetworkOperatorConfigs, error) { + + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/sim/network_operator_config", api.getResourceURL(), id) + ) + + res := &sacloud.SIMNetworkOperatorConfigs{} + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res, nil +} + +// SetNetworkOperator 通信キャリア 設定 +func (api *SIMAPI) SetNetworkOperator(id int64, opConfig ...*sacloud.SIMNetworkOperatorConfig) (bool, error) { + + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/sim/network_operator_config", api.getResourceURL(), id) + ) + + err := api.baseAPI.request(method, uri, &sacloud.SIMNetworkOperatorConfigs{NetworkOperatorConfigs: opConfig}, nil) + if err != nil { + return false, err + } + return true, nil +} + // Monitor アクティビティーモニター(Up/Down link BPS)取得 func (api *SIMAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { var ( diff --git a/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go b/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go index 881cdfdd..bd72feb3 100644 --- a/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go +++ b/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go @@ -118,6 +118,25 @@ func (api *SimpleMonitorAPI) Delete(id int64) (*sacloud.SimpleMonitor, error) { }) } +// Health ヘルスチェック +// +// まだチェックが行われていない場合nilを返す +func (api *SimpleMonitorAPI) Health(id int64) (*sacloud.SimpleMonitorHealthCheckStatus, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/health", api.getResourceURL(), id) + ) + res := struct { + SimpleMonitor *sacloud.SimpleMonitorHealthCheckStatus `json:",omitempty"` + }{} + + err := api.baseAPI.request(method, uri, nil, &res) + if err != nil { + return nil, err + } + return res.SimpleMonitor, nil +} + // MonitorResponseTimeSec アクティビティーモニター(レスポンスタイム)取得 func (api *SimpleMonitorAPI) MonitorResponseTimeSec(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { var ( diff --git a/vendor/github.com/sacloud/libsacloud/libsacloud.go b/vendor/github.com/sacloud/libsacloud/libsacloud.go index 8403b2a3..bb92c752 100644 --- a/vendor/github.com/sacloud/libsacloud/libsacloud.go +++ b/vendor/github.com/sacloud/libsacloud/libsacloud.go @@ -2,4 +2,4 @@ package libsacloud // Version バージョン -const Version = "1.0.0" +const Version = "1.19.0" diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go b/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go index 7b709f43..042b861d 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go @@ -138,6 +138,36 @@ var ( // EDiskConnection ディスク接続方法 type EDiskConnection string +// EUpstreamNetworkType 上流ネットワーク種別 +type EUpstreamNetworkType string + +// String EUpstreamNetworkTypeの文字列表現 +func (t EUpstreamNetworkType) String() string { + return string(t) +} + +var ( + // EUpstreamNetworkUnknown 不明 + EUpstreamNetworkUnknown = EUpstreamNetworkType("unknown") + // EUpstreamNetworkShared 共有セグメント + EUpstreamNetworkShared = EUpstreamNetworkType("shared") + // EUpstreamNetworkSwitch スイッチ(非スイッチ+ルータ) + EUpstreamNetworkSwitch = EUpstreamNetworkType("switch") + // EUpstreamNetworkRouter ルータ(スイッチ+ルータのスイッチ) + EUpstreamNetworkRouter = EUpstreamNetworkType("router") + // EUpstreamNetworkNone 接続なし + EUpstreamNetworkNone = EUpstreamNetworkType("none") + + // UpstreamNetworks 文字列とEUpstreamNetworkTypeのマッピング + UpstreamNetworks = map[string]EUpstreamNetworkType{ + "unknown": EUpstreamNetworkUnknown, + "shared": EUpstreamNetworkShared, + "switch": EUpstreamNetworkSwitch, + "router": EUpstreamNetworkRouter, + "none": EUpstreamNetworkNone, + } +) + // SakuraCloudResources さくらのクラウド上のリソース種別一覧 type SakuraCloudResources struct { Server *Server `json:",omitempty"` // サーバー diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/coupon.go b/vendor/github.com/sacloud/libsacloud/sacloud/coupon.go new file mode 100644 index 00000000..90b6f738 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/coupon.go @@ -0,0 +1,14 @@ +package sacloud + +import "time" + +// Coupon クーポン情報 +type Coupon struct { + CouponID string `json:",omitempty"` // クーポンID + MemberID string `json:",omitempty"` // メンバーID + ContractID int64 `json:",omitempty"` // 契約ID + ServiceClassID int64 `json:",omitempty"` // サービスクラスID + Discount int64 `json:",omitempty"` // クーポン残高 + AppliedAt time.Time `json:",omitempty"` // 適用開始日 + UntilAt time.Time `json:",omitempty"` // 有効期限 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/database.go b/vendor/github.com/sacloud/libsacloud/sacloud/database.go index 0f989f2f..4d1b3ad6 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/database.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/database.go @@ -181,7 +181,9 @@ type DatabaseReplicationSetting struct { // Model レプリケーションモデル Model DatabaseReplicationModels `json:",omitempty"` // Appliance マスター側アプライアンス - Appliance *Resource `json:",omitempty"` + Appliance *struct { + ID string + } `json:",omitempty"` // IPAddress IPアドレス IPAddress string `json:",omitempty"` // Port ポート @@ -456,7 +458,7 @@ func NewSlaveDatabaseValue(values *SlaveDatabaseValue) *Database { // Replication Replication: &DatabaseReplicationSetting{ Model: DatabaseReplicationModelAsyncReplica, - Appliance: NewResource(values.MasterApplianceID), + Appliance: &struct{ ID string }{ID: fmt.Sprintf("%d", values.MasterApplianceID)}, IPAddress: values.MasterIPAddress, Port: values.MasterPort, User: "replica", @@ -504,5 +506,68 @@ func (s *Database) DeleteSourceNetwork(nw string) { // IsReplicationMaster レプリケーションが有効かつマスターとして構成されているか func (s *Database) IsReplicationMaster() bool { - return s.Settings.DBConf.Replication != nil && s.Settings.DBConf.Replication.Model == DatabaseReplicationModelMasterSlave + return s.IsReplicationEnabled() && s.Settings.DBConf.Replication.Model == DatabaseReplicationModelMasterSlave +} + +// IsReplicationEnabled レプリケーションが有効な場合はTrueを返す +func (s *Database) IsReplicationEnabled() bool { + return s.Settings.DBConf.Replication != nil +} + +// DatabaseName MariaDB or PostgreSQLの何れかを返す +func (s *Database) DatabaseName() string { + return s.Remark.DBConf.Common.DatabaseName +} + +// DatabaseRevision データベースのリビジョンを返す +// +// 例: MariaDBの場合 => 10.2.15 / PostgreSQLの場合 => 10.3 +func (s *Database) DatabaseRevision() string { + return s.Remark.DBConf.Common.DatabaseRevision +} + +// DatabaseVersion データベースのバージョンを返す +// +// 例: MariaDBの場合 => 10.2 / PostgreSQLの場合 => 10 +func (s *Database) DatabaseVersion() string { + return s.Remark.DBConf.Common.DatabaseVersion +} + +// WebUIAddress WebUIが有効な場合、IPアドレス or FQDNを返す、無効な場合は空文字を返す +func (s *Database) WebUIAddress() string { + webUI := s.Settings.DBConf.Common.WebUI + if webUI != nil { + if v, ok := webUI.(string); ok { + return v + } + } + return "" +} + +// IPAddress IPアドレスを取得 +func (s *Database) IPAddress() string { + if len(s.Remark.Servers) < 1 { + return "" + } + v, ok := s.Remark.Servers[0].(map[string]string) + if !ok { + return "" + } + return v["IPAddress"] +} + +// NetworkMaskLen ネットワークマスク長を取得 +func (s *Database) NetworkMaskLen() int { + if s.Remark.Network == nil { + return -1 + } + return s.Remark.Network.NetworkMaskLen +} + +// DefaultRoute デフォルトゲートウェイアドレスを取得 +func (s *Database) DefaultRoute() string { + if s.Remark.Network == nil { + return "" + } + return s.Remark.Network.DefaultRoute } diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/interface.go b/vendor/github.com/sacloud/libsacloud/sacloud/interface.go index 557a3444..8260686b 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/interface.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/interface.go @@ -41,3 +41,21 @@ func (i *Interface) GetHostName() string { func (i *Interface) GetPacketFilter() *PacketFilter { return i.PacketFilter } + +// UpstreamType 上流ネットワーク種別 +func (i *Interface) UpstreamType() EUpstreamNetworkType { + sw := i.Switch + if sw == nil { + return EUpstreamNetworkNone + } + + if sw.Subnet == nil { + return EUpstreamNetworkSwitch + } + + if sw.Scope == ESCopeShared { + return EUpstreamNetworkShared + } + + return EUpstreamNetworkRouter +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go b/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go index 08b4716d..71cd1dcb 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go @@ -1,5 +1,7 @@ package sacloud +import "strconv" + // LoadBalancer ロードバランサー type LoadBalancer struct { *Appliance // アプライアンス共通属性 @@ -8,6 +10,43 @@ type LoadBalancer struct { Settings *LoadBalancerSettings `json:",omitempty"` // ロードバランサー設定 } +// IsHA 冗長化されている場合にtrueを返す +func (l *LoadBalancer) IsHA() bool { + isHA := false + if len(l.Remark.Servers) > 1 { + if v, ok := l.Remark.Servers[1].(map[string]string); ok { + if _, ok := v["IPAddress"]; ok { + isHA = true + } + } + } + return isHA +} + +// IPAddress1 ロードバランサ本体のIPアドレス(1番目)を返す +func (l *LoadBalancer) IPAddress1() string { + if len(l.Remark.Servers) > 0 { + if v, ok := l.Remark.Servers[0].(map[string]string); ok { + if v, ok := v["IPAddress"]; ok { + return v + } + } + } + return "" +} + +// IPAddress2 ロードバランサ本体のIPアドレス(2番目)を返す +func (l *LoadBalancer) IPAddress2() string { + if len(l.Remark.Servers) > 1 { + if v, ok := l.Remark.Servers[1].(map[string]string); ok { + if v, ok := v["IPAddress"]; ok { + return v + } + } + } + return "" +} + // LoadBalancerRemark リマーク type LoadBalancerRemark struct { *ApplianceRemarkBase @@ -17,7 +56,7 @@ type LoadBalancerRemark struct { // LoadBalancerSettings ロードバランサー設定リスト type LoadBalancerSettings struct { - LoadBalancer []*LoadBalancerSetting `json:",omitempty"` // ロードバランサー設定リスト + LoadBalancer []*LoadBalancerSetting // ロードバランサー設定リスト } // LoadBalancerSetting ロードバランサー仮想IP設定 @@ -180,3 +219,73 @@ func (s *LoadBalancerSetting) DeleteServer(ip string, port string) { s.Servers = res } + +// LoadBalancerStatusResult ロードバランサーのステータスAPI戻り値 +type LoadBalancerStatusResult []*LoadBalancerStatus + +// Get VIPに対応するステータスを取得 +func (l *LoadBalancerStatusResult) Get(vip string) *LoadBalancerStatus { + for _, v := range *l { + if v.VirtualIPAddress == vip { + return v + } + } + return nil +} + +// LoadBalancerStatus ロードバランサーのステータス +type LoadBalancerStatus struct { + VirtualIPAddress string + Port string + Servers []*LoadBalancerServerStatus `json:",omitempty"` + CPS string +} + +// Get IPアドレスに対応する実サーバのステータスを取得 +func (l *LoadBalancerStatus) Get(ip string) *LoadBalancerServerStatus { + for _, v := range l.Servers { + if v.IPAddress == ip { + return v + } + } + return nil +} + +// NumCPS CPSを数値にして返す +func (l *LoadBalancerStatus) NumCPS() int { + v, _ := strconv.Atoi(l.CPS) // nolint - ignore error + return v +} + +// NumPort Portを数値にして返す +func (l *LoadBalancerStatus) NumPort() int { + v, _ := strconv.Atoi(l.Port) // nolint - ignore error + return v +} + +// LoadBalancerServerStatus ロードバランサーのVIP配下の実サーバのステータス +type LoadBalancerServerStatus struct { + ActiveConn string + IPAddress string + Status string + Port string + CPS string +} + +// NumActiveConn ActiveConnを数値にして返す +func (l *LoadBalancerServerStatus) NumActiveConn() int { + v, _ := strconv.Atoi(l.ActiveConn) // nolint - ignore error + return v +} + +// NumCPS CPSを数値にして返す +func (l *LoadBalancerServerStatus) NumCPS() int { + v, _ := strconv.Atoi(l.CPS) // nolint - ignore error + return v +} + +// NumPort Portを数値にして返す +func (l *LoadBalancerServerStatus) NumPort() int { + v, _ := strconv.Atoi(l.Port) // nolint - ignore error + return v +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/lock.go b/vendor/github.com/sacloud/libsacloud/sacloud/lock.go new file mode 100644 index 00000000..f8e66c9b --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/lock.go @@ -0,0 +1,29 @@ +package sacloud + +import ( + "fmt" + + "github.com/sacloud/libsacloud/utils/mutexkv" +) + +var resourceMu = mutexkv.NewMutexKV() + +// LockByKey 任意のキーでのMutexロック +func LockByKey(key string) { + resourceMu.Lock(key) +} + +// UnlockByKey 任意のキーでのMutexアンロック +func UnlockByKey(key string) { + resourceMu.Unlock(key) +} + +// LockByResourceID リソース単位でのMutexロック +func LockByResourceID(resourceID int64) { + resourceMu.Lock(fmt.Sprintf("%d", resourceID)) +} + +// UnlockByResourceID リソース単位でのMutexアンロック +func UnlockByResourceID(resourceID int64) { + resourceMu.Unlock(fmt.Sprintf("%d", resourceID)) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go b/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go index b3b8ccbf..a6e6dc4e 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go @@ -29,9 +29,10 @@ type MobileGatewaySettings struct { // MobileGatewaySetting モバイルゲートウェイ設定 type MobileGatewaySetting struct { - InternetConnection *MGWInternetConnection `json:",omitempty"` // インターネット接続 - Interfaces []*MGWInterface `json:",omitempty"` // インターフェース - StaticRoutes []*MGWStaticRoute `json:",omitempty"` // スタティックルート + InternetConnection *MGWInternetConnection `json:",omitempty"` // インターネット接続 + InterDeviceCommunication *MGWInterDeviceCommunication `json:",omitempty"` // デバイス間通信 + Interfaces []*MGWInterface `json:",omitempty"` // インターフェース + StaticRoutes []*MGWStaticRoute `json:",omitempty"` // スタティックルート } // HasStaticRoutes スタティックルートを保持しているか @@ -90,6 +91,11 @@ func (m *MobileGatewaySetting) FindStaticRoute(prefix string, nextHop string) (i return -1, nil } +// MGWInterDeviceCommunication デバイス間通信 +type MGWInterDeviceCommunication struct { + Enabled string `json:",omitempty"` +} + // MGWInternetConnection インターネット接続 type MGWInternetConnection struct { Enabled string `json:",omitempty"` @@ -188,6 +194,58 @@ func (m *MobileGateway) HasStaticRoutes() bool { return m.HasSetting() && m.Settings.MobileGateway.HasStaticRoutes() } +// InternetConnection インターネット接続が有効な場合にTrueを返す +func (m *MobileGateway) InternetConnection() bool { + return m.HasSetting() && + m.Settings.MobileGateway.InternetConnection != nil && + m.Settings.MobileGateway.InternetConnection.Enabled == "True" +} + +// InterDeviceCommunication デバイス間通信が有効な場合にTrueを返す +func (m *MobileGateway) InterDeviceCommunication() bool { + return m.HasSetting() && + m.Settings.MobileGateway.InterDeviceCommunication != nil && + m.Settings.MobileGateway.InterDeviceCommunication.Enabled == "True" +} + +// IPAddress 0番目のNICのIPアドレスを取得 +func (m *MobileGateway) IPAddress() string { + return m.IPAddressAt(0) +} + +// IPAddressAt IPアドレスを取得 +func (m *MobileGateway) IPAddressAt(index int) string { + if len(m.Interfaces) <= index { + return "" + } + if index == 0 { + return m.Interfaces[0].IPAddress + } + + ipaddresses := m.Settings.MobileGateway.Interfaces[index].IPAddress + if len(ipaddresses) < 1 { + return "" + } + return ipaddresses[0] +} + +// NetworkMaskLen 0番目のNICのネットワークマスク長を取得 +func (m *MobileGateway) NetworkMaskLen() int { + return m.NetworkMaskLenAt(0) +} + +// NetworkMaskLenAt ネットワークマスク長を取得 +func (m *MobileGateway) NetworkMaskLenAt(index int) int { + if len(m.Interfaces) <= index { + return -1 + } + if index == 0 { + return m.Interfaces[0].Switch.UserSubnet.NetworkMaskLen + } + + return m.Settings.MobileGateway.Interfaces[0].NetworkMaskLen +} + // NewMobileGatewayResolver DNS登録用パラメータ作成 func NewMobileGatewayResolver(dns1, dns2 string) *MobileGatewayResolver { return &MobileGatewayResolver{ diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go b/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go index f4ecc061..1cfa2534 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go @@ -9,23 +9,27 @@ import ( // MonitorValue アクティビティモニター type MonitorValue struct { - CPUTime *float64 `json:"CPU-TIME,omitempty"` // CPU時間 - Write *float64 `json:",omitempty"` // ディスク書き込み - Read *float64 `json:",omitempty"` // ディスク読み取り - Receive *float64 `json:",omitempty"` // パケット受信 - Send *float64 `json:",omitempty"` // パケット送信 - In *float64 `json:",omitempty"` // パケット受信 - Out *float64 `json:",omitempty"` // パケット送信 - TotalMemorySize *float64 `json:"Total-Memory-Size,omitempty"` // 総メモリサイズ - UsedMemorySize *float64 `json:"Used-Memory-Size,omitempty"` // 使用済みメモリサイズ - TotalDisk1Size *float64 `json:"Total-Disk1-Size,omitempty"` // 総ディスクサイズ - UsedDisk1Size *float64 `json:"Used-Disk1-Size,omitempty"` // 使用済みディスクサイズ - TotalDisk2Size *float64 `json:"Total-Disk2-Size,omitempty"` // 総ディスクサイズ - UsedDisk2Size *float64 `json:"Used-Disk2-Size,omitempty"` // 使用済みディスクサイズ - FreeDiskSize *float64 `json:"Free-Disk-Size,omitempty"` // 空きディスクサイズ(NFS) - ResponseTimeSec *float64 `json:"responsetimesec,omitempty"` // レスポンスタイム(シンプル監視) - UplinkBPS *float64 `json:"UplinkBps,omitempty"` // 上り方向トラフィック - DownlinkBPS *float64 `json:"DownlinkBps"` // 下り方向トラフィック + CPUTime *float64 `json:"CPU-TIME,omitempty"` // CPU時間 + Write *float64 `json:",omitempty"` // ディスク書き込み + Read *float64 `json:",omitempty"` // ディスク読み取り + Receive *float64 `json:",omitempty"` // パケット受信 + Send *float64 `json:",omitempty"` // パケット送信 + In *float64 `json:",omitempty"` // パケット受信 + Out *float64 `json:",omitempty"` // パケット送信 + TotalMemorySize *float64 `json:"Total-Memory-Size,omitempty"` // 総メモリサイズ + UsedMemorySize *float64 `json:"Used-Memory-Size,omitempty"` // 使用済みメモリサイズ + TotalDisk1Size *float64 `json:"Total-Disk1-Size,omitempty"` // 総ディスクサイズ + UsedDisk1Size *float64 `json:"Used-Disk1-Size,omitempty"` // 使用済みディスクサイズ + TotalDisk2Size *float64 `json:"Total-Disk2-Size,omitempty"` // 総ディスクサイズ + UsedDisk2Size *float64 `json:"Used-Disk2-Size,omitempty"` // 使用済みディスクサイズ + BinlogUsedSizeKiB *float64 `json:"binlogUsedSizeKiB,omitempty"` // バイナリログのサイズ(レプリケーション有効時のみ、master/slave両方で利用可能) + DelayTimeSec *float64 `json:"delayTimeSec,omitempty"` // レプリケーション遅延時間(レプリケーション有効時のみ、slave側のみ) + FreeDiskSize *float64 `json:"Free-Disk-Size,omitempty"` // 空きディスクサイズ(NFS) + ResponseTimeSec *float64 `json:"responsetimesec,omitempty"` // レスポンスタイム(シンプル監視) + UplinkBPS *float64 `json:"UplinkBps,omitempty"` // 上り方向トラフィック + DownlinkBPS *float64 `json:"DownlinkBps,omitempty"` // 下り方向トラフィック + ActiveConnections *float64 `json:"activeConnections,omitempty"` // アクティブコネクション(プロキシLB) + ConnectionsPerSec *float64 `json:"connectionsPerSec,omitempty"` // 秒間コネクション数 } // UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) @@ -36,23 +40,27 @@ func (m *MonitorValue) UnmarshalJSON(data []byte) error { } tmp := &struct { - CPUTime *float64 `json:"CPU-TIME,omitempty"` - Write *float64 `json:",omitempty"` - Read *float64 `json:",omitempty"` - Receive *float64 `json:",omitempty"` - Send *float64 `json:",omitempty"` - In *float64 `json:",omitempty"` - Out *float64 `json:",omitempty"` - TotalMemorySize *float64 `json:"Total-Memory-Size,omitempty"` - UsedMemorySize *float64 `json:"Used-Memory-Size,omitempty"` - TotalDisk1Size *float64 `json:"Total-Disk1-Size,omitempty"` - UsedDisk1Size *float64 `json:"Used-Disk1-Size,omitempty"` - TotalDisk2Size *float64 `json:"Total-Disk2-Size,omitempty"` - UsedDisk2Size *float64 `json:"Used-Disk2-Size,omitempty"` - FreeDiskSize *float64 `json:"Free-Disk-Size,omitempty"` - ResponseTimeSec *float64 `json:"responsetimesec,omitempty"` - UplinkBPS *float64 `json:"UplinkBps,omitempty"` - DownlinkBPS *float64 `json:"DownlinkBps"` + CPUTime *float64 `json:"CPU-TIME,omitempty"` + Write *float64 `json:",omitempty"` + Read *float64 `json:",omitempty"` + Receive *float64 `json:",omitempty"` + Send *float64 `json:",omitempty"` + In *float64 `json:",omitempty"` + Out *float64 `json:",omitempty"` + TotalMemorySize *float64 `json:"Total-Memory-Size,omitempty"` + UsedMemorySize *float64 `json:"Used-Memory-Size,omitempty"` + TotalDisk1Size *float64 `json:"Total-Disk1-Size,omitempty"` + UsedDisk1Size *float64 `json:"Used-Disk1-Size,omitempty"` + TotalDisk2Size *float64 `json:"Total-Disk2-Size,omitempty"` + UsedDisk2Size *float64 `json:"Used-Disk2-Size,omitempty"` + BinlogUsedSizeKiB *float64 `json:"binlogUsedSizeKiB,omitempty"` + DelayTimeSec *float64 `json:"delayTimeSec,omitempty"` + FreeDiskSize *float64 `json:"Free-Disk-Size,omitempty"` + ResponseTimeSec *float64 `json:"responsetimesec,omitempty"` + UplinkBPS *float64 `json:"UplinkBps,omitempty"` + DownlinkBPS *float64 `json:"DownlinkBps,omitempty"` + ActiveConnections *float64 `json:"activeConnections,omitempty"` + ConnectionsPerSec *float64 `json:"connectionsPerSec,omitempty"` }{} if err := json.Unmarshal(data, &tmp); err != nil { return err @@ -71,10 +79,14 @@ func (m *MonitorValue) UnmarshalJSON(data []byte) error { m.UsedDisk1Size = tmp.UsedDisk1Size m.TotalDisk2Size = tmp.TotalDisk2Size m.UsedDisk2Size = tmp.UsedDisk2Size + m.BinlogUsedSizeKiB = tmp.BinlogUsedSizeKiB + m.DelayTimeSec = tmp.DelayTimeSec m.FreeDiskSize = tmp.FreeDiskSize m.ResponseTimeSec = tmp.ResponseTimeSec m.UplinkBPS = tmp.UplinkBPS m.DownlinkBPS = tmp.DownlinkBPS + m.ActiveConnections = tmp.ActiveConnections + m.ConnectionsPerSec = tmp.ConnectionsPerSec return nil } @@ -104,6 +116,23 @@ type ResourceMonitorResponse struct { Data *MonitorValues `json:",omitempty"` // メトリクス } +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (m *MonitorValues) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `[]` { + return nil + } + + tmp := map[string]*MonitorValue{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + value := MonitorValues(tmp) + *m = value + return nil +} + // MonitorSummaryData メトリクスサマリー type MonitorSummaryData struct { Max float64 // 最大値 @@ -242,6 +271,16 @@ func (m *MonitorValues) FlattenUsedDisk2SizeValue() ([]FlatMonitorValue, error) return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedDisk2Size }) } +// FlattenBinlogUsedSizeKiBValue フラット化 バイナリログサイズ +func (m *MonitorValues) FlattenBinlogUsedSizeKiBValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.BinlogUsedSizeKiB }) +} + +// FlattenDelayTimeSecValue フラット化 レプリケーション遅延時間 +func (m *MonitorValues) FlattenDelayTimeSecValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.DelayTimeSec }) +} + // FlattenFreeDiskSizeValue フラット化 空きディスクサイズ(NFS) func (m *MonitorValues) FlattenFreeDiskSizeValue() ([]FlatMonitorValue, error) { return m.flattenValue(func(v *MonitorValue) *float64 { return v.FreeDiskSize }) @@ -262,6 +301,16 @@ func (m *MonitorValues) FlattenDownlinkBPSValue() ([]FlatMonitorValue, error) { return m.flattenValue(func(v *MonitorValue) *float64 { return v.DownlinkBPS }) } +// FlattenActiveConnections フラット化 アクティブコネクション +func (m *MonitorValues) FlattenActiveConnections() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.ActiveConnections }) +} + +// FlattenConnectionsPerSec フラット化 秒間接続数 +func (m *MonitorValues) FlattenConnectionsPerSec() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.ConnectionsPerSec }) +} + func (m *MonitorValues) flattenValue(f func(*MonitorValue) *float64) ([]FlatMonitorValue, error) { var res []FlatMonitorValue @@ -293,8 +342,10 @@ func (m *MonitorValue) HasValue() bool { m.TotalMemorySize, m.UsedMemorySize, m.TotalDisk1Size, m.UsedDisk1Size, m.TotalDisk2Size, m.UsedDisk2Size, + m.BinlogUsedSizeKiB, m.DelayTimeSec, m.FreeDiskSize, m.ResponseTimeSec, m.UplinkBPS, m.DownlinkBPS, + m.ActiveConnections, m.ConnectionsPerSec, } for _, v := range values { if v != nil { diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go b/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go index 0f593531..ee897e09 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go @@ -1,5 +1,9 @@ package sacloud +import ( + "encoding/json" +) + // NFS NFS type NFS struct { *Appliance // アプライアンス共通属性 @@ -11,47 +15,97 @@ type NFS struct { // NFSRemark リマーク type NFSRemark struct { *ApplianceRemarkBase - propPlanID + Plan *struct { + ID json.Number `json:",omitempty"` + } `json:",omitempty"` // プラン // TODO Zone //Zone *Resource //SourceAppliance *Resource // クローン元DB } +// SetRemarkPlanID プランID設定 +func (n NFSRemark) SetRemarkPlanID(planID int64) { + if n.Plan == nil { + n.Plan = &struct { + ID json.Number `json:",omitempty"` + }{} + } + n.Plan.ID = json.Number(planID) +} + // NFSSettings NFS設定リスト type NFSSettings struct { } -// NFSPlan NFSプラン +// NFSPlan プラン(HDD/SSD) type NFSPlan int var ( - // NFSPlan100G 100Gプラン - NFSPlan100G = NFSPlan(100) - // NFSPlan500G 500Gプラン - NFSPlan500G = NFSPlan(500) - // NFSPlan1T 1T(1024GB)プラン - NFSPlan1T = NFSPlan(1024 * 1) - // NFSPlan2T 2T(2048GB)プラン - NFSPlan2T = NFSPlan(1024 * 2) - // NFSPlan4T 4T(4096GB)プラン - NFSPlan4T = NFSPlan(1024 * 4) + // NFSPlanHDD 標準プラン(HDD) + NFSPlanHDD = NFSPlan(1) + // NFSPlanSSD SSHプラン + NFSPlanSSD = NFSPlan(2) ) -// AllowNFSPlans 指定可能なNFSプラン -func AllowNFSPlans() []int { +// String NFSプランの文字列表現 +func (p NFSPlan) String() string { + switch p { + case NFSPlanHDD: + return "HDD" + case NFSPlanSSD: + return "SSD" + default: + return "" + } +} + +// NFSSize NFSサイズ +type NFSSize int + +var ( + // NFSSize100G 100Gプラン + NFSSize100G = NFSSize(100) + // NFSSize500G 500Gプラン + NFSSize500G = NFSSize(500) + // NFSSize1T 1T(1024GB)プラン + NFSSize1T = NFSSize(1024 * 1) + // NFSSize2T 2T(2048GB)プラン + NFSSize2T = NFSSize(1024 * 2) + // NFSSize4T 4T(4096GB)プラン + NFSSize4T = NFSSize(1024 * 4) + // NFSSize8T 8TBプラン + NFSSize8T = NFSSize(1024 * 8) + // NFSSize12T 12TBプラン + NFSSize12T = NFSSize(1024 * 12) +) + +// AllowNFSNormalPlanSizes 指定可能なNFSサイズ(標準プラン) +func AllowNFSNormalPlanSizes() []int { return []int{ - int(NFSPlan100G), - int(NFSPlan500G), - int(NFSPlan1T), - int(NFSPlan2T), - int(NFSPlan4T), + int(NFSSize100G), + int(NFSSize500G), + int(NFSSize1T), + int(NFSSize2T), + int(NFSSize4T), + int(NFSSize8T), + int(NFSSize12T), + } +} + +// AllowNFSSSDPlanSizes 指定可能なNFSサイズ(SSDプラン) +func AllowNFSSSDPlanSizes() []int { + return []int{ + int(NFSSize100G), + int(NFSSize500G), + int(NFSSize1T), + int(NFSSize2T), + int(NFSSize4T), } } // CreateNFSValue NFS作成用パラメーター type CreateNFSValue struct { SwitchID string // 接続先スイッチID - Plan NFSPlan // プラン IPAddress string // IPアドレス MaskLen int // ネットワークマスク長 DefaultRoute string // デフォルトルート @@ -62,27 +116,16 @@ type CreateNFSValue struct { SourceAppliance *Resource // クローン元NFS } -// NewCreateNFSValue NFS作成用パラメーター -func NewCreateNFSValue() *CreateNFSValue { - return &CreateNFSValue{ - Plan: NFSPlan100G, - } -} - // NewNFS NFS作成(冗長化なし) func NewNFS(values *CreateNFSValue) *NFS { - if int(values.Plan) == 0 { - values.Plan = NFSPlan100G - } - return &NFS{ Appliance: &Appliance{ Class: "nfs", propName: propName{Name: values.Name}, propDescription: propDescription{Description: values.Description}, propTags: propTags{Tags: values.Tags}, - propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + //propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, propIcon: propIcon{ &Icon{ Resource: values.Icon, @@ -102,9 +145,104 @@ func NewNFS(values *CreateNFSValue) *NFS { map[string]interface{}{"IPAddress": values.IPAddress}, }, }, - propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, - //SourceAppliance: values.SourceAppliance, + //propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, }, } } + +// IPAddress IPアドレスを取得 +func (n *NFS) IPAddress() string { + if len(n.Remark.Servers) < 1 { + return "" + } + + v, ok := n.Remark.Servers[0].(map[string]interface{}) + if !ok { + return "" + } + + if ip, ok := v["IPAddress"]; ok { + return ip.(string) + } + return "" +} + +// NetworkMaskLen ネットワークマスク長を取得 +func (n *NFS) NetworkMaskLen() int { + if n.Remark.Network == nil { + return -1 + } + return n.Remark.Network.NetworkMaskLen +} + +// DefaultRoute デフォルトゲートウェイを取得 +func (n *NFS) DefaultRoute() string { + if n.Remark.Network == nil { + return "" + } + return n.Remark.Network.DefaultRoute +} + +// NFSPlans NFSプラン +type NFSPlans struct { + HDD []NFSPlanValue + SSD []NFSPlanValue +} + +// FindPlanID プランとサイズからプランIDを取得 +func (p NFSPlans) FindPlanID(plan NFSPlan, size NFSSize) int64 { + var plans []NFSPlanValue + switch plan { + case NFSPlanHDD: + plans = p.HDD + case NFSPlanSSD: + plans = p.SSD + default: + return -1 + } + + for _, plan := range plans { + if plan.Availability == "available" && plan.Size == int(size) { + res, err := plan.PlanID.Int64() + if err != nil { + return -1 + } + return res + } + } + + return -1 +} + +// FindByPlanID プランIDから該当プランを取得 +func (p NFSPlans) FindByPlanID(planID int64) (NFSPlan, *NFSPlanValue) { + + for _, plan := range p.SSD { + id, err := plan.PlanID.Int64() + if err != nil { + continue + } + if id == planID { + return NFSPlanSSD, &plan + } + } + + for _, plan := range p.HDD { + id, err := plan.PlanID.Int64() + if err != nil { + continue + } + if id == planID { + return NFSPlanHDD, &plan + } + } + return NFSPlan(-1), nil +} + +// NFSPlanValue NFSプラン +type NFSPlanValue struct { + Size int `json:"size"` + Availability string `json:"availability"` + PlanID json.Number `json:"planId"` +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go index 51c63ce0..29d75485 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go @@ -53,6 +53,8 @@ const ( Windows2016SQLServerStandardAll // Windows2016SQLServer2017StandardAll OS種別:Windows Server 2016 SQLServer 2017(Standard) + RDS + Office Windows2016SQLServer2017StandardAll + // Windows2019 OS種別:Windows Server 2019 Datacenter Edition + Windows2019 // Custom OS種別:カスタム Custom ) @@ -66,6 +68,7 @@ var OSTypeShortNames = []string{ "windows2016", "windows2016-rds", "windows2016-rds-office", "windows2016-sql-web", "windows2016-sql-standard", "windows2016-sql-standard-all", "windows2016-sql2017-standard", "windows2016-sql2017-standard-all", + "windows2019", } // IsWindows Windowsか @@ -74,7 +77,8 @@ func (o ArchiveOSTypes) IsWindows() bool { case Windows2012, Windows2012RDS, Windows2012RDSOffice, Windows2016, Windows2016RDS, Windows2016RDSOffice, Windows2016SQLServerWeb, Windows2016SQLServerStandard, Windows2016SQLServerStandardAll, - Windows2016SQLServer2017Standard, Windows2016SQLServer2017StandardAll: + Windows2016SQLServer2017Standard, Windows2016SQLServer2017StandardAll, + Windows2019: return true default: return false @@ -140,6 +144,8 @@ func StrToOSType(osType string) ArchiveOSTypes { return Windows2016SQLServerStandardAll case "windows2016-sql2017-standard-all": return Windows2016SQLServer2017StandardAll + case "windows2019": + return Windows2019 default: return Custom } diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go index b59eca73..77d31e27 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go @@ -4,9 +4,9 @@ package ostype import "strconv" -const _ArchiveOSTypes_name = "CentOSCentOS6UbuntuDebianVyOSCoreOSRancherOSKusanagiSophosUTMFreeBSDNetwiserOPNsenseWindows2012Windows2012RDSWindows2012RDSOfficeWindows2016Windows2016RDSWindows2016RDSOfficeWindows2016SQLServerWebWindows2016SQLServerStandardWindows2016SQLServer2017StandardWindows2016SQLServerStandardAllWindows2016SQLServer2017StandardAllCustom" +const _ArchiveOSTypes_name = "CentOSCentOS6UbuntuDebianVyOSCoreOSRancherOSKusanagiSophosUTMFreeBSDNetwiserOPNsenseWindows2012Windows2012RDSWindows2012RDSOfficeWindows2016Windows2016RDSWindows2016RDSOfficeWindows2016SQLServerWebWindows2016SQLServerStandardWindows2016SQLServer2017StandardWindows2016SQLServerStandardAllWindows2016SQLServer2017StandardAllWindows2019Custom" -var _ArchiveOSTypes_index = [...]uint16{0, 6, 13, 19, 25, 29, 35, 44, 52, 61, 68, 76, 84, 95, 109, 129, 140, 154, 174, 197, 225, 257, 288, 323, 329} +var _ArchiveOSTypes_index = [...]uint16{0, 6, 13, 19, 25, 29, 35, 44, 52, 61, 68, 76, 84, 95, 109, 129, 140, 154, 174, 197, 225, 257, 288, 323, 334, 340} func (i ArchiveOSTypes) String() string { if i < 0 || i >= ArchiveOSTypes(len(_ArchiveOSTypes_index)-1) { diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/proxylb.go b/vendor/github.com/sacloud/libsacloud/sacloud/proxylb.go new file mode 100644 index 00000000..64523e6a --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/proxylb.go @@ -0,0 +1,517 @@ +package sacloud + +import ( + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "strconv" + "strings" + "time" +) + +// ProxyLB ProxyLB(CommonServiceItem) +type ProxyLB struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + propAvailability // 有効状態 + + Status *ProxyLBStatus `json:",omitempty"` // ステータス + Provider ProxyLBProvider `json:",omitempty"` // プロバイダ + Settings ProxyLBSettings `json:",omitempty"` // ProxyLB設定 + +} + +// ProxyLBSettings ProxyLB設定 +type ProxyLBSettings struct { + ProxyLB ProxyLBSetting `json:",omitempty"` // ProxyLB ProxyLBエントリー +} + +// ProxyLBStatus ProxyLBステータス +type ProxyLBStatus struct { + FQDN string `json:",omitempty"` // 割り当てられたFQDN(site-*******.proxylb?.sakura.ne.jp) UseVIPFailoverがtrueの場合のみ有効 + VirtualIPAddress string `json:",omitempty"` // 割り当てられたVIP UseVIPFailoverがfalseの場合のみ有効 + ProxyNetworks []string `json:",omitempty"` // プロキシ元ネットワークアドレス(CIDR) + UseVIPFailover bool // VIPフェイルオーバ +} + +// ProxyLBProvider プロバイダ +type ProxyLBProvider struct { + Class string `json:",omitempty"` // クラス +} + +// CreateNewProxyLB ProxyLB作成 +func CreateNewProxyLB(name string) *ProxyLB { + return &ProxyLB{ + Resource: &Resource{}, + propName: propName{Name: name}, + Provider: ProxyLBProvider{ + Class: "proxylb", + }, + Settings: ProxyLBSettings{ + ProxyLB: ProxyLBSetting{ + HealthCheck: defaultProxyLBHealthCheck, + SorryServer: ProxyLBSorryServer{}, + Servers: []ProxyLBServer{}, + }, + }, + } +} + +// ProxyLBPlan ProxyLBプラン +type ProxyLBPlan int + +var ( + // ProxyLBPlan1000 1,000cpsプラン + ProxyLBPlan1000 = ProxyLBPlan(1000) + // ProxyLBPlan5000 5,000cpsプラン + ProxyLBPlan5000 = ProxyLBPlan(5000) + // ProxyLBPlan10000 10,000cpsプラン + ProxyLBPlan10000 = ProxyLBPlan(10000) + // ProxyLBPlan50000 50,000cpsプラン + ProxyLBPlan50000 = ProxyLBPlan(50000) + // ProxyLBPlan100000 100,000cpsプラン + ProxyLBPlan100000 = ProxyLBPlan(100000) +) + +// AllowProxyLBPlans 有効なプランIDリスト +var AllowProxyLBPlans = []int{ + int(ProxyLBPlan1000), + int(ProxyLBPlan5000), + int(ProxyLBPlan10000), + int(ProxyLBPlan50000), + int(ProxyLBPlan100000), +} + +// GetPlan プラン取得(デフォルト: 1000cps) +func (p *ProxyLB) GetPlan() ProxyLBPlan { + classes := strings.Split(p.ServiceClass, "/") + class, err := strconv.Atoi(classes[len(classes)-1]) + if err != nil { + return ProxyLBPlan1000 + } + return ProxyLBPlan(class) +} + +// SetPlan プラン指定 +func (p *ProxyLB) SetPlan(plan ProxyLBPlan) { + p.ServiceClass = fmt.Sprintf("cloud/proxylb/plain/%d", plan) +} + +// SetHTTPHealthCheck HTTPヘルスチェック 設定 +func (p *ProxyLB) SetHTTPHealthCheck(hostHeader, path string, delayLoop int) { + if delayLoop <= 0 { + delayLoop = 10 + } + + p.Settings.ProxyLB.HealthCheck.Protocol = "http" + p.Settings.ProxyLB.HealthCheck.Host = hostHeader + p.Settings.ProxyLB.HealthCheck.Path = path + p.Settings.ProxyLB.HealthCheck.DelayLoop = delayLoop +} + +// SetTCPHealthCheck TCPヘルスチェック 設定 +func (p *ProxyLB) SetTCPHealthCheck(delayLoop int) { + if delayLoop <= 0 { + delayLoop = 10 + } + + p.Settings.ProxyLB.HealthCheck.Protocol = "tcp" + p.Settings.ProxyLB.HealthCheck.Host = "" + p.Settings.ProxyLB.HealthCheck.Path = "" + p.Settings.ProxyLB.HealthCheck.DelayLoop = delayLoop +} + +// SetSorryServer ソーリーサーバ 設定 +func (p *ProxyLB) SetSorryServer(ipaddress string, port int) { + var pt *int + if port > 0 { + pt = &port + } + p.Settings.ProxyLB.SorryServer = ProxyLBSorryServer{ + IPAddress: ipaddress, + Port: pt, + } +} + +// ClearSorryServer ソーリーサーバ クリア +func (p *ProxyLB) ClearSorryServer() { + p.SetSorryServer("", 0) +} + +// HasProxyLBServer ProxyLB配下にサーバーを保持しているか判定 +func (p *ProxyLB) HasProxyLBServer() bool { + return len(p.Settings.ProxyLB.Servers) > 0 +} + +// ClearProxyLBServer ProxyLB配下のサーバーをクリア +func (p *ProxyLB) ClearProxyLBServer() { + p.Settings.ProxyLB.Servers = []ProxyLBServer{} +} + +// AddBindPort バインドポート追加 +func (p *ProxyLB) AddBindPort(mode string, port int) { + p.Settings.ProxyLB.AddBindPort(mode, port) +} + +// DeleteBindPort バインドポート削除 +func (p *ProxyLB) DeleteBindPort(mode string, port int) { + p.Settings.ProxyLB.DeleteBindPort(mode, port) +} + +// ClearBindPorts バインドポート クリア +func (p *ProxyLB) ClearBindPorts() { + p.Settings.ProxyLB.BindPorts = []*ProxyLBBindPorts{} +} + +// AddServer ProxyLB配下のサーバーを追加 +func (p *ProxyLB) AddServer(ip string, port int, enabled bool) { + p.Settings.ProxyLB.AddServer(ip, port, enabled) +} + +// DeleteServer ProxyLB配下のサーバーを削除 +func (p *ProxyLB) DeleteServer(ip string, port int) { + p.Settings.ProxyLB.DeleteServer(ip, port) +} + +// ProxyLBSetting ProxyLBセッティング +type ProxyLBSetting struct { + HealthCheck ProxyLBHealthCheck `json:",omitempty"` // ヘルスチェック + SorryServer ProxyLBSorryServer `json:",omitempty"` // ソーリーサーバー + BindPorts []*ProxyLBBindPorts `json:",omitempty"` // プロキシ方式(プロトコル&ポート) + Servers []ProxyLBServer `json:",omitempty"` // サーバー +} + +// ProxyLBSorryServer ソーリーサーバ +type ProxyLBSorryServer struct { + IPAddress string // IPアドレス + Port *int // ポート +} + +// AddBindPort バインドポート追加 +func (s *ProxyLBSetting) AddBindPort(mode string, port int) { + var isExist bool + for i := range s.BindPorts { + if s.BindPorts[i].ProxyMode == mode && s.BindPorts[i].Port == port { + isExist = true + } + } + + if !isExist { + s.BindPorts = append(s.BindPorts, &ProxyLBBindPorts{ + ProxyMode: mode, + Port: port, + }) + } +} + +// DeleteBindPort バインドポート削除 +func (s *ProxyLBSetting) DeleteBindPort(mode string, port int) { + var res []*ProxyLBBindPorts + for i := range s.BindPorts { + if s.BindPorts[i].ProxyMode != mode || s.BindPorts[i].Port != port { + res = append(res, s.BindPorts[i]) + } + } + s.BindPorts = res +} + +// AddServer ProxyLB配下のサーバーを追加 +func (s *ProxyLBSetting) AddServer(ip string, port int, enabled bool) { + var record ProxyLBServer + var isExist = false + for i := range s.Servers { + if s.Servers[i].IPAddress == ip && s.Servers[i].Port == port { + isExist = true + s.Servers[i].Enabled = enabled + } + } + + if !isExist { + record = ProxyLBServer{ + IPAddress: ip, + Port: port, + Enabled: enabled, + } + s.Servers = append(s.Servers, record) + } +} + +// DeleteServer ProxyLB配下のサーバーを削除 +func (s *ProxyLBSetting) DeleteServer(ip string, port int) { + var res []ProxyLBServer + for i := range s.Servers { + if s.Servers[i].IPAddress != ip || s.Servers[i].Port != port { + res = append(res, s.Servers[i]) + } + } + + s.Servers = res +} + +// AllowProxyLBBindModes プロキシ方式 +var AllowProxyLBBindModes = []string{"http", "https"} + +// ProxyLBBindPorts プロキシ方式 +type ProxyLBBindPorts struct { + ProxyMode string `json:",omitempty"` // モード(プロトコル) + Port int `json:",omitempty"` // ポート +} + +// ProxyLBServer ProxyLB配下のサーバー +type ProxyLBServer struct { + IPAddress string `json:",omitempty"` // IPアドレス + Port int `json:",omitempty"` // ポート + Enabled bool `json:",omitempty"` // 有効/無効 +} + +// NewProxyLBServer ProxyLB配下のサーバ作成 +func NewProxyLBServer(ipaddress string, port int) *ProxyLBServer { + return &ProxyLBServer{ + IPAddress: ipaddress, + Port: port, + Enabled: true, + } +} + +// AllowProxyLBHealthCheckProtocols プロキシLBで利用できるヘルスチェックプロトコル +var AllowProxyLBHealthCheckProtocols = []string{"http", "tcp"} + +// ProxyLBHealthCheck ヘルスチェック +type ProxyLBHealthCheck struct { + Protocol string `json:",omitempty"` // プロトコル + Host string `json:",omitempty"` // 対象ホスト + Path string `json:",omitempty"` // HTTPの場合のリクエストパス + DelayLoop int `json:",omitempty"` // 監視間隔 + +} + +var defaultProxyLBHealthCheck = ProxyLBHealthCheck{ + Protocol: "http", + Host: "", + Path: "/", + DelayLoop: 10, +} + +// ProxyLBAdditionalCerts additional certificates +type ProxyLBAdditionalCerts []*ProxyLBCertificate + +// ProxyLBCertificates ProxyLBのSSL証明書 +type ProxyLBCertificates struct { + ServerCertificate string // サーバ証明書 + IntermediateCertificate string // 中間証明書 + PrivateKey string // 秘密鍵 + CertificateEndDate time.Time `json:",omitempty"` // 有効期限 + CertificateCommonName string `json:",omitempty"` // CommonName + AdditionalCerts ProxyLBAdditionalCerts +} + +// UnmarshalJSON UnmarshalJSON(AdditionalCertsが空の場合に空文字を返す問題への対応) +func (p *ProxyLBAdditionalCerts) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `` { + return nil + } + + var certs []*ProxyLBCertificate + if err := json.Unmarshal(data, &certs); err != nil { + return err + } + + *p = certs + return nil +} + +// SetPrimaryCert PrimaryCertを設定 +func (p *ProxyLBCertificates) SetPrimaryCert(cert *ProxyLBCertificate) { + p.ServerCertificate = cert.ServerCertificate + p.IntermediateCertificate = cert.IntermediateCertificate + p.PrivateKey = cert.PrivateKey + p.CertificateEndDate = cert.CertificateEndDate + p.CertificateCommonName = cert.CertificateCommonName +} + +// SetPrimaryCertValue PrimaryCertを設定 +func (p *ProxyLBCertificates) SetPrimaryCertValue(serverCert, intermediateCert, privateKey string) { + p.ServerCertificate = serverCert + p.IntermediateCertificate = intermediateCert + p.PrivateKey = privateKey +} + +// AddAdditionalCert AdditionalCertを追加 +func (p *ProxyLBCertificates) AddAdditionalCert(serverCert, intermediateCert, privateKey string) { + p.AdditionalCerts = append(p.AdditionalCerts, &ProxyLBCertificate{ + ServerCertificate: serverCert, + IntermediateCertificate: intermediateCert, + PrivateKey: privateKey, + }) +} + +// RemoveAdditionalCertAt 指定のインデックスを持つAdditionalCertを削除 +func (p *ProxyLBCertificates) RemoveAdditionalCertAt(index int) { + var certs []*ProxyLBCertificate + for i, cert := range p.AdditionalCerts { + if i != index { + certs = append(certs, cert) + } + } + p.AdditionalCerts = certs +} + +// RemoveAdditionalCert 指定の内容を持つAdditionalCertを削除 +func (p *ProxyLBCertificates) RemoveAdditionalCert(serverCert, intermediateCert, privateKey string) { + var certs []*ProxyLBCertificate + for _, cert := range p.AdditionalCerts { + if !(cert.ServerCertificate == serverCert && cert.IntermediateCertificate == intermediateCert && cert.PrivateKey == privateKey) { + certs = append(certs, cert) + } + } + p.AdditionalCerts = certs +} + +// RemoveAdditionalCerts AdditionalCertsを全て削除 +func (p *ProxyLBCertificates) RemoveAdditionalCerts() { + p.AdditionalCerts = []*ProxyLBCertificate{} +} + +// UnmarshalJSON UnmarshalJSON(CertificateEndDateのtime.TimeへのUnmarshal対応) +func (p *ProxyLBCertificates) UnmarshalJSON(data []byte) error { + var tmp map[string]interface{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + p.ServerCertificate = tmp["ServerCertificate"].(string) + p.IntermediateCertificate = tmp["IntermediateCertificate"].(string) + p.PrivateKey = tmp["PrivateKey"].(string) + p.CertificateCommonName = tmp["CertificateCommonName"].(string) + endDate := tmp["CertificateEndDate"].(string) + if endDate != "" { + date, err := time.Parse("Jan _2 15:04:05 2006 MST", endDate) + if err != nil { + return err + } + p.CertificateEndDate = date + } + + if _, ok := tmp["AdditionalCerts"].(string); !ok { + rawCerts, err := json.Marshal(tmp["AdditionalCerts"]) + if err != nil { + return err + } + var additionalCerts ProxyLBAdditionalCerts + if err := json.Unmarshal(rawCerts, &additionalCerts); err != nil { + return err + } + p.AdditionalCerts = additionalCerts + } + + return nil +} + +// ParseServerCertificate サーバ証明書のパース +func (p *ProxyLBCertificates) ParseServerCertificate() (*x509.Certificate, error) { + cert, e := p.parseCertificate(p.ServerCertificate) + if e != nil { + return nil, e + } + return cert, nil +} + +// ParseIntermediateCertificate 中間証明書のパース +func (p *ProxyLBCertificates) ParseIntermediateCertificate() (*x509.Certificate, error) { + cert, e := p.parseCertificate(p.IntermediateCertificate) + if e != nil { + return nil, e + } + return cert, nil +} + +func (p *ProxyLBCertificates) parseCertificate(certPEM string) (*x509.Certificate, error) { + block, _ := pem.Decode([]byte(certPEM)) + if block != nil { + return x509.ParseCertificate(block.Bytes) + } + return nil, fmt.Errorf("can't decode certificate") +} + +// ProxyLBCertificate ProxyLBのSSL証明書詳細 +type ProxyLBCertificate struct { + ServerCertificate string // サーバ証明書 + IntermediateCertificate string // 中間証明書 + PrivateKey string // 秘密鍵 + CertificateEndDate time.Time `json:",omitempty"` // 有効期限 + CertificateCommonName string `json:",omitempty"` // CommonName +} + +// UnmarshalJSON UnmarshalJSON(CertificateEndDateのtime.TimeへのUnmarshal対応) +func (p *ProxyLBCertificate) UnmarshalJSON(data []byte) error { + var tmp map[string]interface{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + p.ServerCertificate = tmp["ServerCertificate"].(string) + p.IntermediateCertificate = tmp["IntermediateCertificate"].(string) + p.PrivateKey = tmp["PrivateKey"].(string) + p.CertificateCommonName = tmp["CertificateCommonName"].(string) + endDate := tmp["CertificateEndDate"].(string) + if endDate != "" { + date, err := time.Parse("Jan _2 15:04:05 2006 MST", endDate) + if err != nil { + return err + } + p.CertificateEndDate = date + } + + return nil +} + +// ParseServerCertificate サーバ証明書のパース +func (p *ProxyLBCertificate) ParseServerCertificate() (*x509.Certificate, error) { + cert, e := p.parseCertificate(p.ServerCertificate) + if e != nil { + return nil, e + } + return cert, nil +} + +// ParseIntermediateCertificate 中間証明書のパース +func (p *ProxyLBCertificate) ParseIntermediateCertificate() (*x509.Certificate, error) { + cert, e := p.parseCertificate(p.IntermediateCertificate) + if e != nil { + return nil, e + } + return cert, nil +} + +func (p *ProxyLBCertificate) parseCertificate(certPEM string) (*x509.Certificate, error) { + block, _ := pem.Decode([]byte(certPEM)) + if block != nil { + return x509.ParseCertificate(block.Bytes) + } + return nil, fmt.Errorf("can't decode certificate") +} + +// ProxyLBHealth ProxyLBのヘルスチェック戻り値 +type ProxyLBHealth struct { + ActiveConn int // アクティブなコネクション数 + CPS int // 秒あたりコネクション数 + Servers []*ProxyLBHealthServer // 実サーバのステータス + CurrentVIP string // 現在のVIP +} + +// ProxyLBHealthServer ProxyLBの実サーバのステータス +type ProxyLBHealthServer struct { + ActiveConn int // アクティブなコネクション数 + Status string // ステータス(UP or DOWN) + IPAddress string // IPアドレス + Port string // ポート + CPS int // 秒あたりコネクション数 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/server.go b/vendor/github.com/sacloud/libsacloud/sacloud/server.go index 477966dc..1b699152 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/server.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/server.go @@ -86,6 +86,119 @@ func (s *Server) CIDRIPAddress() string { return "" } +// UpstreamType 1番目(0番目)のNICの上流ネットワーク種別 +func (s *Server) UpstreamType() EUpstreamNetworkType { + return s.UpstreamTypeAt(0) +} + +// UpstreamTypeAt 指定インデックスのNICの上流ネットワーク種別 +func (s *Server) UpstreamTypeAt(index int) EUpstreamNetworkType { + if len(s.Interfaces) <= index { + return EUpstreamNetworkUnknown + } + return s.Interfaces[index].UpstreamType() +} + +// SwitchID 上流のスイッチのID +// +// NICがない、上流スイッチが見つからない、上流が共有セグメントの場合は-1を返す +func (s *Server) SwitchID() int64 { + return s.SwitchIDAt(0) +} + +// SwitchIDAt 上流ネットワークのスイッチのID +// +// NICがない、上流スイッチが見つからない、上流が共有セグメントの場合は-1を返す +func (s *Server) SwitchIDAt(index int) int64 { + if len(s.Interfaces) <= index { + return -1 + } + + nic := s.Interfaces[index] + if nic.Switch == nil || nic.Switch.Scope == ESCopeShared { + return -1 + } + return nic.Switch.ID +} + +// SwitchName 上流のスイッチのID +// +// NICがない、上流スイッチが見つからない、上流が共有セグメントの場合は空文字を返す +func (s *Server) SwitchName() string { + return s.SwitchNameAt(0) +} + +// SwitchNameAt 上流ネットワークのスイッチのID +// +// NICがない、上流スイッチが見つからない、上流が共有セグメントの場合は空文字を返す +func (s *Server) SwitchNameAt(index int) string { + if len(s.Interfaces) <= index { + return "" + } + + nic := s.Interfaces[index] + if nic.Switch == nil || nic.Switch.Scope == ESCopeShared { + return "" + } + return nic.Switch.Name +} + +// Bandwidth 上流ネットワークの帯域幅(単位:Mbps) +// +// -1: 1番目(0番目)のNICが存在しない場合 or 切断されている場合 +// 0 : 制限なしの場合 +// 以外: 帯域幅(Mbps) +func (s *Server) Bandwidth() int { + return s.BandwidthAt(0) +} + +// BandwidthAt 上流ネットワークの帯域幅(単位:Mbps) +// +// -1: 存在しないインデックスを取得した場合 or 切断されている場合 +// 0 : 制限なしの場合 +// 以外: 帯域幅(Mbps) +func (s *Server) BandwidthAt(index int) int { + if len(s.Interfaces) <= index { + return -1 + } + + nic := s.Interfaces[index] + + switch nic.UpstreamType() { + case EUpstreamNetworkNone: + return -1 + case EUpstreamNetworkShared: + return 100 + case EUpstreamNetworkSwitch, EUpstreamNetworkRouter: + // + // 上流ネットワークがスイッチだった場合の帯域制限 + // https://manual.sakura.ad.jp/cloud/support/technical/network.html#support-network-03 + // + + // 専有ホストの場合は制限なし + if s.PrivateHost != nil { + return 0 + } + + // メモリに応じた制限 + memory := s.GetMemoryGB() + switch { + case memory < 32: + return 1000 + case 32 <= memory && memory < 128: + return 2000 + case 128 <= memory && memory < 224: + return 5000 + case 224 <= memory: + return 10000 + default: + return -1 + } + default: + return -1 + } +} + const ( // ServerMaxInterfaceLen サーバーに接続できるNICの最大数 ServerMaxInterfaceLen = 10 diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/sim.go b/vendor/github.com/sacloud/libsacloud/sacloud/sim.go index e4367d0f..a663b150 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/sim.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/sim.go @@ -7,6 +7,15 @@ import ( "time" ) +const ( + // SIMOperatorsKDDI KDDI + SIMOperatorsKDDI = "KDDI" + // SIMOperatorsDOCOMO Docomo + SIMOperatorsDOCOMO = "NTT DOCOMO" + // SIMOperatorsSoftBank SoftBank + SIMOperatorsSoftBank = "SoftBank" +) + // SIM SIM(CommonServiceItem) type SIM struct { *Resource // ID @@ -101,6 +110,18 @@ type SIMLog struct { IMSI string `json:"imsi,omitempty"` } +// SIMNetworkOperatorConfig SIM通信キャリア設定 +type SIMNetworkOperatorConfig struct { + Allow bool `json:"allow,omitempty"` + CountryCode string `json:"country_code,omitempty"` + Name string `json:"name,omitempty"` +} + +// SIMNetworkOperatorConfigs SIM通信キャリア設定 リクエストパラメータ +type SIMNetworkOperatorConfigs struct { + NetworkOperatorConfigs []*SIMNetworkOperatorConfig `json:"network_operator_config,omitempty"` +} + // CreateNewSIM SIM作成 func CreateNewSIM(name string, iccID string, passcode string) *SIM { return &SIM{ diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go b/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go index 365f2861..e2a0844d 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go @@ -1,5 +1,7 @@ package sacloud +import "time" + // SimpleMonitor シンプル監視 type SimpleMonitor struct { *Resource // ID @@ -70,6 +72,33 @@ type SimpleMonitorNotify struct { IncomingWebhooksURL string `json:",omitempty"` // Slack通知の場合のWebhook URL } +// ESimpleMonitorHealth シンプル監視ステータス +type ESimpleMonitorHealth string + +var ( + // EHealthUp Up + EHealthUp = ESimpleMonitorHealth("UP") + // EHealthDown Down + EHealthDown = ESimpleMonitorHealth("DOWN") +) + +// IsUp アップ +func (e ESimpleMonitorHealth) IsUp() bool { + return e == EHealthUp +} + +// IsDown ダウン +func (e ESimpleMonitorHealth) IsDown() bool { + return e == EHealthDown +} + +// SimpleMonitorHealthCheckStatus シンプル監視ステータス +type SimpleMonitorHealthCheckStatus struct { + LastCheckedAt time.Time + LastHealthChangedAt time.Time + Health ESimpleMonitorHealth +} + // CreateNewSimpleMonitor シンプル監視作成 func CreateNewSimpleMonitor(target string) *SimpleMonitor { return &SimpleMonitor{ diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/switch.go b/vendor/github.com/sacloud/libsacloud/sacloud/switch.go index cb90bfb1..75013c12 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/switch.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/switch.go @@ -15,6 +15,7 @@ type Switch struct { propIcon // アイコン propTags // タグ propCreatedAt // 作成日時 + propZone // ゾーン ServerCount int `json:",omitempty"` // 接続サーバー数 ApplianceCount int `json:",omitempty"` // 接続アプライアンス数 diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go index 20bee798..ed394e8b 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go @@ -236,3 +236,134 @@ func (v *VPCRouter) FindBelongsInterface(ip net.IP) (int, *VPCRouterInterface) { } return -1, nil } + +// IPAddress1 1番目(0番目)のNICのIPアドレス1 +func (v *VPCRouter) IPAddress1() string { + return v.IPAddress1At(0) +} + +// IPAddress1At 指定インデックスのNICのIPアドレス1 +func (v *VPCRouter) IPAddress1At(index int) string { + if len(v.Interfaces) <= index { + return "" + } + + if index == 0 { + if v.IsStandardPlan() { + return v.Interfaces[0].IPAddress + } + + if !v.HasInterfaces() { + return "" + } + if len(v.Settings.Router.Interfaces[0].IPAddress) < 1 { + return "" + } + return v.Settings.Router.Interfaces[0].IPAddress[0] + } + + nic := v.Settings.Router.Interfaces[index] + if len(nic.IPAddress) < 1 { + return "" + } + return nic.IPAddress[0] +} + +// IPAddress2 1番目(0番目)のNICのIPアドレス2 +func (v *VPCRouter) IPAddress2() string { + return v.IPAddress2At(0) +} + +// IPAddress2At 指定インデックスのNICのIPアドレス2 +func (v *VPCRouter) IPAddress2At(index int) string { + if v.IsStandardPlan() { + return "" + } + if len(v.Interfaces) <= index { + return "" + } + + if index == 0 { + if !v.HasInterfaces() { + return "" + } + if len(v.Settings.Router.Interfaces[0].IPAddress) < 2 { + return "" + } + return v.Settings.Router.Interfaces[0].IPAddress[1] + } + + nic := v.Settings.Router.Interfaces[index] + if len(nic.IPAddress) < 2 { + return "" + } + return nic.IPAddress[1] +} + +// VirtualIPAddress 1番目(0番目)のNICのVIP +func (v *VPCRouter) VirtualIPAddress() string { + return v.VirtualIPAddressAt(0) +} + +// VirtualIPAddressAt 指定インデックスのNICのVIP +func (v *VPCRouter) VirtualIPAddressAt(index int) string { + if v.IsStandardPlan() { + return "" + } + if len(v.Interfaces) <= index { + return "" + } + + return v.Settings.Router.Interfaces[0].VirtualIPAddress +} + +// NetworkMaskLen 1番目(0番目)のNICのネットワークマスク長 +func (v *VPCRouter) NetworkMaskLen() int { + return v.NetworkMaskLenAt(0) +} + +// NetworkMaskLenAt 指定インデックスのNICのネットワークマスク長 +func (v *VPCRouter) NetworkMaskLenAt(index int) int { + if !v.HasInterfaces() { + return -1 + } + if len(v.Interfaces) <= index { + return -1 + } + + if index == 0 { + return v.Interfaces[0].Switch.Subnet.NetworkMaskLen + } + + return v.Settings.Router.Interfaces[index].NetworkMaskLen +} + +// Zone スイッチから現在のゾーン名を取得 +// +// Note: 共有セグメント接続時は取得不能 +func (v *VPCRouter) Zone() string { + if v.Switch != nil { + return v.Switch.GetZoneName() + } + + if len(v.Interfaces) > 0 && v.Interfaces[0].Switch != nil { + return v.Interfaces[0].Switch.GetZoneName() + } + + return "" +} + +// VRID VRIDを取得 +// +// スタンダードプラン、またはVRIDの参照に失敗した場合は-1を返す +func (v *VPCRouter) VRID() int { + if v.IsStandardPlan() { + return -1 + } + + if !v.HasSetting() || v.Settings.Router.VRID == nil { + return -1 + } + + return *v.Settings.Router.VRID +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go index cb8e2b22..70a5f798 100644 --- a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go +++ b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go @@ -5,6 +5,7 @@ type VPCRouterStatus struct { FirewallReceiveLogs []string FirewallSendLogs []string VPNLogs []string + SessionCount int DHCPServerLeases []struct { IPAddress string MACAddress string @@ -12,12 +13,12 @@ type VPCRouterStatus struct { L2TPIPsecServerSessions []struct { User string IPAddress string - TimeSec string + TimeSec int } PPTPServerSessions []struct { User string IPAddress string - TimeSec string + TimeSec int } SiteToSiteIPsecVPNPeers []struct { Status string diff --git a/vendor/github.com/sacloud/libsacloud/utils/mutexkv/mutexkv.go b/vendor/github.com/sacloud/libsacloud/utils/mutexkv/mutexkv.go new file mode 100644 index 00000000..38355588 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/utils/mutexkv/mutexkv.go @@ -0,0 +1,43 @@ +package mutexkv + +import ( + "sync" +) + +// MutexKV is a simple key/value store for arbitrary mutexes. It can be used to +// serialize changes across arbitrary collaborators that share knowledge of the +// keys they must serialize on. +type MutexKV struct { + lock sync.Mutex + store map[string]*sync.Mutex +} + +// Lock the mutex for the given key. Caller is responsible for calling Unlock +// for the same key +func (m *MutexKV) Lock(key string) { + m.get(key).Lock() +} + +// Unlock the mutex for the given key. Caller must have called Lock for the same key first +func (m *MutexKV) Unlock(key string) { + m.get(key).Unlock() +} + +// Returns a mutex for the given key, no guarantee of its lock status +func (m *MutexKV) get(key string) *sync.Mutex { + m.lock.Lock() + defer m.lock.Unlock() + mutex, ok := m.store[key] + if !ok { + mutex = &sync.Mutex{} + m.store[key] = mutex + } + return mutex +} + +// NewMutexKV Returns a properly initalized MutexKV +func NewMutexKV() *MutexKV { + return &MutexKV{ + store: make(map[string]*sync.Mutex), + } +}