Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package config | |
| 6 | |
| 7 import ( | |
| 8 "encoding/json" | |
| 9 "net/url" | |
| 10 | |
| 11 "github.com/luci/luci-go/common/config" | |
| 12 "github.com/luci/luci-go/common/errors" | |
| 13 | |
| 14 "golang.org/x/net/context" | |
| 15 ) | |
| 16 | |
| 17 // ErrNoConfig is a sentinel error returned by Get when the requested | |
| 18 // configuration is not found. | |
|
iannucci
2017/01/07 20:12:16
I would mention that it's an alias of config.ErrNo
dnj
2017/01/10 03:25:57
Done.
| |
| 19 var ErrNoConfig = config.ErrNoConfig | |
| 20 | |
| 21 // GetAllType is the type of configuration to retrieve with GetAll. | |
| 22 // | |
| 23 // GetAllType marshals/unmarshals to/from a compact JSON representation. This is | |
| 24 // used by the caching layer. | |
| 25 type GetAllType string | |
| 26 | |
| 27 const ( | |
| 28 // Project indicates that project configus should be retrieved. | |
| 29 Project = GetAllType("Project") | |
| 30 // Ref indicates that ref configs should be retrieved. | |
| 31 Ref = GetAllType("Ref") | |
| 32 ) | |
| 33 | |
| 34 // MarshalJSON implements json.Marshaler. | |
| 35 func (gat GetAllType) MarshalJSON() ([]byte, error) { | |
| 36 switch gat { | |
| 37 case Project: | |
| 38 return json.Marshal("P") | |
| 39 case Ref: | |
| 40 return json.Marshal("R") | |
| 41 default: | |
| 42 return nil, errors.Reason("unknown GetAllType: %(value)v").D("va lue", gat).Err() | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 // UnmarshalJSON implements json.Unmarshaler. | |
| 47 func (gat *GetAllType) UnmarshalJSON(d []byte) error { | |
|
iannucci
2017/01/07 20:12:15
same as other *JSON methods re: constant []byte va
dnj
2017/01/10 03:25:57
Done.
| |
| 48 var v string | |
| 49 if err := json.Unmarshal(d, &v); err != nil { | |
| 50 return errors.Annotate(err).Err() | |
| 51 } | |
| 52 | |
| 53 switch v { | |
| 54 case "P": | |
| 55 *gat = Project | |
| 56 return nil | |
| 57 case "R": | |
| 58 *gat = Ref | |
| 59 return nil | |
| 60 default: | |
| 61 return errors.Reason("unknown GetAllType: %(value)q").D("value", v).Err() | |
| 62 } | |
| 63 } | |
| 64 | |
| 65 // Meta is metadata about a configuration. | |
| 66 type Meta struct { | |
| 67 // ConfigSet is the item's config set. | |
| 68 ConfigSet string | |
| 69 // Path is the item's path within its config set. | |
| 70 Path string | |
| 71 | |
| 72 // ContentHash is the content hash. | |
| 73 ContentHash string | |
| 74 // Revision is the revision string. | |
| 75 Revision string | |
| 76 } | |
| 77 | |
| 78 // ServiceURL returns the URL of the config service. | |
| 79 func ServiceURL(c context.Context) url.URL { return getBackend(c).ServiceURL(c) } | |
| 80 | |
| 81 // Get retrieves a single configuration. | |
|
iannucci
2017/01/07 20:12:16
can we call them 'configuration file's? Since from
dnj
2017/01/10 03:25:57
Done.
| |
| 82 // | |
| 83 // r, if supplied, is a MultiResolver that will load the configuration data. | |
| 84 // If nil, the configuration data will be discarded (useful if you only care | |
| 85 // about metas). | |
| 86 // | |
| 87 // meta, if not nil, will have the configuration's Meta loaded into it on | |
| 88 // success. | |
| 89 func Get(c context.Context, a Authority, configSet, path string, r Resolver, met a *Meta) error { | |
| 90 be := getBackend(c) | |
| 91 | |
| 92 params := Params{ | |
| 93 Authority: a, | |
| 94 Content: true, | |
| 95 } | |
| 96 | |
| 97 if fr, ok := r.(FormattingResolver); ok { | |
| 98 params.Format, params.FormatData = fr.Format() | |
| 99 } | |
| 100 | |
| 101 item, err := be.Get(c, configSet, path, params) | |
|
iannucci
2017/01/07 20:12:15
maybe the Params thing would be clearer if backend
dnj
2017/01/10 03:25:58
Yeah I was debating doing this. I think I was out
| |
| 102 if err != nil { | |
| 103 return err | |
| 104 } | |
| 105 | |
| 106 if meta != nil { | |
| 107 *meta = item.Meta | |
| 108 } | |
| 109 if r == nil { | |
| 110 return nil | |
| 111 } | |
| 112 return r.Resolve(item) | |
| 113 } | |
| 114 | |
| 115 // GetAll retrieves all configurations of a given type. | |
| 116 // | |
| 117 // r, if supplied, is a MultiResolver that will load the configuration data. | |
| 118 // If nil, the configuration data will be discarded (useful if you only care | |
| 119 // about metas). If the MultiResolver operates on a slice (which it probably | |
| 120 // will), each meta and/or error index will correspond to its slice index. | |
| 121 // | |
| 122 // If meta is not nil, it will be populated with a slice of *Meta entries | |
| 123 // for each loaded configuration, in the same order that r receives them. If | |
| 124 // r resolves to a slice, the indexes for each resolved slice entry and meta | |
| 125 // entry should align unless r is doing something funky. | |
| 126 // | |
| 127 // Two types of failure may happen here. A systemic failure fails to load the | |
| 128 // set of project configurations. This will be returned directly. | |
| 129 // | |
| 130 // A resolver failure happens when the configuratiokns load, but could not be | |
| 131 // resolved. In this case, any successful resolutions and "meta" will be | |
| 132 // populated and an errors.MultiError will be returned containing non-nil | |
| 133 // errors at indices whose configs failed to resolve. | |
| 134 func GetAll(c context.Context, a Authority, t GetAllType, path string, r MultiRe solver, meta *[]*Meta) error { | |
| 135 be := getBackend(c) | |
| 136 | |
| 137 params := Params{ | |
| 138 Authority: a, | |
| 139 Content: true, | |
| 140 } | |
| 141 | |
| 142 if fr, ok := r.(FormattingResolver); ok { | |
| 143 params.Format, params.FormatData = fr.Format() | |
| 144 } | |
| 145 | |
| 146 items, err := be.GetAll(c, t, path, params) | |
| 147 if err != nil { | |
| 148 return err | |
| 149 } | |
| 150 | |
| 151 // Load our metas. | |
| 152 if meta != nil { | |
| 153 metaSlice := *meta | |
| 154 if metaSlice == nil { | |
| 155 metaSlice = make([]*Meta, 0, len(items)) | |
| 156 } else { | |
| 157 // Re-use the supplied slice. | |
| 158 metaSlice = metaSlice[:0] | |
| 159 } | |
| 160 | |
| 161 for _, item := range items { | |
| 162 metaSlice = append(metaSlice, &item.Meta) | |
| 163 } | |
| 164 | |
| 165 *meta = metaSlice | |
| 166 } | |
| 167 | |
| 168 if r == nil { | |
| 169 return nil | |
| 170 } | |
| 171 | |
| 172 // Resolve our items. If any individual resolution fails, return that fa ilure | |
| 173 // as a positional error in a MultiError. | |
| 174 lme := errors.NewLazyMultiError(len(items)) | |
| 175 r.PrepareMulti(len(items)) | |
| 176 for i, it := range items { | |
| 177 if err := r.ResolveItemAt(i, it); err != nil { | |
| 178 lme.Assign(i, err) | |
| 179 } | |
| 180 } | |
| 181 return lme.Get() | |
| 182 } | |
| 183 | |
| 184 // GetConfigSetURL returns the URL where the specified ConfigSet is | |
| 185 // configured. If the config set doesn't exist, ErrNoConfig will be returned. | |
| 186 func GetConfigSetURL(c context.Context, a Authority, configSet string) (url.URL, error) { | |
| 187 return getBackend(c).ConfigSetURL(c, a, configSet) | |
| 188 } | |
| OLD | NEW |