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