github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/config/zonepb/zone_yaml.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package zonepb 12 13 import ( 14 "fmt" 15 "runtime/debug" 16 "sort" 17 "strings" 18 19 "github.com/cockroachdb/errors" 20 "github.com/gogo/protobuf/proto" 21 "gopkg.in/yaml.v2" 22 ) 23 24 var _ yaml.Marshaler = LeasePreference{} 25 var _ yaml.Unmarshaler = &LeasePreference{} 26 27 // MarshalYAML implements yaml.Marshaler. 28 func (l LeasePreference) MarshalYAML() (interface{}, error) { 29 short := make([]string, len(l.Constraints)) 30 for i, c := range l.Constraints { 31 short[i] = c.String() 32 } 33 return short, nil 34 } 35 36 // UnmarshalYAML implements yaml.Unmarshaler. 37 func (l *LeasePreference) UnmarshalYAML(unmarshal func(interface{}) error) error { 38 var shortConstraints []string 39 if err := unmarshal(&shortConstraints); err != nil { 40 return err 41 } 42 constraints := make([]Constraint, len(shortConstraints)) 43 for i, short := range shortConstraints { 44 if err := constraints[i].FromString(short); err != nil { 45 return err 46 } 47 } 48 l.Constraints = constraints 49 return nil 50 } 51 52 var _ yaml.Marshaler = ConstraintsConjunction{} 53 var _ yaml.Unmarshaler = &ConstraintsConjunction{} 54 55 // MarshalYAML implements yaml.Marshaler. 56 func (c ConstraintsConjunction) MarshalYAML() (interface{}, error) { 57 return nil, fmt.Errorf( 58 "MarshalYAML should never be called directly on Constraints (%v): %v", c, debug.Stack()) 59 } 60 61 // UnmarshalYAML implements yaml.Marshaler. 62 func (c *ConstraintsConjunction) UnmarshalYAML(unmarshal func(interface{}) error) error { 63 return fmt.Errorf( 64 "UnmarshalYAML should never be called directly on Constraints: %v", debug.Stack()) 65 } 66 67 // ConstraintsList is an alias for a slice of Constraints that can be 68 // properly marshaled to/from YAML. 69 type ConstraintsList struct { 70 Constraints []ConstraintsConjunction 71 Inherited bool 72 } 73 74 var _ yaml.Marshaler = ConstraintsList{} 75 var _ yaml.Unmarshaler = &ConstraintsList{} 76 77 // MarshalYAML implements yaml.Marshaler. 78 // 79 // We use two different formats here, dependent on whether per-replica 80 // constraints are being used in ConstraintsList: 81 // 1. A legacy format when there are 0 or 1 Constraints and NumReplicas is 82 // zero: 83 // [c1, c2, c3] 84 // 2. A per-replica format when NumReplicas is non-zero: 85 // {"c1,c2,c3": numReplicas1, "c4,c5": numReplicas2} 86 func (c ConstraintsList) MarshalYAML() (interface{}, error) { 87 // If per-replica Constraints aren't in use, marshal everything into a list 88 // for compatibility with pre-2.0-style configs. 89 if c.Inherited || len(c.Constraints) == 0 { 90 return []string{}, nil 91 } 92 if len(c.Constraints) == 1 && c.Constraints[0].NumReplicas == 0 { 93 short := make([]string, len(c.Constraints[0].Constraints)) 94 for i, constraint := range c.Constraints[0].Constraints { 95 short[i] = constraint.String() 96 } 97 return short, nil 98 } 99 100 // Otherwise, convert into a map from Constraints to NumReplicas. 101 constraintsMap := make(map[string]int32) 102 for _, constraints := range c.Constraints { 103 short := make([]string, len(constraints.Constraints)) 104 for i, constraint := range constraints.Constraints { 105 short[i] = constraint.String() 106 } 107 constraintsMap[strings.Join(short, ",")] = constraints.NumReplicas 108 } 109 return constraintsMap, nil 110 } 111 112 // UnmarshalYAML implements yaml.Unmarshaler. 113 func (c *ConstraintsList) UnmarshalYAML(unmarshal func(interface{}) error) error { 114 // Note that we're intentionally checking for err == nil here. This handles 115 // unmarshaling the legacy Constraints format, which is just a list of 116 // strings. 117 var strs []string 118 c.Inherited = true 119 if err := unmarshal(&strs); err == nil { 120 constraints := make([]Constraint, len(strs)) 121 for i, short := range strs { 122 if err := constraints[i].FromString(short); err != nil { 123 return err 124 } 125 } 126 if len(constraints) == 0 { 127 c.Constraints = []ConstraintsConjunction{} 128 c.Inherited = false 129 } else { 130 c.Constraints = []ConstraintsConjunction{ 131 { 132 Constraints: constraints, 133 NumReplicas: 0, 134 }, 135 } 136 c.Inherited = false 137 } 138 return nil 139 } 140 141 // Otherwise, the input must be a map that can be converted to per-replica 142 // constraints. 143 constraintsMap := make(map[string]int32) 144 if err := unmarshal(&constraintsMap); err != nil { 145 return errors.New( 146 "invalid constraints format. expected an array of strings or a map of strings to ints") 147 } 148 149 constraintsList := make([]ConstraintsConjunction, 0, len(constraintsMap)) 150 for constraintsStr, numReplicas := range constraintsMap { 151 shortConstraints := strings.Split(constraintsStr, ",") 152 constraints := make([]Constraint, len(shortConstraints)) 153 for i, short := range shortConstraints { 154 if err := constraints[i].FromString(short); err != nil { 155 return err 156 } 157 } 158 constraintsList = append(constraintsList, ConstraintsConjunction{ 159 Constraints: constraints, 160 NumReplicas: numReplicas, 161 }) 162 } 163 164 // Sort the resulting list for reproducible orderings in tests. 165 sort.Slice(constraintsList, func(i, j int) bool { 166 // First, try to find which Constraints sort first alphabetically in string 167 // format, considering the shorter list lesser if they're otherwise equal. 168 for k := range constraintsList[i].Constraints { 169 if k >= len(constraintsList[j].Constraints) { 170 return false 171 } 172 lStr := constraintsList[i].Constraints[k].String() 173 rStr := constraintsList[j].Constraints[k].String() 174 if lStr < rStr { 175 return true 176 } 177 if lStr > rStr { 178 return false 179 } 180 } 181 if len(constraintsList[i].Constraints) < len(constraintsList[j].Constraints) { 182 return true 183 } 184 // If they're completely equal and the same length, go by NumReplicas. 185 return constraintsList[i].NumReplicas < constraintsList[j].NumReplicas 186 }) 187 188 c.Constraints = constraintsList 189 c.Inherited = false 190 return nil 191 } 192 193 // marshalableZoneConfig should be kept up-to-date with the real, 194 // auto-generated ZoneConfig type, but with []Constraints changed to 195 // ConstraintsList for backwards-compatible yaml marshaling and unmarshaling. 196 // We also support parsing both lease_preferences (for v2.1+) and 197 // experimental_lease_preferences (for v2.0), copying both into the same proto 198 // field as needed. 199 // 200 // TODO(a-robinson,v2.2): Remove the experimental_lease_preferences field. 201 type marshalableZoneConfig struct { 202 RangeMinBytes *int64 `json:"range_min_bytes" yaml:"range_min_bytes"` 203 RangeMaxBytes *int64 `json:"range_max_bytes" yaml:"range_max_bytes"` 204 GC *GCPolicy `json:"gc"` 205 NumReplicas *int32 `json:"num_replicas" yaml:"num_replicas"` 206 Constraints ConstraintsList `json:"constraints" yaml:"constraints,flow"` 207 LeasePreferences []LeasePreference `json:"lease_preferences" yaml:"lease_preferences,flow"` 208 ExperimentalLeasePreferences []LeasePreference `json:"experimental_lease_preferences" yaml:"experimental_lease_preferences,flow,omitempty"` 209 Subzones []Subzone `json:"subzones" yaml:"-"` 210 SubzoneSpans []SubzoneSpan `json:"subzone_spans" yaml:"-"` 211 } 212 213 func zoneConfigToMarshalable(c ZoneConfig) marshalableZoneConfig { 214 var m marshalableZoneConfig 215 if c.RangeMinBytes != nil { 216 m.RangeMinBytes = proto.Int64(*c.RangeMinBytes) 217 } 218 if c.RangeMaxBytes != nil { 219 m.RangeMaxBytes = proto.Int64(*c.RangeMaxBytes) 220 } 221 if c.GC != nil { 222 tempGC := *c.GC 223 m.GC = &tempGC 224 } 225 if c.NumReplicas != nil && *c.NumReplicas != 0 { 226 m.NumReplicas = proto.Int32(*c.NumReplicas) 227 } 228 m.Constraints = ConstraintsList{c.Constraints, c.InheritedConstraints} 229 if !c.InheritedLeasePreferences { 230 m.LeasePreferences = c.LeasePreferences 231 } 232 // We intentionally do not round-trip ExperimentalLeasePreferences. We never 233 // want to return yaml containing it. 234 m.Subzones = c.Subzones 235 m.SubzoneSpans = c.SubzoneSpans 236 return m 237 } 238 239 // zoneConfigFromMarshalable returns a ZoneConfig from the marshaled struct 240 // NOTE: The config passed in the parameter is used so we can determine keep 241 // the original value of the InheritedLeasePreferences field in the output. 242 func zoneConfigFromMarshalable(m marshalableZoneConfig, c ZoneConfig) ZoneConfig { 243 if m.RangeMinBytes != nil { 244 c.RangeMinBytes = proto.Int64(*m.RangeMinBytes) 245 } 246 if m.RangeMaxBytes != nil { 247 c.RangeMaxBytes = proto.Int64(*m.RangeMaxBytes) 248 } 249 if m.GC != nil { 250 tempGC := *m.GC 251 c.GC = &tempGC 252 } 253 if m.NumReplicas != nil { 254 c.NumReplicas = proto.Int32(*m.NumReplicas) 255 } 256 c.Constraints = m.Constraints.Constraints 257 c.InheritedConstraints = m.Constraints.Inherited 258 if m.LeasePreferences != nil { 259 c.LeasePreferences = m.LeasePreferences 260 } 261 262 // Prefer a provided m.ExperimentalLeasePreferences value over whatever is in 263 // m.LeasePreferences, since we know that m.ExperimentalLeasePreferences can 264 // only possibly come from the user-specified input, whereas 265 // m.LeasePreferences could be the old value of the field retrieved from 266 // internal storage that the user is now trying to overwrite. 267 if m.ExperimentalLeasePreferences != nil { 268 c.LeasePreferences = m.ExperimentalLeasePreferences 269 } 270 271 if m.LeasePreferences != nil || m.ExperimentalLeasePreferences != nil { 272 c.InheritedLeasePreferences = false 273 } 274 c.Subzones = m.Subzones 275 c.SubzoneSpans = m.SubzoneSpans 276 return c 277 } 278 279 var _ yaml.Marshaler = ZoneConfig{} 280 var _ yaml.Unmarshaler = &ZoneConfig{} 281 282 // MarshalYAML implements yaml.Marshaler. 283 func (c ZoneConfig) MarshalYAML() (interface{}, error) { 284 return zoneConfigToMarshalable(c), nil 285 } 286 287 // UnmarshalYAML implements yaml.Unmarshaler. 288 func (c *ZoneConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { 289 // Pre-initialize aux with the contents of c. This is important for 290 // maintaining the behavior of not overwriting existing fields unless the 291 // user provided new values for them. 292 aux := zoneConfigToMarshalable(*c) 293 if err := unmarshal(&aux); err != nil { 294 return err 295 } 296 *c = zoneConfigFromMarshalable(aux, *c) 297 return nil 298 }