1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/platform/basic.h"
32 
33 #include "mongo/s/chunk_version.h"
34 
35 #include "mongo/base/status_with.h"
36 #include "mongo/bson/bsonobj.h"
37 #include "mongo/bson/bsonobjbuilder.h"
38 #include "mongo/bson/util/bson_extract.h"
39 #include "mongo/util/mongoutils/str.h"
40 
41 namespace mongo {
42 namespace {
43 
44 const char kVersion[] = "version";
45 const char kLastmod[] = "lastmod";
46 
47 }  // namespace
48 
49 const char ChunkVersion::kShardVersionField[] = "shardVersion";
50 
parseFromBSONForCommands(const BSONObj & obj)51 StatusWith<ChunkVersion> ChunkVersion::parseFromBSONForCommands(const BSONObj& obj) {
52     return parseFromBSONWithFieldForCommands(obj, kShardVersionField);
53 }
54 
parseFromBSONWithFieldForCommands(const BSONObj & obj,StringData field)55 StatusWith<ChunkVersion> ChunkVersion::parseFromBSONWithFieldForCommands(const BSONObj& obj,
56                                                                          StringData field) {
57     BSONElement versionElem;
58     Status status = bsonExtractField(obj, field, &versionElem);
59     if (!status.isOK())
60         return status;
61 
62     if (versionElem.type() != Array) {
63         return {ErrorCodes::TypeMismatch,
64                 str::stream() << "Invalid type " << versionElem.type()
65                               << " for shardVersion element. Expected an array"};
66     }
67 
68     BSONObjIterator it(versionElem.Obj());
69     if (!it.more())
70         return {ErrorCodes::BadValue, "Unexpected empty version"};
71 
72     ChunkVersion version;
73 
74     // Expect the timestamp
75     {
76         BSONElement tsPart = it.next();
77         if (tsPart.type() != bsonTimestamp)
78             return {ErrorCodes::TypeMismatch,
79                     str::stream() << "Invalid type " << tsPart.type()
80                                   << " for version timestamp part."};
81 
82         version._combined = tsPart.timestamp().asULL();
83     }
84 
85     // Expect the epoch OID
86     {
87         BSONElement epochPart = it.next();
88         if (epochPart.type() != jstOID)
89             return {ErrorCodes::TypeMismatch,
90                     str::stream() << "Invalid type " << epochPart.type()
91                                   << " for version epoch part."};
92 
93         version._epoch = epochPart.OID();
94     }
95 
96     return version;
97 }
98 
parseFromBSONForSetShardVersion(const BSONObj & obj)99 StatusWith<ChunkVersion> ChunkVersion::parseFromBSONForSetShardVersion(const BSONObj& obj) {
100     bool canParse;
101     const ChunkVersion chunkVersion = ChunkVersion::fromBSON(obj, kVersion, &canParse);
102     if (!canParse)
103         return {ErrorCodes::BadValue, "Unable to parse shard version"};
104 
105     return chunkVersion;
106 }
107 
parseFromBSONForChunk(const BSONObj & obj)108 StatusWith<ChunkVersion> ChunkVersion::parseFromBSONForChunk(const BSONObj& obj) {
109     bool canParse;
110     const ChunkVersion chunkVersion = ChunkVersion::fromBSON(obj, kLastmod, &canParse);
111     if (!canParse)
112         return {ErrorCodes::BadValue, "Unable to parse shard version"};
113 
114     return chunkVersion;
115 }
116 
parseFromBSONWithFieldAndSetEpoch(const BSONObj & obj,StringData field,const OID & epoch)117 StatusWith<ChunkVersion> ChunkVersion::parseFromBSONWithFieldAndSetEpoch(const BSONObj& obj,
118                                                                          StringData field,
119                                                                          const OID& epoch) {
120     bool canParse;
121     ChunkVersion chunkVersion = ChunkVersion::fromBSON(obj, field.toString(), &canParse);
122     if (!canParse)
123         return {ErrorCodes::BadValue, "Unable to parse shard version"};
124     chunkVersion._epoch = epoch;
125     return chunkVersion;
126 }
127 
appendForSetShardVersion(BSONObjBuilder * builder) const128 void ChunkVersion::appendForSetShardVersion(BSONObjBuilder* builder) const {
129     addToBSON(*builder, kVersion);
130 }
131 
appendForCommands(BSONObjBuilder * builder) const132 void ChunkVersion::appendForCommands(BSONObjBuilder* builder) const {
133     appendWithFieldForCommands(builder, kShardVersionField);
134 }
135 
appendWithFieldForCommands(BSONObjBuilder * builder,StringData field) const136 void ChunkVersion::appendWithFieldForCommands(BSONObjBuilder* builder, StringData field) const {
137     builder->appendArray(field, toBSON());
138 }
139 
appendForChunk(BSONObjBuilder * builder) const140 void ChunkVersion::appendForChunk(BSONObjBuilder* builder) const {
141     addToBSON(*builder, kLastmod);
142 }
143 
toBSON() const144 BSONObj ChunkVersion::toBSON() const {
145     BSONArrayBuilder b;
146     b.appendTimestamp(_combined);
147     b.append(_epoch);
148     return b.arr();
149 }
150 
151 }  // namespace mongo
152