ops_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 fake
  16  
  17  import (
  18  	"context"
  19  	"time"
  20  
  21  	"github.com/sacloud/iaas-api-go"
  22  	"github.com/sacloud/iaas-api-go/types"
  23  )
  24  
  25  // Find is fake implementation
  26  func (o *DatabaseOp) Find(ctx context.Context, zone string, conditions *iaas.FindCondition) (*iaas.DatabaseFindResult, error) {
  27  	results, _ := find(o.key, zone, conditions)
  28  	var values []*iaas.Database
  29  	for _, res := range results {
  30  		dest := &iaas.Database{}
  31  		copySameNameField(res, dest)
  32  		values = append(values, dest)
  33  	}
  34  	return &iaas.DatabaseFindResult{
  35  		Total:     len(results),
  36  		Count:     len(results),
  37  		From:      0,
  38  		Databases: values,
  39  	}, nil
  40  }
  41  
  42  // Create is fake implementation
  43  func (o *DatabaseOp) Create(ctx context.Context, zone string, param *iaas.DatabaseCreateRequest) (*iaas.Database, error) {
  44  	result := &iaas.Database{}
  45  	copySameNameField(param, result)
  46  	fill(result, fillID, fillCreatedAt)
  47  
  48  	result.Class = "database"
  49  	result.Availability = types.Availabilities.Available
  50  	if result.Conf != nil {
  51  		if result.Conf.DatabaseVersion == "" {
  52  			result.Conf.DatabaseVersion = "1"
  53  		}
  54  		if result.Conf.DatabaseRevision == "" {
  55  			result.Conf.DatabaseRevision = "1"
  56  		}
  57  	}
  58  
  59  	putDatabase(zone, result)
  60  
  61  	id := result.ID
  62  	startPowerOn(o.key, zone, func() (interface{}, error) {
  63  		return o.Read(context.Background(), zone, id)
  64  	})
  65  	return result, nil
  66  }
  67  
  68  // Read is fake implementation
  69  func (o *DatabaseOp) Read(ctx context.Context, zone string, id types.ID) (*iaas.Database, error) {
  70  	value := getDatabaseByID(zone, id)
  71  	if value == nil {
  72  		return nil, newErrorNotFound(o.key, id)
  73  	}
  74  	dest := &iaas.Database{}
  75  	copySameNameField(value, dest)
  76  	return dest, nil
  77  }
  78  
  79  // Update is fake implementation
  80  func (o *DatabaseOp) Update(ctx context.Context, zone string, id types.ID, param *iaas.DatabaseUpdateRequest) (*iaas.Database, error) {
  81  	value, err := o.Read(ctx, zone, id)
  82  	if err != nil {
  83  		return nil, err
  84  	}
  85  	copySameNameField(param, value)
  86  	fill(value, fillModifiedAt)
  87  
  88  	putDatabase(zone, value)
  89  	return value, nil
  90  }
  91  
  92  // UpdateSettings is fake implementation
  93  func (o *DatabaseOp) UpdateSettings(ctx context.Context, zone string, id types.ID, param *iaas.DatabaseUpdateSettingsRequest) (*iaas.Database, error) {
  94  	value, err := o.Read(ctx, zone, id)
  95  	if err != nil {
  96  		return nil, err
  97  	}
  98  	copySameNameField(param, value)
  99  	fill(value, fillModifiedAt)
 100  
 101  	putDatabase(zone, value)
 102  	return value, nil
 103  }
 104  
 105  // Delete is fake implementation
 106  func (o *DatabaseOp) Delete(ctx context.Context, zone string, id types.ID) error {
 107  	_, err := o.Read(ctx, zone, id)
 108  	if err != nil {
 109  		return err
 110  	}
 111  
 112  	ds().Delete(o.key, zone, id)
 113  	return nil
 114  }
 115  
 116  // Config is fake implementation
 117  func (o *DatabaseOp) Config(ctx context.Context, zone string, id types.ID) error {
 118  	_, err := o.Read(ctx, zone, id)
 119  	if err != nil {
 120  		return err
 121  	}
 122  	return nil
 123  }
 124  
 125  // Boot is fake implementation
 126  func (o *DatabaseOp) Boot(ctx context.Context, zone string, id types.ID) error {
 127  	value, err := o.Read(ctx, zone, id)
 128  	if err != nil {
 129  		return err
 130  	}
 131  	if value.InstanceStatus.IsUp() {
 132  		return newErrorConflict(o.key, id, "Boot is failed")
 133  	}
 134  
 135  	startPowerOn(o.key, zone, func() (interface{}, error) {
 136  		return o.Read(context.Background(), zone, id)
 137  	})
 138  
 139  	return err
 140  }
 141  
 142  // Shutdown is fake implementation
 143  func (o *DatabaseOp) Shutdown(ctx context.Context, zone string, id types.ID, shutdownOption *iaas.ShutdownOption) error {
 144  	value, err := o.Read(ctx, zone, id)
 145  	if err != nil {
 146  		return err
 147  	}
 148  	if !value.InstanceStatus.IsUp() {
 149  		return newErrorConflict(o.key, id, "Shutdown is failed")
 150  	}
 151  
 152  	startPowerOff(o.key, zone, func() (interface{}, error) {
 153  		return o.Read(context.Background(), zone, id)
 154  	})
 155  
 156  	return err
 157  }
 158  
 159  // Reset is fake implementation
 160  func (o *DatabaseOp) Reset(ctx context.Context, zone string, id types.ID) error {
 161  	value, err := o.Read(ctx, zone, id)
 162  	if err != nil {
 163  		return err
 164  	}
 165  	if !value.InstanceStatus.IsUp() {
 166  		return newErrorConflict(o.key, id, "Reset is failed")
 167  	}
 168  
 169  	startPowerOn(o.key, zone, func() (interface{}, error) {
 170  		return o.Read(context.Background(), zone, id)
 171  	})
 172  
 173  	return nil
 174  }
 175  
 176  // MonitorCPU is fake implementation
 177  func (o *DatabaseOp) MonitorCPU(ctx context.Context, zone string, id types.ID, condition *iaas.MonitorCondition) (*iaas.CPUTimeActivity, error) {
 178  	_, err := o.Read(ctx, zone, id)
 179  	if err != nil {
 180  		return nil, err
 181  	}
 182  
 183  	now := time.Now().Truncate(time.Second)
 184  	m := now.Minute() % 5
 185  	if m != 0 {
 186  		now.Add(time.Duration(m) * time.Minute)
 187  	}
 188  
 189  	res := &iaas.CPUTimeActivity{}
 190  	for i := 0; i < 5; i++ {
 191  		res.Values = append(res.Values, &iaas.MonitorCPUTimeValue{
 192  			Time:    now.Add(time.Duration(i*-5) * time.Minute),
 193  			CPUTime: float64(random(1000)),
 194  		})
 195  	}
 196  
 197  	return res, nil
 198  }
 199  
 200  // MonitorDisk is fake implementation
 201  func (o *DatabaseOp) MonitorDisk(ctx context.Context, zone string, id types.ID, condition *iaas.MonitorCondition) (*iaas.DiskActivity, error) {
 202  	_, err := o.Read(ctx, zone, id)
 203  	if err != nil {
 204  		return nil, err
 205  	}
 206  
 207  	now := time.Now().Truncate(time.Second)
 208  	m := now.Minute() % 5
 209  	if m != 0 {
 210  		now.Add(time.Duration(m) * time.Minute)
 211  	}
 212  
 213  	res := &iaas.DiskActivity{}
 214  	for i := 0; i < 5; i++ {
 215  		res.Values = append(res.Values, &iaas.MonitorDiskValue{
 216  			Time:  now.Add(time.Duration(i*-5) * time.Minute),
 217  			Read:  float64(random(1000)),
 218  			Write: float64(random(1000)),
 219  		})
 220  	}
 221  
 222  	return res, nil
 223  }
 224  
 225  // MonitorInterface is fake implementation
 226  func (o *DatabaseOp) MonitorInterface(ctx context.Context, zone string, id types.ID, condition *iaas.MonitorCondition) (*iaas.InterfaceActivity, error) {
 227  	_, err := o.Read(ctx, zone, id)
 228  	if err != nil {
 229  		return nil, err
 230  	}
 231  
 232  	now := time.Now().Truncate(time.Second)
 233  	m := now.Minute() % 5
 234  	if m != 0 {
 235  		now.Add(time.Duration(m) * time.Minute)
 236  	}
 237  
 238  	res := &iaas.InterfaceActivity{}
 239  	for i := 0; i < 5; i++ {
 240  		res.Values = append(res.Values, &iaas.MonitorInterfaceValue{
 241  			Time:    now.Add(time.Duration(i*-5) * time.Minute),
 242  			Receive: float64(random(1000)),
 243  			Send:    float64(random(1000)),
 244  		})
 245  	}
 246  
 247  	return res, nil
 248  }
 249  
 250  // MonitorDatabase is fake implementation
 251  func (o *DatabaseOp) MonitorDatabase(ctx context.Context, zone string, id types.ID, condition *iaas.MonitorCondition) (*iaas.DatabaseActivity, error) {
 252  	_, err := o.Read(ctx, zone, id)
 253  	if err != nil {
 254  		return nil, err
 255  	}
 256  
 257  	now := time.Now().Truncate(time.Second)
 258  	m := now.Minute() % 5
 259  	if m != 0 {
 260  		now.Add(time.Duration(m) * time.Minute)
 261  	}
 262  
 263  	res := &iaas.DatabaseActivity{}
 264  	for i := 0; i < 5; i++ {
 265  		res.Values = append(res.Values, &iaas.MonitorDatabaseValue{
 266  			Time:              now.Add(time.Duration(i*-5) * time.Minute),
 267  			TotalMemorySize:   float64(random(1000)),
 268  			UsedMemorySize:    float64(random(1000)),
 269  			TotalDisk1Size:    float64(random(1000)),
 270  			UsedDisk1Size:     float64(random(1000)),
 271  			TotalDisk2Size:    float64(random(1000)),
 272  			UsedDisk2Size:     float64(random(1000)),
 273  			BinlogUsedSizeKiB: float64(random(1000)),
 274  			DelayTimeSec:      float64(random(1000)),
 275  		})
 276  	}
 277  
 278  	return res, nil
 279  }
 280  
 281  // Status is fake implementation
 282  func (o *DatabaseOp) Status(ctx context.Context, zone string, id types.ID) (*iaas.DatabaseStatus, error) {
 283  	value, err := o.Read(ctx, zone, id)
 284  	if err != nil {
 285  		return nil, err
 286  	}
 287  
 288  	return &iaas.DatabaseStatus{
 289  		Status:        value.InstanceStatus,
 290  		IsFatal:       false,
 291  		MariaDBStatus: "running",
 292  		Version: &iaas.DatabaseVersionInfo{
 293  			LastModified: value.ModifiedAt.String(),
 294  			CommitHash:   "foobar",
 295  			Status:       "up",
 296  			Tag:          "stable",
 297  		},
 298  	}, nil
 299  }
 300  
 301  func (o *DatabaseOp) GetParameter(ctx context.Context, zone string, id types.ID) (*iaas.DatabaseParameter, error) {
 302  	v, err := o.Read(ctx, zone, id)
 303  	if err != nil {
 304  		return nil, err
 305  	}
 306  
 307  	var settings map[string]interface{}
 308  	raw := ds().Get(ResourceDatabase+"Parameter", zone, id)
 309  	if raw != nil {
 310  		settings = raw.(map[string]interface{})
 311  	}
 312  
 313  	meta := fakeDatabaseParameterMetaForMariaDB
 314  	if v.Conf.DatabaseName == "postgres" {
 315  		meta = fakeDatabaseParameterMetaForPostgreSQL
 316  	}
 317  	return &iaas.DatabaseParameter{
 318  		Settings: settings,
 319  		MetaInfo: meta,
 320  	}, nil
 321  }
 322  
 323  var (
 324  	fakeDatabaseParameterMetaForMariaDB = []*iaas.DatabaseParameterMeta{
 325  		{
 326  			Type:    "number",
 327  			Name:    "MariaDB/server.cnf/mysqld/max_connections",
 328  			Label:   "max_connections",
 329  			Text:    "同時クライアント接続の最大数を設定します。",
 330  			Example: "100",
 331  			Min:     10,
 332  			Max:     1000,
 333  			MaxLen:  0,
 334  			Reboot:  "static",
 335  		},
 336  		{
 337  			Type:    "string",
 338  			Name:    "MariaDB/server.cnf/mysqld/event_scheduler",
 339  			Label:   "event_scheduler",
 340  			Text:    "イベントスケジュールの有効無効を設定します。",
 341  			Example: "ON",
 342  			Min:     0,
 343  			Max:     0,
 344  			MaxLen:  0,
 345  			Reboot:  "dynamic",
 346  		},
 347  	}
 348  	fakeDatabaseParameterMetaForPostgreSQL = []*iaas.DatabaseParameterMeta{
 349  		{
 350  			Type:    "number",
 351  			Name:    "postgres/postgresql.conf/max_connections",
 352  			Label:   "max_connections",
 353  			Text:    "同時クライアント接続の最大数を設定します。",
 354  			Example: "100",
 355  			Min:     10,
 356  			Max:     1000,
 357  			MaxLen:  0,
 358  			Reboot:  "static",
 359  		},
 360  		{
 361  			Type:    "number",
 362  			Name:    "postgres/postgresql.conf/work_mem",
 363  			Label:   "work_mem",
 364  			Text:    "クエリワークスペースに使用するメモリの最大量を設定します。",
 365  			Example: "4096",
 366  			Min:     64,
 367  			Max:     2147483647,
 368  			MaxLen:  10,
 369  			Reboot:  "dynamic",
 370  		},
 371  	}
 372  )
 373  
 374  func (o *DatabaseOp) SetParameter(ctx context.Context, zone string, id types.ID, param map[string]interface{}) error {
 375  	_, err := o.Read(ctx, zone, id)
 376  	if err != nil {
 377  		return err
 378  	}
 379  
 380  	var settings map[string]interface{}
 381  	raw := ds().Get(ResourceDatabase+"Parameter", zone, id)
 382  	if raw != nil {
 383  		settings = raw.(map[string]interface{})
 384  	} else {
 385  		settings = make(map[string]interface{})
 386  	}
 387  	for k, v := range param {
 388  		if v == nil {
 389  			delete(settings, k)
 390  		} else {
 391  			switch v := v.(type) {
 392  			case int:
 393  				settings[k] = float64(v)
 394  			default:
 395  				settings[k] = v
 396  			}
 397  		}
 398  	}
 399  
 400  	ds().Put(ResourceDatabase+"Parameter", zone, id, settings)
 401  	return nil
 402  }
 403