database.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  	"strconv"
  20  	"strings"
  21  	"time"
  22  
  23  	"github.com/sacloud/iaas-api-go/types"
  24  )
  25  
  26  // Database データベース
  27  type Database struct {
  28  	ID           types.ID            `json:",omitempty" yaml:"id,omitempty" structs:",omitempty"`
  29  	Name         string              `json:",omitempty" yaml:"name,omitempty" structs:",omitempty"`
  30  	Description  string              `yaml:"description"`
  31  	Tags         types.Tags          `yaml:"tags"`
  32  	Icon         *Icon               `json:",omitempty" yaml:"icon,omitempty" structs:",omitempty"`
  33  	CreatedAt    *time.Time          `json:",omitempty" yaml:"created_at,omitempty" structs:",omitempty"`
  34  	ModifiedAt   *time.Time          `json:",omitempty" yaml:"modified_at,omitempty" structs:",omitempty"`
  35  	Availability types.EAvailability `json:",omitempty" yaml:"availability,omitempty" structs:",omitempty"`
  36  	Class        string              `json:",omitempty" yaml:"class,omitempty" structs:",omitempty"`
  37  	ServiceClass string              `json:",omitempty" yaml:"service_class,omitempty" structs:",omitempty"`
  38  	Plan         *AppliancePlan      `json:",omitempty" yaml:"plan,omitempty" structs:",omitempty"`
  39  	Instance     *Instance           `json:",omitempty" yaml:"instance,omitempty" structs:",omitempty"`
  40  	Interfaces   Interfaces          `json:",omitempty" yaml:"interfaces,omitempty" structs:",omitempty"`
  41  	Switch       *Switch             `json:",omitempty" yaml:"switch,omitempty" structs:",omitempty"`
  42  	Settings     *DatabaseSettings   `json:",omitempty" yaml:"settings,omitempty" structs:",omitempty"`
  43  	SettingsHash string              `json:",omitempty" yaml:"settings_hash,omitempty" structs:",omitempty"`
  44  	Remark       *ApplianceRemark    `json:",omitempty" yaml:"remark,omitempty" structs:",omitempty"`
  45  	Disk         *DatabaseDisk       `json:",omitempty" yaml:"disk,omitempty" structs:",omitempty"`
  46  
  47  	Generation interface{}
  48  }
  49  
  50  type DatabaseDisk struct {
  51  	EncryptionAlgorithm types.EDiskEncryptionAlgorithm `json:",omitempty" yaml:"encryption_algorithm,omitempty" structs:",omitempty"`
  52  	EncryptionKey       *EncryptionKey                 `json:",omitempty" yaml:"kms_key,omitempty" structs:",omitempty"`
  53  }
  54  
  55  // DatabaseSettingsUpdate データベース
  56  type DatabaseSettingsUpdate struct {
  57  	Settings     *DatabaseSettings `json:",omitempty" yaml:"settings,omitempty" structs:",omitempty"`
  58  	SettingsHash string            `json:",omitempty" yaml:"settings_hash,omitempty" structs:",omitempty"`
  59  }
  60  
  61  // DatabaseSettings データベース設定
  62  type DatabaseSettings struct {
  63  	DBConf          *DatabaseSetting `json:",omitempty" yaml:"db_conf,omitempty" structs:",omitempty"`
  64  	MonitoringSuite *MonitoringSuite `json:",omitempty" yaml:"monitoring_suite_log,omitempty" structs:",omitempty"`
  65  }
  66  
  67  // DatabaseSetting データベース設定
  68  type DatabaseSetting struct {
  69  	Common      *DatabaseSettingCommon      `json:",omitempty" yaml:"common,omitempty" structs:",omitempty"`
  70  	Backup      *DatabaseSettingBackup      `json:",omitempty" yaml:"backup,omitempty" structs:",omitempty"`
  71  	Backupv2    *DatabaseSettingBackupv2    `json:",omitempty" yaml:"backupv2,omitempty" structs:",omitempty"`
  72  	Replication *DatabaseSettingReplication `json:",omitempty" yaml:"replication,omitempty" structs:",omitempty"`
  73  	Interfaces  DatabaseSettingInterfaces   `json:",omitempty" yaml:"common,omitempty" structs:",omitempty"`
  74  }
  75  
  76  // DatabaseSettingCommon データベース設定 汎用項目設定
  77  type DatabaseSettingCommon struct {
  78  	// WebUI WebUIの有効/無効、またはアクセスするためのアドレス
  79  	//
  80  	// [HACK] Create時はbool型、Read/Update時は文字列(FQDN or IP)となる。
  81  	// また、無効にするにはJSONで要素自体を指定しないことで行う。
  82  	WebUI           interface{}                   `yaml:"web_ui"`
  83  	ServicePort     int                           `json:",omitempty" yaml:"service_port,omitempty" structs:",omitempty"`
  84  	SourceNetwork   DatabaseSettingSourceNetworks `yaml:"source_network"`
  85  	DefaultUser     string                        `json:",omitempty" yaml:"default_user,omitempty" structs:",omitempty"`
  86  	UserPassword    string                        `json:",omitempty" yaml:"user_password,omitempty" structs:",omitempty"`
  87  	ReplicaUser     string                        `json:",omitempty" yaml:"replica_user,omitempty" structs:",omitempty"`
  88  	ReplicaPassword string                        `json:",omitempty" yaml:"replica_password,omitempty" structs:",omitempty"`
  89  }
  90  
  91  // DatabaseSettingSourceNetworks データベースへのアクセスを許可するCIDRリスト
  92  //
  93  // Note: すべての接続先を許可する場合は"0.0.0.0/0"を指定する。
  94  // この処理はMarshalJSON時にDatabaseSettingSourceNetwork側で行われるため、
  95  // APIクライアント側は許可したいCIDRブロックのリストを指定する。
  96  // libsacloudではすべての接続を拒否する設定はサポートしない。
  97  type DatabaseSettingSourceNetworks []string
  98  
  99  // MarshalJSON すべての接続先を許可する場合は"0.0.0.0/0"を指定するための対応
 100  func (d DatabaseSettingSourceNetworks) MarshalJSON() ([]byte, error) {
 101  	type alias DatabaseSettingSourceNetworks
 102  	dest := alias(d)
 103  
 104  	if len(dest) == 0 {
 105  		dest = append(dest, "0.0.0.0/0")
 106  	}
 107  
 108  	return json.Marshal(dest)
 109  }
 110  
 111  func (d *DatabaseSettingSourceNetworks) UnmarshalJSON(b []byte) error {
 112  	if string(b) == `""` || string(b) == "" {
 113  		return nil
 114  	}
 115  	type alias DatabaseSettingSourceNetworks
 116  
 117  	var a alias
 118  	if err := json.Unmarshal(b, &a); err != nil {
 119  		return err
 120  	}
 121  	if len(a) == 1 && a[0] == "0.0.0.0/0" {
 122  		return nil
 123  	}
 124  	*d = DatabaseSettingSourceNetworks(a)
 125  	return nil
 126  }
 127  
 128  // DatabaseSettingBackup データベース設定 バックアップ設定
 129  type DatabaseSettingBackup struct {
 130  	Rotate    int                   `json:",omitempty" yaml:"rotate,omitempty" structs:",omitempty"`
 131  	Time      string                `json:",omitempty" yaml:"time,omitempty" structs:",omitempty"`
 132  	DayOfWeek []types.EDayOfTheWeek `json:",omitempty" yaml:"day_of_week,omitempty" structs:",omitempty"`
 133  	Connect   string                `json:",omitempty" yaml:"connect,omitempty" structs:",omitempty"` // 冗長化オプション有効時のバックアップ先NFS 例:`nfs://192.168.0.41/export`
 134  }
 135  
 136  // DatabaseSettingBackupv2 データベース設定 継続的バックアップ(バックアップv2)設定
 137  type DatabaseSettingBackupv2 struct {
 138  	Rotate         int                   `json:",omitempty" yaml:"rotate,omitempty" structs:",omitempty"`
 139  	Time           string                `json:",omitempty" yaml:"time,omitempty" structs:",omitempty"`
 140  	DayOfWeek      []types.EDayOfTheWeek `json:",omitempty" yaml:"day_of_week,omitempty" structs:",omitempty"`
 141  	Connect        string                `json:",omitempty" yaml:"connect,omitempty" structs:",omitempty"` // バックアップ先NFS: 例:`nfs://192.168.0.41/export`
 142  	FirstEnabledAt *time.Time            `json:",omitempty" yaml:"first_enabled_at,omitempty" structs:",omitempty"`
 143  }
 144  
 145  // UnmarshalJSON 配列/オブジェクトが混在することへの対応
 146  func (d *DatabaseSettingBackup) UnmarshalJSON(b []byte) error {
 147  	if string(b) == "[]" {
 148  		return nil
 149  	}
 150  	type alias DatabaseSettingBackup
 151  
 152  	var a alias
 153  	if err := json.Unmarshal(b, &a); err != nil {
 154  		return err
 155  	}
 156  	*d = DatabaseSettingBackup(a)
 157  	return nil
 158  }
 159  
 160  // DatabaseSettingReplication レプリケーション設定
 161  type DatabaseSettingReplication struct {
 162  	Model     types.EDatabaseReplicationModel `json:",omitempty" yaml:"model,omitempty" structs:",omitempty"`
 163  	Appliance *struct {
 164  		ID types.ID
 165  	} `json:",omitempty" yaml:"appliance,omitempty" structs:",omitempty"`
 166  	IPAddress string `json:",omitempty" yaml:"ip_address,omitempty" structs:",omitempty"`
 167  	Port      int    `json:",omitempty" yaml:"port,omitempty" structs:",omitempty"`
 168  	User      string `json:",omitempty" yaml:"user,omitempty" structs:",omitempty"`
 169  	Password  string `json:",omitempty" yaml:"password,omitempty" structs:",omitempty"`
 170  }
 171  
 172  type DatabaseSettingInterfaces []*DatabaseSettingInterface
 173  
 174  type DatabaseSettingInterface struct {
 175  	VirtualIPAddress string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 176  	// Index 仮想フィールド、VPCルータなどでInterfaces(実体は[]*Interface)を扱う場合にUnmarshalJSONの中で設定される
 177  	//
 178  	// Findした際のAPIからの応答にも同名のフィールドが含まれるが無関係。
 179  	Index int `json:"-"`
 180  }
 181  
 182  // UnmarshalJSON 配列中にnullが返ってくる(VPCルータなど)への対応
 183  //
 184  // Note: この実装は要素として`[]`が来た場合にゼロ値として返している。
 185  //
 186  //	クライアント側では必要に応じてnil判定ではなくゼロ値である事の判定を行う。
 187  func (i *DatabaseSettingInterfaces) UnmarshalJSON(b []byte) error {
 188  	type alias DatabaseSettingInterfaces
 189  	var a alias
 190  	if err := json.Unmarshal(b, &a); err != nil {
 191  		return err
 192  	}
 193  
 194  	var dest []*DatabaseSettingInterface
 195  	for i, v := range a {
 196  		if v != nil {
 197  			if v.Index == 0 {
 198  				v.Index = i
 199  			}
 200  			dest = append(dest, v)
 201  		}
 202  	}
 203  
 204  	*i = DatabaseSettingInterfaces(dest)
 205  	return nil
 206  }
 207  
 208  // MarshalJSON 配列中にnullが入る場合(VPCルータなど)への対応
 209  func (i *DatabaseSettingInterfaces) MarshalJSON() ([]byte, error) {
 210  	max := 0
 211  	for _, iface := range *i {
 212  		if max < iface.Index {
 213  			max = iface.Index
 214  		}
 215  	}
 216  
 217  	var dest = make([]*DatabaseSettingInterface, max+1)
 218  	for _, iface := range *i {
 219  		dest[iface.Index] = iface
 220  	}
 221  
 222  	return json.Marshal(dest)
 223  }
 224  
 225  // MarshalJSON JSON
 226  func (i *DatabaseSettingInterface) MarshalJSON() ([]byte, error) {
 227  	type alias struct {
 228  		IPAddress        []string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 229  		VirtualIPAddress string   `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 230  		IPAliases        []string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 231  		NetworkMaskLen   int      `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 232  	}
 233  
 234  	tmp := alias{
 235  		VirtualIPAddress: i.VirtualIPAddress,
 236  	}
 237  	return json.Marshal(tmp)
 238  }
 239  
 240  func (i *DatabaseSettingInterface) UnmarshalJSON(b []byte) error {
 241  	if string(b) == "[]" {
 242  		return nil
 243  	}
 244  	type alias DatabaseSettingInterface
 245  
 246  	var a alias
 247  	if err := json.Unmarshal(b, &a); err != nil {
 248  		return err
 249  	}
 250  	*i = DatabaseSettingInterface(a)
 251  	return nil
 252  }
 253  
 254  // DatabaseStatusResponse Status APIの戻り値
 255  type DatabaseStatusResponse struct {
 256  	SettingsResponse *DatabaseStatus `json:",omitempty" yaml:"settings_response,omitempty" structs:",omitempty"`
 257  }
 258  
 259  // DatabaseStatus データベースのステータス
 260  type DatabaseStatus struct {
 261  	Status  types.EServerInstanceStatus `json:",omitempty" yaml:"status,omitempty" structs:",omitempty"`
 262  	IsFatal bool                        `json:"is_fatal"`
 263  	DBConf  *DatabaseStatusDBConf       `json:",omitempty" yaml:"db_conf,omitempty" structs:",omitempty"`
 264  }
 265  
 266  // DatabaseStatusDBConf データベース設定
 267  type DatabaseStatusDBConf struct {
 268  	Version  *DatabaseStatusVersion    `json:"version,omitempty" yaml:"version,omitempty" structs:",omitempty"`
 269  	Log      []*DatabaseLog            `json:"log,omitempty" yaml:"log,omitempty" structs:",omitempty"`
 270  	Backup   *DatabaseBackupInfo       `json:"backup,omitempty" yaml:"backup,omitempty" structs:",omitempty"`
 271  	MariaDB  *DatabaseStatusMariaDB    `json:",omitempty" yaml:"maria_db,omitempty" structs:",omitempty"`
 272  	Postgres *DatabaseStatusPostgreSQL `json:"postgres,omitempty" yaml:"postgres,omitempty" structs:",omitempty"`
 273  
 274  	// 以下フィールドはサポートしない
 275  	// Replication
 276  }
 277  
 278  type DatabaseStatusMariaDB struct {
 279  	Status string `json:"status,omitempty"`
 280  }
 281  type DatabaseStatusPostgreSQL struct {
 282  	Status string `json:"status,omitempty"`
 283  }
 284  
 285  // DatabaseStatusVersion データベース設定バージョン情報
 286  type DatabaseStatusVersion struct {
 287  	LastModified string      `json:"lastmodified,omitempty" yaml:"last_modified,omitempty" structs:",omitempty"`
 288  	CommitHash   string      `json:"commithash,omitempty" yaml:"commit_hash,omitempty" structs:",omitempty"`
 289  	Status       string      `json:"status,omitempty" yaml:"status,omitempty" structs:",omitempty"`
 290  	Tag          interface{} `json:"tag,omitempty" yaml:"tag,omitempty" structs:",omitempty"` // Note: `1.1`や`"1.1"`などと表記揺れがあるためここではinterface{}で受け取る
 291  	Expire       string      `json:"expire,omitempty" yaml:"expire,omitempty" structs:",omitempty"`
 292  }
 293  
 294  // DatabaseLog データベースログ
 295  type DatabaseLog struct {
 296  	Name string             `json:"name,omitempty" yaml:"name,omitempty" structs:",omitempty"`
 297  	Data string             `json:"data,omitempty" yaml:"data,omitempty" structs:",omitempty"`
 298  	Size types.StringNumber `json:"size,omitempty" yaml:"size,omitempty" structs:",omitempty"`
 299  }
 300  
 301  // IsSystemdLog systemctlのログか判定
 302  func (l *DatabaseLog) IsSystemdLog() bool {
 303  	return l.Name == "systemctl"
 304  }
 305  
 306  // Logs ログボディ取得
 307  func (l *DatabaseLog) Logs() []string {
 308  	return strings.Split(l.Data, "\n")
 309  }
 310  
 311  // ID ログのID取得
 312  func (l *DatabaseLog) ID() string {
 313  	return l.Name
 314  }
 315  
 316  // DatabaseBackupInfo データベースバックアップ情報
 317  type DatabaseBackupInfo struct {
 318  	History []*DatabaseBackupHistory `json:"history,omitempty" yaml:"history,omitempty" structs:",omitempty"`
 319  }
 320  
 321  // DatabaseBackupHistory データベースバックアップ履歴情報
 322  type DatabaseBackupHistory struct {
 323  	CreatedAt    time.Time  `json:"createdat,omitempty" yaml:"created_at,omitempty" structs:",omitempty"`
 324  	Availability string     `json:"availability,omitempty" yaml:"availability,omitempty" structs:",omitempty"`
 325  	RecoveredAt  *time.Time `json:"recoveredat,omitempty" yaml:"recovered_at,omitempty" structs:",omitempty"`
 326  	Size         int64      `json:"size,omitempty" yaml:"size,omitempty" structs:",omitempty"`
 327  }
 328  
 329  // ID バックアップ履歴のID取得
 330  func (h *DatabaseBackupHistory) ID() string {
 331  	return h.CreatedAt.Format(time.RFC3339)
 332  }
 333  
 334  // FormatCreatedAt 指定のレイアウトで作成日時を文字列化
 335  func (h *DatabaseBackupHistory) FormatCreatedAt(layout string) string {
 336  	return h.CreatedAt.Format(layout)
 337  }
 338  
 339  // FormatRecoveredAt 指定のレイアウトで復元日時を文字列化
 340  //
 341  // 復元日時がnilの場合は空の文字列を返す
 342  func (h *DatabaseBackupHistory) FormatRecoveredAt(layout string) string {
 343  	if h.RecoveredAt == nil {
 344  		return ""
 345  	}
 346  	return h.RecoveredAt.Format(layout)
 347  }
 348  
 349  // UnmarshalJSON JSON復号処理
 350  func (h *DatabaseBackupHistory) UnmarshalJSON(data []byte) error {
 351  	var tmpMap = map[string]interface{}{}
 352  	if err := json.Unmarshal(data, &tmpMap); err != nil {
 353  		return err
 354  	}
 355  
 356  	if recoveredAt, ok := tmpMap["recoveredat"]; ok {
 357  		if strRecoveredAt, ok := recoveredAt.(string); ok {
 358  			if _, err := time.Parse(time.RFC3339, strRecoveredAt); err != nil {
 359  				tmpMap["recoveredat"] = nil
 360  			}
 361  		}
 362  	}
 363  
 364  	data, err := json.Marshal(tmpMap)
 365  	if err != nil {
 366  		return err
 367  	}
 368  
 369  	tmp := &struct {
 370  		CreatedAt    time.Time  `json:"createdat,omitempty"`
 371  		Availability string     `json:"availability,omitempty"`
 372  		RecoveredAt  *time.Time `json:"recoveredat,omitempty"`
 373  		Size         string     `json:"size,omitempty"`
 374  	}{}
 375  	if err := json.Unmarshal(data, &tmp); err != nil {
 376  		return err
 377  	}
 378  
 379  	h.CreatedAt = tmp.CreatedAt
 380  	h.Availability = tmp.Availability
 381  	h.RecoveredAt = tmp.RecoveredAt
 382  	s, err := strconv.ParseInt(tmp.Size, 10, 64)
 383  	if err == nil {
 384  		h.Size = s
 385  	} else {
 386  		return err
 387  	}
 388  
 389  	return nil
 390  }
 391  
 392  // DatabaseParameter RDBMSごとに固有のパラメータ設定
 393  type DatabaseParameter struct {
 394  	Parameter *DatabaseParameterSetting `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 395  	Remark    *DatabaseParameterRemark  `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
 396  }
 397  
 398  type DatabaseParameterSetting struct {
 399  	NoteID types.ID                     `json:",omitempty" yaml:"note_id,omitempty" structs:",omitempty"`
 400  	Attr   DatabaseParameterSettingAttr `json:",omitempty" yaml:"attr,omitempty" structs:",omitempty"`
 401  }
 402  
 403  type DatabaseParameterSettingAttr map[string]interface{}
 404  
 405  // UnmarshalJSON 配列/オブジェクトが混在することへの対応
 406  func (d *DatabaseParameterSettingAttr) UnmarshalJSON(b []byte) error {
 407  	if string(b) == "[]" {
 408  		return nil
 409  	}
 410  	type alias map[string]interface{}
 411  
 412  	var a alias
 413  	if err := json.Unmarshal(b, &a); err != nil {
 414  		return err
 415  	}
 416  	*d = DatabaseParameterSettingAttr(a)
 417  	return nil
 418  }
 419  
 420  type DatabaseParameterRemark struct {
 421  	Settings []interface{}                // どのような値が入るのか不明
 422  	Form     []*DatabaseParameterFormMeta `json:",omitempty" yaml:"form,omitempty" structs:",omitempty"`
 423  }
 424  
 425  type DatabaseParameterFormMeta struct {
 426  	Type    string                            `json:"type" yaml:"yaml"`
 427  	Name    string                            `json:"name" yaml:"name"`
 428  	Label   string                            `json:"label" yaml:"label"`
 429  	Options *DatabaseParameterFormMetaOptions `json:"options" yaml:"options"`
 430  	Items   [][]interface{}                   `json:"items,omitempty" yaml:"items,omitempty" structs:",omitempty"` // 例: [["value1", "text1"],[ "value2", "text2"]] ※ valueは数値となる可能性がある
 431  }
 432  
 433  type DatabaseParameterFormMetaOptions struct {
 434  	Validator string  `json:"validator" yaml:"validator"`
 435  	Example   string  `json:"ex" yaml:"ex"`
 436  	Min       float64 `json:"min" yaml:"min"`
 437  	Max       float64 `json:"max" yaml:"max"`
 438  	MaxLen    int     `json:"maxlen" yaml:"maxlen"`
 439  	Text      string  `json:"text" yaml:"text"`
 440  	Reboot    string  `json:"reboot" yaml:"reboot"`
 441  	Type      string  `json:"type" yaml:"type"`
 442  	Integer   bool    `json:"integer" yaml:"integer"` // postgres用のパラメータにだけ存在する模様
 443  }
 444