github.com/openshift-online/ocm-sdk-go@v0.1.473/helpers/helpers.go (about) 1 /* 2 Copyright (c) 2020 Red Hat, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // IMPORTANT: This file has been generated automatically, refrain from modifying it manually as all 18 // your changes will be lost when the file is generated again. 19 20 package helpers // github.com/openshift-online/ocm-sdk-go/helpers 21 22 import ( 23 "context" 24 "fmt" 25 "net/http" 26 "net/url" 27 "strings" 28 "time" 29 ) 30 31 // AddValue creates the given set of query parameters if needed, an then adds 32 // the given parameter. 33 func AddValue(query *url.Values, name string, value interface{}) { 34 if *query == nil { 35 *query = make(url.Values) 36 } 37 var text string 38 switch typed := value.(type) { 39 case time.Time: 40 text = typed.UTC().Format(time.RFC3339) 41 default: 42 text = fmt.Sprintf("%v", value) 43 } 44 query.Add(name, text) 45 } 46 47 // CopyQuery creates a copy of the given set of query parameters. 48 func CopyQuery(query url.Values) url.Values { 49 if query == nil { 50 return nil 51 } 52 result := make(url.Values) 53 for name, values := range query { 54 result[name] = CopyValues(values) 55 } 56 return result 57 } 58 59 // AddHeader creates the given set of headers if needed, and then adds the given 60 // header: 61 func AddHeader(header *http.Header, name string, value interface{}) { 62 if *header == nil { 63 *header = make(http.Header) 64 } 65 header.Add(name, fmt.Sprintf("%v", value)) 66 } 67 68 // CopyHeader creates a copy of the given set of headers. 69 func CopyHeader(header http.Header) http.Header { 70 result := make(http.Header) 71 for name, values := range header { 72 result[name] = CopyValues(values) 73 } 74 return result 75 } 76 77 const impersonateUserHeader = "Impersonate-User" 78 79 func AddImpersonationHeader(header *http.Header, user string) { 80 AddHeader(header, impersonateUserHeader, user) 81 } 82 83 // CopyValues copies a slice of strings. 84 func CopyValues(values []string) []string { 85 if values == nil { 86 return nil 87 } 88 result := make([]string, len(values)) 89 copy(result, values) 90 return result 91 } 92 93 // Segments calculates the path segments for the given path. 94 func Segments(path string) []string { 95 for strings.HasPrefix(path, "/") { 96 path = path[1:] 97 } 98 for strings.HasSuffix(path, "/") { 99 path = path[0 : len(path)-1] 100 } 101 return strings.Split(path, "/") 102 } 103 104 // PollContext repeatedly executes a task till it returns one of the given statuses and till the result 105 // satisfies all the given predicates. 106 func PollContext( 107 ctx context.Context, 108 interval time.Duration, 109 statuses []int, 110 predicates []func(interface{}) bool, 111 task func(context.Context) (int, interface{}, error), 112 ) (result interface{}, err error) { 113 // Check the deadline: 114 deadline, ok := ctx.Deadline() 115 if !ok { 116 err = fmt.Errorf("context deadline is mandatory") 117 return 118 } 119 120 // Check the interval: 121 if interval <= 0 { 122 err = fmt.Errorf("interval must be greater than zero") 123 return 124 } 125 126 // Create a cancellable context so that we can explicitly cancel it when we know that the next 127 // iteration of the loop will be after the deadline: 128 ctx, cancel := context.WithCancel(ctx) 129 defer cancel() 130 131 // If no expected status has been explicitly specified then add the default: 132 if len(statuses) == 0 { 133 statuses = []int{http.StatusOK} 134 } 135 for { 136 // Execute the task. If this produces an error and the status code is zero it means that 137 // there was an error like a timeout, or a low level communications problem. In that 138 // case we want to immediately stop waiting. 139 var status int 140 status, result, err = task(ctx) 141 if err != nil && status == 0 { 142 break 143 } 144 145 // Evaluate the status and the predicates: 146 statusOK := evalStatus(statuses, status) 147 predicatesOK := evalPredicates(predicates, result) 148 if statusOK && predicatesOK { 149 break 150 } 151 152 // If either the status or the predicates aren't acceptable then we need to check if we 153 // have enough time for another iteration before the deadline: 154 if time.Now().Add(interval).After(deadline) { 155 cancel() 156 break 157 } 158 time.Sleep(interval) 159 } 160 return 161 } 162 163 // evalStatus checks if the actual status is one of the expected ones. 164 func evalStatus(expected []int, actual int) bool { 165 for _, current := range expected { 166 if actual == current { 167 return true 168 } 169 } 170 return false 171 } 172 173 // evalPredicates checks if the object satisfies all the predicates. 174 func evalPredicates(predicates []func(interface{}) bool, object interface{}) bool { 175 if len(predicates) > 0 && object == nil { 176 return false 177 } 178 for _, predicate := range predicates { 179 if !predicate(object) { 180 return false 181 } 182 } 183 return true 184 }