proxylb.go raw

   1  // Copyright 2022-2025 The sacloud/iaas-api-go Authors
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License");
   4  // you may not use this file except in compliance with the License.
   5  // You may obtain a copy of the License at
   6  //
   7  //      http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS,
  11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12  // See the License for the specific language governing permissions and
  13  // limitations under the License.
  14  
  15  package naked
  16  
  17  import (
  18  	"encoding/json"
  19  	"strings"
  20  	"time"
  21  
  22  	"github.com/sacloud/iaas-api-go/types"
  23  )
  24  
  25  // ProxyLB エンハンスドロードバランサ
  26  type ProxyLB struct {
  27  	ID           types.ID            `json:",omitempty" yaml:"id,omitempty" structs:",omitempty"`
  28  	Name         string              `json:",omitempty" yaml:"name,omitempty" structs:",omitempty"`
  29  	Description  string              `yaml:"description"`
  30  	Tags         types.Tags          `yaml:"tags"`
  31  	Icon         *Icon               `json:",omitempty" yaml:"icon,omitempty" structs:",omitempty"`
  32  	CreatedAt    *time.Time          `json:",omitempty" yaml:"created_at,omitempty" structs:",omitempty"`
  33  	ModifiedAt   *time.Time          `json:",omitempty" yaml:"modified_at,omitempty" structs:",omitempty"`
  34  	Availability types.EAvailability `json:",omitempty" yaml:"availability,omitempty" structs:",omitempty"`
  35  	Provider     *Provider           `json:",omitempty" yaml:"provider,omitempty" structs:",omitempty"`
  36  	Settings     *ProxyLBSettings    `json:",omitempty" yaml:"settings,omitempty" structs:",omitempty"`
  37  	SettingsHash string              `json:",omitempty" yaml:"settings_hash,omitempty" structs:",omitempty"`
  38  	Status       *ProxyLBStatus      `json:",omitempty" yaml:"status,omitempty" structs:",omitempty"`
  39  
  40  	// ServiceClass [HACK] ServiceClassはプランとリージョンから決定するためjson.Marshalerで出力する。
  41  	// see https://github.com/sacloud/libsacloud/issues/577
  42  	ServiceClass string             `json:",omitempty" yaml:"service_class,omitempty" structs:",omitempty"`
  43  	Plan         types.EProxyLBPlan `json:"-"`
  44  }
  45  
  46  // MarshalJSON implements json.Marshaler
  47  func (p *ProxyLB) MarshalJSON() ([]byte, error) {
  48  	if p.Status != nil && p.Plan != types.EProxyLBPlan(0) {
  49  		p.ServiceClass = types.ProxyLBServiceClass(p.Plan, p.Status.Region)
  50  	}
  51  
  52  	type alias ProxyLB
  53  	tmp := alias(*p)
  54  	return json.Marshal(&tmp)
  55  }
  56  
  57  // UnmarshalJSON implements json.Unmarshaler
  58  func (p *ProxyLB) UnmarshalJSON(b []byte) error {
  59  	type alias ProxyLB
  60  	var tmp alias
  61  	if err := json.Unmarshal(b, &tmp); err != nil {
  62  		return err
  63  	}
  64  
  65  	tmp.Plan = types.ProxyLBPlanFromServiceClass(tmp.ServiceClass)
  66  	*p = ProxyLB(tmp)
  67  	return nil
  68  }
  69  
  70  // ProxyLBPlanChange エンハンスドロードバランサのプラン変更
  71  type ProxyLBPlanChange struct {
  72  	ServiceClass string `yaml:"service_class"`
  73  }
  74  
  75  // ProxyLBSettingsUpdate エンハンスドロードバランサ
  76  type ProxyLBSettingsUpdate struct {
  77  	Settings     *ProxyLBSettings `json:",omitempty" yaml:"settings,omitempty" structs:",omitempty"`
  78  	SettingsHash string           `json:",omitempty" yaml:"settings_hash,omitempty" structs:",omitempty"`
  79  }
  80  
  81  // ProxyLBSettings エンハンスドロードバランサ設定
  82  type ProxyLBSettings struct {
  83  	ProxyLB *ProxyLBSetting `json:",omitempty" yaml:"proxy_lb,omitempty" structs:",omitempty"`
  84  }
  85  
  86  // ProxyLBSetting エンハンスドロードバランサ設定
  87  type ProxyLBSetting struct {
  88  	HealthCheck          ProxyLBHealthCheck           `yaml:"health_check"`                                                              // ヘルスチェック
  89  	SorryServer          ProxyLBSorryServer           `yaml:"sorry_server"`                                                              // ソーリーサーバー
  90  	BindPorts            []*ProxyLBBindPorts          `yaml:"bind_ports"`                                                                // プロキシ方式(プロトコル&ポート)
  91  	Servers              []ProxyLBServer              `yaml:"servers"`                                                                   // サーバー
  92  	Rules                []ProxyLBRule                `yaml:"rules"`                                                                     // 振り分けルール
  93  	LetsEncrypt          *ProxyLBACMESetting          `json:",omitempty" yaml:"lets_encrypt,omitempty" structs:",omitempty"`             // Let's encryptでの証明書取得設定
  94  	StickySession        ProxyLBStickySession         `yaml:"sticky_session"`                                                            // StickySession
  95  	Timeout              ProxyLBTimeout               `json:",omitempty" yaml:"timeout,omitempty" structs:",omitempty"`                  // タイムアウト
  96  	Gzip                 ProxyLBGzip                  `yaml:"gzip"`                                                                      // Gzip
  97  	BackendHttpKeepAlive *ProxyLBBackendHTTPKeepAlive `json:",omitempty" yaml:",backend_http_keey_alive,omitempty" structs:",omitempty"` // 実サーバとのHTTP持続接続
  98  	MonitoringSuiteLog   *MonitoringSuiteLog          `json:",omitempty" yaml:"monitoring_suite_log,omitempty" structs:",omitempty"`
  99  	ProxyProtocol        ProxyLBProxyProtocol         `yaml:"proxy_protocol"`
 100  	Syslog               ProxyLBSyslog                `yaml:"syslog"`
 101  }
 102  
 103  // MarshalJSON nullの場合に空配列を出力するための実装
 104  func (s ProxyLBSetting) MarshalJSON() ([]byte, error) {
 105  	if s.BindPorts == nil {
 106  		s.BindPorts = make([]*ProxyLBBindPorts, 0)
 107  	}
 108  	if s.Servers == nil {
 109  		s.Servers = make([]ProxyLBServer, 0)
 110  	}
 111  	if s.Rules == nil {
 112  		s.Rules = make([]ProxyLBRule, 0)
 113  	}
 114  	// syslogは値がないと400エラーになるため両方空の場合はポートのデフォルト値だけ設定しておく
 115  	if s.Syslog.Server == "" && s.Syslog.Port == 0 {
 116  		s.Syslog.Port = 514
 117  	}
 118  
 119  	type alias ProxyLBSetting
 120  	tmp := alias(s)
 121  	return json.Marshal(&tmp)
 122  }
 123  
 124  // ProxyLBHealthCheck ヘルスチェック
 125  type ProxyLBHealthCheck struct {
 126  	Protocol  types.EProxyLBHealthCheckProtocol `json:",omitempty" yaml:"protocol,omitempty" structs:",omitempty"`
 127  	Path      string                            `json:",omitempty" yaml:"path,omitempty" structs:",omitempty"`
 128  	Host      string                            `json:",omitempty" yaml:"host,omitempty" structs:",omitempty"`
 129  	DelayLoop int                               `json:",omitempty" yaml:"delay_loop,omitempty" structs:",omitempty"`
 130  }
 131  
 132  // ProxyLBSorryServer ソーリーサーバ設定
 133  type ProxyLBSorryServer struct {
 134  	IPAddress string `yaml:"ip_address"`
 135  	Port      *int   `yaml:"port"`
 136  }
 137  
 138  // ProxyLBBindPorts プロキシ方式
 139  type ProxyLBBindPorts struct {
 140  	ProxyMode         types.EProxyLBProxyMode  `json:",omitempty" yaml:"proxy_mode,omitempty" structs:",omitempty"`          // モード(プロトコル)
 141  	Port              int                      `json:",omitempty" yaml:"port,omitempty" structs:",omitempty"`                // ポート
 142  	RedirectToHTTPS   bool                     `json:"RedirectToHttps" yaml:"redirect_to_https"`                             // HTTPSへのリダイレクト(モードがhttpの場合のみ)
 143  	SupportHTTP2      bool                     `json:"SupportHttp2" yaml:"support_http2"`                                    // HTTP/2のサポート(モードがhttpsの場合のみ)
 144  	AddResponseHeader []*ProxyLBResponseHeader `json:",omitempty" yaml:"add_response_header,omitempty" structs:",omitempty"` // レスポンスヘッダ
 145  	SSLPolicy         string                   `json:",omitempty" yaml:"ssl_policy,omitempty" structs:",omitempty"`          // SSLポリシー
 146  }
 147  
 148  // ProxyLBResponseHeader ポートごとの追加レスポンスヘッダ
 149  type ProxyLBResponseHeader struct {
 150  	Header string // ヘッダ名称(英字, 数字, ハイフン)
 151  	Value  string // 値(英字, 数字, 半角スペース, 一部記号(!#$%&'()*+,-./:;<=>?@[]^_`{|}~))
 152  }
 153  
 154  // ProxyLBServer ProxyLB配下のサーバー
 155  type ProxyLBServer struct {
 156  	IPAddress   string `json:",omitempty" yaml:"ip_address,omitempty" structs:",omitempty"` // IPアドレス
 157  	Port        int    `json:",omitempty" yaml:"port,omitempty" structs:",omitempty"`       // ポート
 158  	ServerGroup string `yaml:"server_group"`                                                // サーバグループ
 159  	Enabled     bool   // 有効/無効
 160  }
 161  
 162  // ProxyLBRule ProxyLBの振り分けルール
 163  type ProxyLBRule struct {
 164  	// 条件部
 165  	Host string `json:",omitempty" yaml:"host,omitempty" structs:",omitempty"` // ホストヘッダのパターン(ワイルドカードとして?と*が利用可能)
 166  	Path string `json:",omitempty" yaml:"path,omitempty" structs:",omitempty"` // パス
 167  
 168  	SourceIPs string `json:",omitempty" yaml:"source_ips,omitempty" structs:",omitempty"`
 169  
 170  	RequestHeaderName            string `json:",omitempty" yaml:"request_header_name,omitempty" structs:",omitempty"`
 171  	RequestHeaderValue           string `json:",omitempty" yaml:"request_header_value,omitempty" structs:",omitempty"`
 172  	RequestHeaderValueIgnoreCase bool   `yaml:"request_header_value_ignore_case"`
 173  	RequestHeaderValueNotMatch   bool   `yaml:"request_header_value_not_match"`
 174  
 175  	// アクション部
 176  	Action      types.EProxyLBRuleAction `json:",omitempty" yaml:"action,omitempty" structs:",omitempty"` // forward(実サーバへ転送) | redirect | fixed(固定レスポンス)
 177  	ServerGroup string                   `json:",omitempty" yaml:"server_group,omitempty" structs:",omitempty"`
 178  
 179  	RedirectLocation   string `json:",omitempty" yaml:"redirect_location,omitempty" structs:",omitempty"`
 180  	RedirectStatusCode string `json:",omitempty" yaml:"redirect_status_code,omitempty" structs:",omitempty"` // 301 | 302
 181  
 182  	FixedStatusCode  string                         `json:",omitempty" yaml:"fixed_status_code,omitempty" structs:",omitempty"`
 183  	FixedContentType types.EProxyLBFixedContentType `json:",omitempty" yaml:"fixed_content_type,omitempty" structs:",omitempty"`
 184  	FixedMessageBody string                         `json:",omitempty" yaml:"fixed_message_body,omitempty" structs:",omitempty"`
 185  }
 186  
 187  // ProxyLBACMESetting Let's Encryptでの証明書取得設定
 188  type ProxyLBACMESetting struct {
 189  	Enabled         bool     `yaml:"enabled"`
 190  	CommonName      string   `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 191  	SubjectAltNames []string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 192  }
 193  
 194  // MarshalJSON SubjectAltNamesをスライスから文字列にする
 195  func (p ProxyLBACMESetting) MarshalJSON() ([]byte, error) {
 196  	type tmpSetting struct {
 197  		Enabled         bool   `yaml:"enabled"`
 198  		CommonName      string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 199  		SubjectAltNames string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 200  	}
 201  	tmp := tmpSetting{
 202  		Enabled:         p.Enabled,
 203  		CommonName:      p.CommonName,
 204  		SubjectAltNames: strings.Join(p.SubjectAltNames, ","), // Note: カンマ区切りで統一
 205  	}
 206  	return json.Marshal(&tmp)
 207  }
 208  
 209  // UnmarshalJSON SubjectAltNamesを文字列からスライスにする
 210  func (p *ProxyLBACMESetting) UnmarshalJSON(data []byte) error {
 211  	type tmpSetting struct {
 212  		Enabled         bool   `yaml:"enabled"`
 213  		CommonName      string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 214  		SubjectAltNames string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 215  	}
 216  	var setting *tmpSetting
 217  	if err := json.Unmarshal(data, &setting); err != nil {
 218  		return err
 219  	}
 220  
 221  	*p = ProxyLBACMESetting{
 222  		Enabled:    setting.Enabled,
 223  		CommonName: setting.CommonName,
 224  		SubjectAltNames: strings.FieldsFunc(setting.SubjectAltNames, func(r rune) bool {
 225  			return r == ' ' || r == ',' || r == '\n'
 226  		}),
 227  	}
 228  	return nil
 229  }
 230  
 231  // ProxyLBStickySession セッション維持(Sticky session)設定
 232  type ProxyLBStickySession struct {
 233  	Enabled bool   `yaml:"enabled"`
 234  	Method  string `json:",omitempty" yaml:"method,omitempty" structs:",omitempty"`
 235  }
 236  
 237  // ProxyLBGzip Gzip圧縮設定
 238  type ProxyLBGzip struct {
 239  	Enabled bool `yaml:"enabled"`
 240  }
 241  
 242  type ProxyLBBackendHTTPKeepAlive struct {
 243  	Mode types.EProxyLBBackendHttpKeepAlive `json:",omitempty" yaml:"mode,omitempty" structs:",omitempty"`
 244  }
 245  
 246  // ProxyLBProxyProtocol ProxyProtocol(v2)の有効設定
 247  type ProxyLBProxyProtocol struct {
 248  	Enabled bool `yaml:"enabled"`
 249  }
 250  
 251  // ProxyLBSyslog Syslog設定
 252  type ProxyLBSyslog struct {
 253  	Server string `yaml:"server"`
 254  	Port   int    `yaml:"port"`
 255  }
 256  
 257  // ProxyLBTimeout 実サーバの通信タイムアウト
 258  type ProxyLBTimeout struct {
 259  	InactiveSec int `json:",omitempty" yaml:"inactive_sec" structs:",omitempty"` // 10から600まで1秒刻みで設定可
 260  }
 261  
 262  // ProxyLBStatus ステータス
 263  type ProxyLBStatus struct {
 264  	UseVIPFailover   bool                 `yaml:"use_vip_failover"`
 265  	Region           types.EProxyLBRegion `json:",omitempty" yaml:"region,omitempty" structs:",omitempty"`
 266  	ProxyNetworks    []string             `json:",omitempty" yaml:"proxy_networks,omitempty" structs:",omitempty"`
 267  	FQDN             string               `json:",omitempty" yaml:"fqdn,omitempty" structs:",omitempty"`
 268  	VirtualIPAddress string               `json:",omitempty" yaml:"virtual_ip_address,omitempty" structs:",omitempty"`
 269  }
 270  
 271  // ProxyLBAdditionalCerts additional certificates
 272  type ProxyLBAdditionalCerts []*ProxyLBCertificate
 273  
 274  // ProxyLBCertificates ProxyLBのSSL証明書
 275  type ProxyLBCertificates struct {
 276  	PrimaryCert     *ProxyLBCertificate    `yaml:"primary_cert"`
 277  	AdditionalCerts ProxyLBAdditionalCerts `yaml:"additional_certs"`
 278  }
 279  
 280  // MarshalJSON nullの場合に空配列を出力するための実装
 281  func (s ProxyLBCertificates) MarshalJSON() ([]byte, error) {
 282  	if s.AdditionalCerts == nil {
 283  		s.AdditionalCerts = make([]*ProxyLBCertificate, 0)
 284  	}
 285  	type alias ProxyLBCertificates
 286  	tmp := alias(s)
 287  	return json.Marshal(&tmp)
 288  }
 289  
 290  // UnmarshalJSON UnmarshalJSON(AdditionalCertsが空の場合に空文字を返す問題への対応)
 291  func (p *ProxyLBAdditionalCerts) UnmarshalJSON(data []byte) error {
 292  	targetData := strings.ReplaceAll(strings.ReplaceAll(string(data), " ", ""), "\n", "")
 293  	if targetData == `` {
 294  		return nil
 295  	}
 296  
 297  	var certs []*ProxyLBCertificate
 298  	if err := json.Unmarshal(data, &certs); err != nil {
 299  		return err
 300  	}
 301  
 302  	*p = certs
 303  	return nil
 304  }
 305  
 306  // ProxyLBCertificate ProxyLBのSSL証明書詳細
 307  type ProxyLBCertificate struct {
 308  	ServerCertificate       string     `yaml:"server_certificate"`                                                       // サーバ証明書
 309  	IntermediateCertificate string     `yaml:"intermediate_certificate"`                                                 // 中間証明書
 310  	PrivateKey              string     `yaml:"private_key"`                                                              // 秘密鍵
 311  	CertificateEndDate      *time.Time `json:",omitempty" yaml:"certificate_end_date,omitempty" structs:",omitempty"`    // 有効期限
 312  	CertificateCommonName   string     `json:",omitempty" yaml:"certificate_common_name,omitempty" structs:",omitempty"` // CommonName
 313  	CertificateAltNames     string     `json:",omitempty" yaml:"certificate_alt_names,omitempty" structs:",omitempty"`   // SAN
 314  }
 315  
 316  // UnmarshalJSON UnmarshalJSON(CertificateEndDateのtime.TimeへのUnmarshal対応)
 317  func (p *ProxyLBCertificate) UnmarshalJSON(data []byte) error {
 318  	var tmp map[string]interface{}
 319  	if err := json.Unmarshal(data, &tmp); err != nil {
 320  		return err
 321  	}
 322  
 323  	p.ServerCertificate = tmp["ServerCertificate"].(string)
 324  	p.IntermediateCertificate = tmp["IntermediateCertificate"].(string)
 325  	p.PrivateKey = tmp["PrivateKey"].(string)
 326  	p.CertificateCommonName = tmp["CertificateCommonName"].(string)
 327  	p.CertificateAltNames = tmp["CertificateAltNames"].(string)
 328  	endDate := tmp["CertificateEndDate"].(string)
 329  	if endDate != "" {
 330  		date, err := time.Parse("Jan _2 15:04:05 2006 MST", endDate)
 331  		if err != nil {
 332  			return err
 333  		}
 334  		p.CertificateEndDate = &date
 335  	}
 336  
 337  	return nil
 338  }
 339  
 340  // ProxyLBHealth ProxyLBのヘルスチェック戻り値
 341  type ProxyLBHealth struct {
 342  	ActiveConn int                    `json:",omitempty" yaml:"active_conn,omitempty" structs:",omitempty"` // アクティブなコネクション数
 343  	CPS        float64                `json:",omitempty" yaml:"cps,omitempty" structs:",omitempty"`         // 秒あたりコネクション数
 344  	Servers    []*ProxyLBHealthServer `json:",omitempty" yaml:"servers,omitempty" structs:",omitempty"`     // 実サーバのステータス
 345  	CurrentVIP string                 `json:",omitempty" yaml:"current_vip,omitempty" structs:",omitempty"` // 現在のVIP
 346  }
 347  
 348  // ProxyLBHealthServer ProxyLBの実サーバのステータス
 349  type ProxyLBHealthServer struct {
 350  	ActiveConn int                         `json:",omitempty" yaml:"active_conn,omitempty" structs:",omitempty"` // アクティブなコネクション数
 351  	Status     types.EServerInstanceStatus `json:",omitempty" yaml:"status,omitempty" structs:",omitempty"`      // ステータス(UP or DOWN)
 352  	IPAddress  string                      `json:",omitempty" yaml:"ip_address,omitempty" structs:",omitempty"`  // IPアドレス
 353  	Port       string                      `json:",omitempty" yaml:"port,omitempty" structs:",omitempty"`        // ポート
 354  	CPS        float64                     `json:",omitempty" yaml:"cps,omitempty" structs:",omitempty"`         // 秒あたりコネクション数
 355  }
 356