go.temporal.io/server@v1.23.0/common/namespace/archival_config_state_machine.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package namespace 26 27 import ( 28 enumspb "go.temporal.io/api/enums/v1" 29 "go.temporal.io/api/serviceerror" 30 ) 31 32 // namespaceArchivalConfigStateMachine is only used by namespaceHandler. 33 // It is simply meant to simplify the logic around archival namespace state changes. 34 // Logically this class can be thought of as part of namespaceHandler. 35 36 type ( 37 // ArchivalConfigState represents the state of archival config 38 // the only invalid state is {URI="", state=enabled} 39 // once URI is set it is immutable 40 ArchivalConfigState struct { 41 State enumspb.ArchivalState 42 URI string 43 } 44 45 // ArchivalConfigEvent represents a change request to archival config state 46 // the only restriction placed on events is that defaultURI is not empty 47 // state can be nil, enabled, or disabled (nil indicates no update by user is being attempted) 48 ArchivalConfigEvent struct { 49 DefaultURI string 50 URI string 51 State enumspb.ArchivalState 52 } 53 ) 54 55 // the following errors represents impossible code states that should never occur 56 var ( 57 errInvalidState = serviceerror.NewInvalidArgument("Encountered illegal state: archival is enabled but URI is not set (should be impossible)") 58 errInvalidEvent = serviceerror.NewInvalidArgument("Encountered illegal event: default URI is not set (should be impossible)") 59 errCannotHandleStateChange = serviceerror.NewInvalidArgument("Encountered current state and event that cannot be handled (should be impossible)") 60 errURIUpdate = serviceerror.NewInvalidArgument("Cannot update existing archival URI") 61 ) 62 63 func NeverEnabledState() *ArchivalConfigState { 64 return &ArchivalConfigState{ 65 URI: "", 66 State: enumspb.ARCHIVAL_STATE_DISABLED, 67 } 68 } 69 70 func (e *ArchivalConfigEvent) Validate() error { 71 if len(e.DefaultURI) == 0 { 72 return errInvalidEvent 73 } 74 return nil 75 } 76 77 func (s *ArchivalConfigState) validate() error { 78 if s.State == enumspb.ARCHIVAL_STATE_ENABLED && len(s.URI) == 0 { 79 return errInvalidState 80 } 81 return nil 82 } 83 84 func (s *ArchivalConfigState) GetNextState( 85 e *ArchivalConfigEvent, 86 URIValidationFunc func(URI string) error, 87 ) (nextState *ArchivalConfigState, changed bool, err error) { 88 defer func() { 89 // ensure that any existing URI name was not mutated 90 if nextState != nil && len(s.URI) != 0 && s.URI != nextState.URI { 91 nextState = nil 92 changed = false 93 err = errCannotHandleStateChange 94 return 95 } 96 97 // ensure that next state is valid 98 if nextState != nil { 99 if nextStateErr := nextState.validate(); nextStateErr != nil { 100 nextState = nil 101 changed = false 102 err = nextStateErr 103 return 104 } 105 } 106 107 if nextState != nil && nextState.URI != "" { 108 if validateURIErr := URIValidationFunc(nextState.URI); validateURIErr != nil { 109 nextState = nil 110 changed = false 111 err = validateURIErr 112 return 113 } 114 } 115 }() 116 117 if s == nil || e == nil { 118 return nil, false, errCannotHandleStateChange 119 } 120 if err := s.validate(); err != nil { 121 return nil, false, err 122 } 123 if err := e.Validate(); err != nil { 124 return nil, false, err 125 } 126 127 /** 128 At this point state and event are both non-nil and valid. 129 130 State can be any one of the following: 131 {state=enabled, URI="foo"} 132 {state=disabled, URI="foo"} 133 {state=disabled, URI=""} 134 135 Event can be any one of the following: 136 {state=enabled, URI="foo", defaultURI="bar"} 137 {state=enabled, URI="", defaultURI="bar"} 138 {state=disabled, URI="foo", defaultURI="bar"} 139 {state=disabled, URI="", defaultURI="bar"} 140 {state=nil, URI="foo", defaultURI="bar"} 141 {state=nil, URI="", defaultURI="bar"} 142 */ 143 144 stateURISet := len(s.URI) != 0 145 eventURISet := len(e.URI) != 0 146 147 // factor this case out to ensure that URI is immutable 148 if stateURISet && eventURISet && s.URI != e.URI { 149 return nil, false, errURIUpdate 150 } 151 152 // state 1 153 if s.State == enumspb.ARCHIVAL_STATE_ENABLED && stateURISet { 154 if e.State == enumspb.ARCHIVAL_STATE_ENABLED && eventURISet { 155 return s, false, nil 156 } 157 if e.State == enumspb.ARCHIVAL_STATE_ENABLED && !eventURISet { 158 return s, false, nil 159 } 160 if e.State == enumspb.ARCHIVAL_STATE_DISABLED && eventURISet { 161 return &ArchivalConfigState{ 162 State: enumspb.ARCHIVAL_STATE_DISABLED, 163 URI: s.URI, 164 }, true, nil 165 } 166 if e.State == enumspb.ARCHIVAL_STATE_DISABLED && !eventURISet { 167 return &ArchivalConfigState{ 168 State: enumspb.ARCHIVAL_STATE_DISABLED, 169 URI: s.URI, 170 }, true, nil 171 } 172 if e.State == enumspb.ARCHIVAL_STATE_UNSPECIFIED && eventURISet { 173 return s, false, nil 174 } 175 if e.State == enumspb.ARCHIVAL_STATE_UNSPECIFIED && !eventURISet { 176 return s, false, nil 177 } 178 } 179 180 // state 2 181 if s.State == enumspb.ARCHIVAL_STATE_DISABLED && stateURISet { 182 if e.State == enumspb.ARCHIVAL_STATE_ENABLED && eventURISet { 183 return &ArchivalConfigState{ 184 URI: s.URI, 185 State: enumspb.ARCHIVAL_STATE_ENABLED, 186 }, true, nil 187 } 188 if e.State == enumspb.ARCHIVAL_STATE_ENABLED && !eventURISet { 189 return &ArchivalConfigState{ 190 State: enumspb.ARCHIVAL_STATE_ENABLED, 191 URI: s.URI, 192 }, true, nil 193 } 194 if e.State == enumspb.ARCHIVAL_STATE_DISABLED && eventURISet { 195 return s, false, nil 196 } 197 if e.State == enumspb.ARCHIVAL_STATE_DISABLED && !eventURISet { 198 return s, false, nil 199 } 200 if e.State == enumspb.ARCHIVAL_STATE_UNSPECIFIED && eventURISet { 201 return s, false, nil 202 } 203 if e.State == enumspb.ARCHIVAL_STATE_UNSPECIFIED && !eventURISet { 204 return s, false, nil 205 } 206 } 207 208 // state 3 209 if s.State == enumspb.ARCHIVAL_STATE_DISABLED && !stateURISet { 210 if e.State == enumspb.ARCHIVAL_STATE_ENABLED && eventURISet { 211 return &ArchivalConfigState{ 212 State: enumspb.ARCHIVAL_STATE_ENABLED, 213 URI: e.URI, 214 }, true, nil 215 } 216 if e.State == enumspb.ARCHIVAL_STATE_ENABLED && !eventURISet { 217 return &ArchivalConfigState{ 218 State: enumspb.ARCHIVAL_STATE_ENABLED, 219 URI: e.DefaultURI, 220 }, true, nil 221 } 222 if e.State == enumspb.ARCHIVAL_STATE_DISABLED && eventURISet { 223 return &ArchivalConfigState{ 224 State: enumspb.ARCHIVAL_STATE_DISABLED, 225 URI: e.URI, 226 }, true, nil 227 } 228 if e.State == enumspb.ARCHIVAL_STATE_DISABLED && !eventURISet { 229 return s, false, nil 230 } 231 if e.State == enumspb.ARCHIVAL_STATE_UNSPECIFIED && eventURISet { 232 return &ArchivalConfigState{ 233 State: enumspb.ARCHIVAL_STATE_DISABLED, 234 URI: e.URI, 235 }, true, nil 236 } 237 if e.State == enumspb.ARCHIVAL_STATE_UNSPECIFIED && !eventURISet { 238 return s, false, nil 239 } 240 } 241 return nil, false, errCannotHandleStateChange 242 }