github.com/sacloud/iaas-api-go@v1.12.0/fake/functions.go (about) 1 // Copyright 2022-2023 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 "encoding/json" 19 "fmt" 20 "math/rand" 21 "net/http" 22 "reflect" 23 "strings" 24 "time" 25 26 "github.com/sacloud/iaas-api-go" 27 "github.com/sacloud/iaas-api-go/accessor" 28 "github.com/sacloud/iaas-api-go/search" 29 "github.com/sacloud/iaas-api-go/types" 30 ) 31 32 func init() { 33 rand.Seed(time.Now().UnixNano()) 34 } 35 36 func random(max int) int { 37 return rand.Intn(max) //nolint:gosec 38 } 39 40 func newErrorNotFound(resourceKey string, id interface{}) error { 41 return iaas.NewAPIError("", nil, http.StatusNotFound, &iaas.APIErrorResponse{ 42 IsFatal: true, 43 Serial: "", 44 Status: "404 NotFound", 45 ErrorCode: fmt.Sprintf("%d", http.StatusNotFound), 46 ErrorMessage: fmt.Sprintf("%s[ID:%s] is not found", resourceKey, id), 47 }) 48 } 49 50 func newErrorBadRequest(resourceKey string, id interface{}, msgs ...string) error { 51 return iaas.NewAPIError("", nil, http.StatusBadRequest, &iaas.APIErrorResponse{ 52 IsFatal: true, 53 Serial: "", 54 Status: "400 BadRequest", 55 ErrorCode: fmt.Sprintf("%d", http.StatusBadRequest), 56 ErrorMessage: fmt.Sprintf("request to %s[ID:%s] is bad: %s", resourceKey, id, strings.Join(msgs, " ")), 57 }) 58 } 59 60 func newErrorConflict(resourceKey string, id interface{}, msgs ...string) error { 61 return iaas.NewAPIError("", nil, http.StatusConflict, &iaas.APIErrorResponse{ 62 IsFatal: true, 63 Serial: "", 64 Status: "409 Conflict", 65 ErrorCode: fmt.Sprintf("%d", http.StatusConflict), 66 ErrorMessage: fmt.Sprintf("request to %s[ID:%s] is conflicted: %s", resourceKey, id, strings.Join(msgs, " ")), 67 }) 68 } 69 70 func newInternalServerError(resourceKey string, id interface{}, msgs ...string) error { 71 return iaas.NewAPIError("", nil, http.StatusInternalServerError, &iaas.APIErrorResponse{ 72 IsFatal: true, 73 Serial: "", 74 Status: "500 Internal Server Error", 75 ErrorCode: fmt.Sprintf("%d", http.StatusInternalServerError), 76 ErrorMessage: fmt.Sprintf("request to %s[ID:%s] is failed: %s", resourceKey, id, strings.Join(msgs, " ")), 77 }) 78 } 79 80 func find(resourceKey, zone string, conditions *iaas.FindCondition) ([]interface{}, error) { 81 var results []interface{} 82 if conditions == nil { 83 conditions = &iaas.FindCondition{} 84 } 85 86 targets := ds().List(resourceKey, zone) 87 88 FILTER_APPLY_LOOP: 89 for i, target := range targets { 90 // count 91 if conditions.Count != 0 && len(results) >= conditions.Count { 92 break 93 } 94 95 // from 96 if i < conditions.From { 97 continue 98 } 99 100 // filter 101 for key, expression := range conditions.Filter { 102 // TODO OpGreater/OpLess is not implemented 103 if key.Op == search.OpEqual { 104 fieldName := key.String() 105 106 // only ID/Name/Tags 107 switch fieldName { 108 case "ID", "Name", "Tags.Name", "Scope", "Class": 109 exp, ok := expression.(*search.EqualExpression) 110 if !ok { 111 exp = search.OrEqual(expression) 112 } 113 114 if fieldName == "Tags.Name" { 115 var tags types.Tags 116 if v, ok := target.(accessor.Tags); ok { 117 tags = v.GetTags() 118 } 119 120 for _, cond := range exp.Conditions { 121 condTags, ok := cond.([]string) 122 if ok { 123 for _, v := range condTags { 124 exists := false 125 for _, tag := range tags { 126 if tag == v { 127 exists = true 128 } 129 } 130 if !exists { 131 continue FILTER_APPLY_LOOP 132 } 133 } 134 } 135 } 136 } else { 137 var value interface{} 138 switch fieldName { 139 case "ID": 140 if v, ok := target.(accessor.ID); ok { 141 value = v.GetID() 142 } 143 case "Name": 144 if v, ok := target.(accessor.Name); ok { 145 value = v.GetName() 146 } 147 case "Scope": 148 if v, ok := target.(accessor.Scope); ok { 149 value = v.GetScope().String() 150 } 151 case "Class": 152 if v, ok := target.(accessor.Class); ok { 153 value = v.GetClass() 154 } 155 } 156 157 switch exp.Op { 158 case search.OpAnd: 159 for _, v := range exp.Conditions { 160 v1, ok1 := v.(string) 161 v2, ok2 := value.(string) 162 if !ok1 || !ok2 || !strings.Contains(v2, v1) { 163 continue FILTER_APPLY_LOOP 164 } 165 } 166 case search.OpOr: 167 match := false 168 for _, v := range exp.Conditions { 169 if reflect.DeepEqual(value, v) { 170 match = true 171 } 172 } 173 if !match { 174 continue FILTER_APPLY_LOOP 175 } 176 } 177 } 178 } 179 } 180 } 181 182 results = append(results, target) 183 } 184 185 // TODO sort/filter/include/exclude is not implemented 186 return results, nil 187 } 188 189 func copySameNameField(source interface{}, dest interface{}) { 190 data, _ := json.Marshal(source) 191 json.Unmarshal(data, dest) //nolint 192 } 193 194 func fill(target interface{}, fillFuncs ...func(interface{})) { 195 for _, f := range fillFuncs { 196 f(target) 197 } 198 } 199 200 func fillID(target interface{}) { 201 if v, ok := target.(accessor.ID); ok { 202 id := v.GetID() 203 if id.IsEmpty() { 204 v.SetID(pool().generateID()) 205 } 206 } 207 } 208 209 func fillAvailability(target interface{}) { 210 if v, ok := target.(accessor.Availability); ok { 211 value := v.GetAvailability() 212 if value == types.Availabilities.Unknown { 213 v.SetAvailability(types.Availabilities.Available) 214 } 215 } 216 } 217 218 func fillScope(target interface{}) { 219 if v, ok := target.(accessor.Scope); ok { 220 value := v.GetScope() 221 if value == types.EScope("") { 222 v.SetScope(types.Scopes.User) 223 } 224 } 225 } 226 227 func fillDiskPlan(target interface{}) { 228 if v, ok := target.(accessor.DiskPlan); ok { 229 id := v.GetDiskPlanID() 230 switch id { 231 case types.DiskPlans.HDD: 232 v.SetDiskPlanName("標準プラン") 233 case types.DiskPlans.SSD: 234 v.SetDiskPlanName("SSDプラン") 235 } 236 v.SetDiskPlanStorageClass("iscsi9999") 237 } 238 } 239 240 func fillCreatedAt(target interface{}) { 241 if v, ok := target.(accessor.CreatedAt); ok { 242 value := v.GetCreatedAt() 243 if value.IsZero() { 244 v.SetCreatedAt(time.Now()) 245 } 246 } 247 } 248 249 func fillModifiedAt(target interface{}) { 250 if v, ok := target.(accessor.ModifiedAt); ok { 251 value := v.GetModifiedAt() 252 if value.IsZero() { 253 v.SetModifiedAt(time.Now()) 254 } 255 } 256 }