go.temporal.io/server@v1.23.0/common/namespace/namespace.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  	"fmt"
    29  	"time"
    30  
    31  	"github.com/google/uuid"
    32  
    33  	"golang.org/x/exp/maps"
    34  
    35  	enumspb "go.temporal.io/api/enums/v1"
    36  	namespacepb "go.temporal.io/api/namespace/v1"
    37  	"go.temporal.io/api/serviceerror"
    38  	"go.temporal.io/server/api/adminservice/v1"
    39  	persistencespb "go.temporal.io/server/api/persistence/v1"
    40  	"go.temporal.io/server/common"
    41  	"go.temporal.io/server/common/persistence"
    42  	"go.temporal.io/server/common/util"
    43  )
    44  
    45  type (
    46  	// Mutation changes a Namespace "in-flight" during a Clone operation.
    47  	Mutation interface {
    48  		apply(*persistence.GetNamespaceResponse)
    49  	}
    50  
    51  	// BadBinaryError is an error type carrying additional information about
    52  	// when/why/who configured a given checksum as being bad.
    53  	BadBinaryError struct {
    54  		cksum string
    55  		info  *namespacepb.BadBinaryInfo
    56  	}
    57  
    58  	// ID is the unique identifier type for a Namespace.
    59  	ID string
    60  
    61  	// Name is a user-supplied nickname for a Namespace.
    62  	Name string
    63  
    64  	// Namespaces is a *Namespace slice
    65  	Namespaces []*Namespace
    66  
    67  	// Namespace contains the info and config for a namespace
    68  	Namespace struct {
    69  		info                        *persistencespb.NamespaceInfo
    70  		config                      *persistencespb.NamespaceConfig
    71  		replicationConfig           *persistencespb.NamespaceReplicationConfig
    72  		configVersion               int64
    73  		failoverVersion             int64
    74  		isGlobalNamespace           bool
    75  		failoverNotificationVersion int64
    76  		notificationVersion         int64
    77  
    78  		customSearchAttributesMapper CustomSearchAttributesMapper
    79  	}
    80  
    81  	CustomSearchAttributesMapper struct {
    82  		fieldToAlias map[string]string
    83  		aliasToField map[string]string
    84  	}
    85  )
    86  
    87  const (
    88  	EmptyName Name = ""
    89  	EmptyID   ID   = ""
    90  )
    91  
    92  func NewID() ID {
    93  	return ID(uuid.NewString())
    94  }
    95  
    96  func FromPersistentState(record *persistence.GetNamespaceResponse) *Namespace {
    97  	return &Namespace{
    98  		info:                        record.Namespace.Info,
    99  		config:                      record.Namespace.Config,
   100  		replicationConfig:           record.Namespace.ReplicationConfig,
   101  		configVersion:               record.Namespace.ConfigVersion,
   102  		failoverVersion:             record.Namespace.FailoverVersion,
   103  		isGlobalNamespace:           record.IsGlobalNamespace,
   104  		failoverNotificationVersion: record.Namespace.FailoverNotificationVersion,
   105  		notificationVersion:         record.NotificationVersion,
   106  		customSearchAttributesMapper: CustomSearchAttributesMapper{
   107  			fieldToAlias: record.Namespace.Config.CustomSearchAttributeAliases,
   108  			aliasToField: util.InverseMap(record.Namespace.Config.CustomSearchAttributeAliases),
   109  		},
   110  	}
   111  }
   112  
   113  func FromAdminClientApiResponse(response *adminservice.GetNamespaceResponse) *Namespace {
   114  	info := &persistencespb.NamespaceInfo{
   115  		Id:          response.GetInfo().GetId(),
   116  		Name:        response.GetInfo().GetName(),
   117  		State:       response.GetInfo().GetState(),
   118  		Description: response.GetInfo().GetDescription(),
   119  		Owner:       response.GetInfo().GetOwnerEmail(),
   120  		Data:        response.GetInfo().GetData(),
   121  	}
   122  	config := &persistencespb.NamespaceConfig{
   123  		Retention:                    response.GetConfig().GetWorkflowExecutionRetentionTtl(),
   124  		HistoryArchivalState:         response.GetConfig().GetHistoryArchivalState(),
   125  		HistoryArchivalUri:           response.GetConfig().GetHistoryArchivalUri(),
   126  		VisibilityArchivalState:      response.GetConfig().GetVisibilityArchivalState(),
   127  		VisibilityArchivalUri:        response.GetConfig().GetVisibilityArchivalUri(),
   128  		CustomSearchAttributeAliases: response.GetConfig().GetCustomSearchAttributeAliases(),
   129  	}
   130  	replicationConfig := &persistencespb.NamespaceReplicationConfig{
   131  		ActiveClusterName: response.GetReplicationConfig().GetActiveClusterName(),
   132  		State:             response.GetReplicationConfig().GetState(),
   133  		Clusters:          ConvertClusterReplicationConfigFromProto(response.GetReplicationConfig().GetClusters()),
   134  		FailoverHistory:   convertFailoverHistoryToPersistenceProto(response.GetFailoverHistory()),
   135  	}
   136  	return &Namespace{
   137  		info:              info,
   138  		config:            config,
   139  		replicationConfig: replicationConfig,
   140  		configVersion:     response.GetConfigVersion(),
   141  		failoverVersion:   response.GetFailoverVersion(),
   142  		isGlobalNamespace: response.GetIsGlobalNamespace(),
   143  	}
   144  }
   145  
   146  func (ns *Namespace) Clone(ms ...Mutation) *Namespace {
   147  	newns := *ns
   148  	r := persistence.GetNamespaceResponse{
   149  		Namespace: &persistencespb.NamespaceDetail{
   150  			Info:                        common.CloneProto(newns.info),
   151  			Config:                      common.CloneProto(newns.config),
   152  			ReplicationConfig:           common.CloneProto(newns.replicationConfig),
   153  			ConfigVersion:               newns.configVersion,
   154  			FailoverNotificationVersion: newns.failoverNotificationVersion,
   155  			FailoverVersion:             newns.failoverVersion,
   156  		},
   157  		IsGlobalNamespace:   newns.isGlobalNamespace,
   158  		NotificationVersion: newns.notificationVersion,
   159  	}
   160  	for _, m := range ms {
   161  		m.apply(&r)
   162  	}
   163  	return FromPersistentState(&r)
   164  }
   165  
   166  // VisibilityArchivalState observes the visibility archive configuration (state
   167  // and URI) for this namespace.
   168  func (ns *Namespace) VisibilityArchivalState() ArchivalConfigState {
   169  	return ArchivalConfigState{
   170  		State: ns.config.VisibilityArchivalState,
   171  		URI:   ns.config.VisibilityArchivalUri,
   172  	}
   173  }
   174  
   175  // HistoryArchivalState observes the history archive configuration (state and
   176  // URI) for this namespace.
   177  func (ns *Namespace) HistoryArchivalState() ArchivalConfigState {
   178  	return ArchivalConfigState{
   179  		State: ns.config.HistoryArchivalState,
   180  		URI:   ns.config.HistoryArchivalUri,
   181  	}
   182  }
   183  
   184  // VerifyBinaryChecksum returns an error if the provided checksum is one of this
   185  // namespace's configured bad binary checksums. The returned error (if any) will
   186  // be unwrappable as BadBinaryError.
   187  func (ns *Namespace) VerifyBinaryChecksum(cksum string) error {
   188  	badBinMap := ns.config.GetBadBinaries().GetBinaries()
   189  	if badBinMap == nil {
   190  		return nil
   191  	}
   192  	if info, ok := badBinMap[cksum]; ok {
   193  		return BadBinaryError{cksum: cksum, info: info}
   194  	}
   195  	return nil
   196  }
   197  
   198  // ID observes this namespace's permanent unique identifier in string form.
   199  func (ns *Namespace) ID() ID {
   200  	if ns.info == nil {
   201  		return ID("")
   202  	}
   203  	return ID(ns.info.Id)
   204  }
   205  
   206  // Name observes this namespace's configured name.
   207  func (ns *Namespace) Name() Name {
   208  	if ns.info == nil {
   209  		return Name("")
   210  	}
   211  	return Name(ns.info.Name)
   212  }
   213  
   214  func (ns *Namespace) State() enumspb.NamespaceState {
   215  	if ns.replicationConfig == nil {
   216  		return enumspb.NAMESPACE_STATE_UNSPECIFIED
   217  	}
   218  	return ns.info.State
   219  }
   220  
   221  func (ns *Namespace) ReplicationState() enumspb.ReplicationState {
   222  	if ns.replicationConfig == nil {
   223  		return enumspb.REPLICATION_STATE_UNSPECIFIED
   224  	}
   225  	return ns.replicationConfig.State
   226  }
   227  
   228  // ActiveClusterName observes the name of the cluster that is currently active
   229  // for this namspace.
   230  func (ns *Namespace) ActiveClusterName() string {
   231  	if ns.replicationConfig == nil {
   232  		return ""
   233  	}
   234  	return ns.replicationConfig.ActiveClusterName
   235  }
   236  
   237  // ClusterNames observes the names of the clusters to which this namespace is
   238  // replicated.
   239  func (ns *Namespace) ClusterNames() []string {
   240  	// copy slice to preserve immutability
   241  	out := make([]string, len(ns.replicationConfig.Clusters))
   242  	copy(out, ns.replicationConfig.Clusters)
   243  	return out
   244  }
   245  
   246  // IsOnCluster returns true is namespace is registered on cluster otherwise false.
   247  func (ns *Namespace) IsOnCluster(clusterName string) bool {
   248  	for _, namespaceCluster := range ns.replicationConfig.Clusters {
   249  		if namespaceCluster == clusterName {
   250  			return true
   251  		}
   252  	}
   253  	return false
   254  }
   255  
   256  // ConfigVersion return the namespace config version
   257  func (ns *Namespace) ConfigVersion() int64 {
   258  	return ns.configVersion
   259  }
   260  
   261  // FailoverVersion return the namespace failover version
   262  func (ns *Namespace) FailoverVersion() int64 {
   263  	return ns.failoverVersion
   264  }
   265  
   266  // IsGlobalNamespace returns whether the namespace is a global namespace.
   267  // Being a global namespace doesn't necessarily mean that there are multiple registered clusters for it, only that it
   268  // has a failover version. To determine whether operations should be replicated for a namespace, see ReplicationPolicy.
   269  func (ns *Namespace) IsGlobalNamespace() bool {
   270  	return ns.isGlobalNamespace
   271  }
   272  
   273  // FailoverNotificationVersion return the global notification version of when failover happened
   274  func (ns *Namespace) FailoverNotificationVersion() int64 {
   275  	return ns.failoverNotificationVersion
   276  }
   277  
   278  // NotificationVersion return the global notification version of when namespace changed
   279  func (ns *Namespace) NotificationVersion() int64 {
   280  	return ns.notificationVersion
   281  }
   282  
   283  // ActiveInCluster returns whether the namespace is active, i.e. non global
   284  // namespace or global namespace which active cluster is the provided cluster
   285  func (ns *Namespace) ActiveInCluster(clusterName string) bool {
   286  	if !ns.isGlobalNamespace {
   287  		// namespace is not a global namespace, meaning namespace is always
   288  		// "active" within each cluster
   289  		return true
   290  	}
   291  	return clusterName == ns.ActiveClusterName()
   292  }
   293  
   294  // ReplicationPolicy return the derived workflow replication policy
   295  func (ns *Namespace) ReplicationPolicy() ReplicationPolicy {
   296  	// frontend guarantee that the clusters always contains the active
   297  	// namespace, so if the # of clusters is 1 then we do not need to send out
   298  	// any events for replication
   299  	if ns.isGlobalNamespace && len(ns.replicationConfig.Clusters) > 1 {
   300  		return ReplicationPolicyMultiCluster
   301  	}
   302  	return ReplicationPolicyOneCluster
   303  }
   304  
   305  func (ns *Namespace) GetCustomData(key string) string {
   306  	if ns.info.Data == nil {
   307  		return ""
   308  	}
   309  	return ns.info.Data[key]
   310  }
   311  
   312  // Retention returns retention duration for this namespace.
   313  func (ns *Namespace) Retention() time.Duration {
   314  	if ns.config.Retention == nil {
   315  		return 0
   316  	}
   317  
   318  	return ns.config.Retention.AsDuration()
   319  }
   320  
   321  func (ns *Namespace) CustomSearchAttributesMapper() CustomSearchAttributesMapper {
   322  	return ns.customSearchAttributesMapper
   323  }
   324  
   325  // Error returns the reason associated with this bad binary.
   326  func (e BadBinaryError) Error() string {
   327  	return e.info.Reason
   328  }
   329  
   330  // Reason returns the reason associated with this bad binary.
   331  func (e BadBinaryError) Reason() string {
   332  	return e.info.Reason
   333  }
   334  
   335  // Operator returns the operator associated with this bad binary.
   336  func (e BadBinaryError) Operator() string {
   337  	return e.info.Operator
   338  }
   339  
   340  // Created returns the time at which this bad binary was declared to be bad.
   341  func (e BadBinaryError) Created() time.Time {
   342  	return e.info.CreateTime.AsTime()
   343  }
   344  
   345  // Checksum observes the binary checksum that caused this error.
   346  func (e BadBinaryError) Checksum() string {
   347  	return e.cksum
   348  }
   349  
   350  func (id ID) String() string {
   351  	return string(id)
   352  }
   353  
   354  func (id ID) IsEmpty() bool {
   355  	return id == EmptyID
   356  }
   357  
   358  func (n Name) String() string {
   359  	return string(n)
   360  }
   361  
   362  func (n Name) IsEmpty() bool {
   363  	return n == EmptyName
   364  }
   365  
   366  func (m *CustomSearchAttributesMapper) GetAlias(fieldName string, namespace string) (string, error) {
   367  	alias, ok := m.fieldToAlias[fieldName]
   368  	if !ok {
   369  		return "", serviceerror.NewInvalidArgument(
   370  			fmt.Sprintf("Namespace %s has no mapping defined for field name %s", namespace, fieldName),
   371  		)
   372  	}
   373  	return alias, nil
   374  }
   375  
   376  func (m *CustomSearchAttributesMapper) GetFieldName(alias string, namespace string) (string, error) {
   377  	fieldName, ok := m.aliasToField[alias]
   378  	if !ok {
   379  		return "", serviceerror.NewInvalidArgument(
   380  			fmt.Sprintf("Namespace %s has no mapping defined for search attribute %s", namespace, alias),
   381  		)
   382  	}
   383  	return fieldName, nil
   384  }
   385  
   386  func (m *CustomSearchAttributesMapper) FieldToAliasMap() map[string]string {
   387  	return maps.Clone(m.fieldToAlias)
   388  }