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  }