1// Copyright (c) 2013 Couchbase, Inc.
2// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3// except in compliance with the License. You may obtain a copy of the License at
4//   http://www.apache.org/licenses/LICENSE-2.0
5// Unless required by applicable law or agreed to in writing, software distributed under the
6// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7// either express or implied. See the License for the specific language governing permissions
8// and limitations under the License.
9
10package metadata
11
12import (
13	"fmt"
14	"github.com/couchbase/goxdcr/base"
15	"reflect"
16	"strings"
17)
18
19/************************************
20/* struct ReplicationSpecification
21*************************************/
22type ReplicationSpecification struct {
23	//id of the replication
24	Id string `json:"id"`
25
26	// internal id, used to detect the case when replication spec has been deleted and recreated
27	InternalId string `json:"internalId"`
28
29	// Source Bucket Name
30	SourceBucketName string `json:"sourceBucketName"`
31
32	//Source Bucket UUID
33	SourceBucketUUID string `json:"sourceBucketUUID"`
34
35	//Target Cluster UUID
36	TargetClusterUUID string `json:"targetClusterUUID"`
37
38	// Target Bucket Name
39	TargetBucketName string `json:"targetBucketName"`
40
41	TargetBucketUUID string `json:"targetBucketUUID"`
42
43	Settings *ReplicationSettings `json:"replicationSettings"`
44
45	// revision number to be used by metadata service. not included in json
46	Revision interface{}
47}
48
49func NewReplicationSpecification(sourceBucketName string, sourceBucketUUID string, targetClusterUUID string, targetBucketName string, targetBucketUUID string) (*ReplicationSpecification, error) {
50	randId, err := base.GenerateRandomId(base.LengthOfRandomId, base.MaxRetryForRandomIdGeneration)
51	if err != nil {
52		return nil, err
53	}
54	return &ReplicationSpecification{Id: ReplicationId(sourceBucketName, targetClusterUUID, targetBucketName),
55		InternalId:        randId,
56		SourceBucketName:  sourceBucketName,
57		SourceBucketUUID:  sourceBucketUUID,
58		TargetClusterUUID: targetClusterUUID,
59		TargetBucketName:  targetBucketName,
60		TargetBucketUUID:  targetBucketUUID,
61		Settings:          DefaultSettings()}, nil
62}
63
64// checks if the passed in spec is the same as the current spec
65// used to check if a spec in cache needs to be refreshed
66func (spec *ReplicationSpecification) SameSpec(spec2 *ReplicationSpecification) bool {
67	if spec == nil {
68		return spec2 == nil
69	}
70	if spec2 == nil {
71		return false
72	}
73	// note that settings in spec are not compared. The assumption is that if settings are different, Revision will have to be different
74	return spec.Id == spec2.Id && spec.InternalId == spec2.InternalId &&
75		spec.SourceBucketName == spec2.SourceBucketName &&
76		spec.SourceBucketUUID == spec2.SourceBucketUUID &&
77		spec.TargetClusterUUID == spec2.TargetClusterUUID && spec.TargetBucketName == spec2.TargetBucketName &&
78		spec.TargetBucketUUID == spec2.TargetBucketUUID && reflect.DeepEqual(spec.Revision, spec2.Revision)
79}
80
81func (spec *ReplicationSpecification) Clone() *ReplicationSpecification {
82	if spec == nil {
83		return nil
84	}
85	return &ReplicationSpecification{Id: spec.Id,
86		InternalId:        spec.InternalId,
87		SourceBucketName:  spec.SourceBucketName,
88		SourceBucketUUID:  spec.SourceBucketUUID,
89		TargetClusterUUID: spec.TargetClusterUUID,
90		TargetBucketName:  spec.TargetBucketName,
91		TargetBucketUUID:  spec.TargetBucketUUID,
92		Settings:          spec.Settings.Clone(),
93		// !!! shallow copy of revision.
94		// spec.Revision should only be passed along and should never be modified
95		Revision: spec.Revision}
96}
97
98func (spec *ReplicationSpecification) Redact() *ReplicationSpecification {
99	if spec != nil {
100		// Currently only the Settings has user identifiable data in filtered expression
101		spec.Settings.Redact()
102	}
103	return spec
104}
105
106func (spec *ReplicationSpecification) CloneAndRedact() *ReplicationSpecification {
107	if spec != nil {
108		return spec.Clone().Redact()
109	}
110	return spec
111}
112
113func ReplicationId(sourceBucketName string, targetClusterUUID string, targetBucketName string) string {
114	parts := []string{targetClusterUUID, sourceBucketName, targetBucketName}
115	return strings.Join(parts, base.KeyPartsDelimiter)
116}
117
118func IsReplicationIdForSourceBucket(replicationId string, sourceBucketName string) (bool, error) {
119	replBucketName, err := GetSourceBucketNameFromReplicationId(replicationId)
120	if err != nil {
121		return false, err
122	} else {
123		return replBucketName == sourceBucketName, nil
124	}
125}
126
127func IsReplicationIdForTargetBucket(replicationId string, targetBucketName string) (bool, error) {
128	replBucketName, err := GetTargetBucketNameFromReplicationId(replicationId)
129	if err != nil {
130		return false, err
131	} else {
132		return replBucketName == targetBucketName, nil
133	}
134}
135
136func GetSourceBucketNameFromReplicationId(replicationId string) (string, error) {
137	parts := strings.Split(replicationId, base.KeyPartsDelimiter)
138	if len(parts) == 3 {
139		return parts[1], nil
140	} else {
141		return "", fmt.Errorf("Invalid replication id: %v", replicationId)
142	}
143}
144
145func GetTargetBucketNameFromReplicationId(replicationId string) (string, error) {
146	parts := strings.Split(replicationId, base.KeyPartsDelimiter)
147	if len(parts) == 3 {
148		return parts[2], nil
149	} else {
150		return "", fmt.Errorf("Invalid replication id: %v", replicationId)
151	}
152}
153