| Line |
Branch |
Exec |
Source |
| 1 |
|
|
/** |
| 2 |
|
|
* This file is part of the CernVM File System. |
| 3 |
|
|
*/ |
| 4 |
|
|
|
| 5 |
|
|
|
| 6 |
|
|
#include <unistd.h> |
| 7 |
|
|
|
| 8 |
|
|
#include <vector> |
| 9 |
|
|
|
| 10 |
|
|
#include "history_sqlite.h" |
| 11 |
|
|
#include "manifest.h" |
| 12 |
|
|
#include "publish/except.h" |
| 13 |
|
|
#include "publish/repository.h" |
| 14 |
|
|
#include "repository_tag.h" |
| 15 |
|
|
#include "sanitizer.h" |
| 16 |
|
|
#include "upload.h" |
| 17 |
|
|
#include "upload_spooler_definition.h" |
| 18 |
|
|
#include "util/logging.h" |
| 19 |
|
|
#include "util/string.h" |
| 20 |
|
|
|
| 21 |
|
|
namespace publish { |
| 22 |
|
|
|
| 23 |
|
✗ |
void Publisher::CheckTagName(const std::string &name) { |
| 24 |
|
✗ |
if (name.empty()) |
| 25 |
|
✗ |
throw EPublish("the empty string is not a valid tag name"); |
| 26 |
|
✗ |
if (name == "trunk") |
| 27 |
|
✗ |
throw EPublish("'trunk' is not allowed as a custom tag name"); |
| 28 |
|
✗ |
if (name == "trunk-previous") |
| 29 |
|
✗ |
throw EPublish("'trunk-previous' is not allowed as a custom tag name"); |
| 30 |
|
✗ |
if (!sanitizer::TagSanitizer().IsValid(name)) |
| 31 |
|
✗ |
throw EPublish("invalid tag name: " + name); |
| 32 |
|
|
} |
| 33 |
|
|
|
| 34 |
|
|
|
| 35 |
|
✗ |
void Publisher::EditTags(const std::vector<history::History::Tag> &add_tags, |
| 36 |
|
|
const std::vector<std::string> &rm_tags) { |
| 37 |
|
✗ |
if (settings_.storage().type() == upload::SpoolerDefinition::Gateway) { |
| 38 |
|
✗ |
EditTagsGateway(add_tags, rm_tags); |
| 39 |
|
✗ |
return; |
| 40 |
|
|
} |
| 41 |
|
|
|
| 42 |
|
✗ |
if (!in_transaction_.IsSet()) |
| 43 |
|
✗ |
throw EPublish("cannot edit tags outside transaction"); |
| 44 |
|
|
|
| 45 |
|
✗ |
for (unsigned i = 0; i < add_tags.size(); ++i) { |
| 46 |
|
✗ |
const std::string name = add_tags[i].name; |
| 47 |
|
✗ |
CheckTagName(name); |
| 48 |
|
✗ |
history_->Insert(add_tags[i]); |
| 49 |
|
|
} |
| 50 |
|
|
|
| 51 |
|
✗ |
for (unsigned i = 0; i < rm_tags.size(); ++i) { |
| 52 |
|
✗ |
const std::string name = rm_tags[i]; |
| 53 |
|
✗ |
CheckTagName(name); |
| 54 |
|
✗ |
if (history_->Exists(name)) { |
| 55 |
|
✗ |
const bool retval = history_->Remove(name); |
| 56 |
|
✗ |
if (!retval) |
| 57 |
|
✗ |
throw EPublish("cannot remove tag " + name); |
| 58 |
|
|
} |
| 59 |
|
|
} |
| 60 |
|
|
|
| 61 |
|
✗ |
PushHistory(); |
| 62 |
|
|
|
| 63 |
|
|
// TODO(jblomer): virtual catalog |
| 64 |
|
|
} |
| 65 |
|
|
|
| 66 |
|
|
|
| 67 |
|
✗ |
void Publisher::EditTagsGateway( |
| 68 |
|
|
const std::vector<history::History::Tag> &add_tags, |
| 69 |
|
|
const std::vector<std::string> &rm_tags) { |
| 70 |
|
|
// Validate everything up front so we never acquire a lease for a request that |
| 71 |
|
|
// is bound to be rejected. |
| 72 |
|
✗ |
RepositoryTag repo_tag; |
| 73 |
|
✗ |
if (!rm_tags.empty()) { |
| 74 |
|
✗ |
for (unsigned i = 0; i < rm_tags.size(); ++i) |
| 75 |
|
✗ |
CheckTagName(rm_tags[i]); |
| 76 |
|
✗ |
repo_tag.SetDeleteTags(JoinStrings(rm_tags, " ")); |
| 77 |
|
|
} |
| 78 |
|
✗ |
if (!add_tags.empty()) { |
| 79 |
|
|
// The commit protocol carries a single tag to add (tagging the new HEAD). |
| 80 |
|
✗ |
if (add_tags.size() > 1) { |
| 81 |
|
✗ |
throw EPublish("adding more than one tag in a single gateway operation " |
| 82 |
|
✗ |
"is not supported"); |
| 83 |
|
|
} |
| 84 |
|
✗ |
CheckTagName(add_tags[0].name); |
| 85 |
|
✗ |
if (!add_tags[0].root_hash.IsNull()) { |
| 86 |
|
✗ |
throw EPublish("tagging a specific root hash is not supported on gateway " |
| 87 |
|
✗ |
"repositories"); |
| 88 |
|
|
} |
| 89 |
|
✗ |
repo_tag.SetName(add_tags[0].name); |
| 90 |
|
✗ |
repo_tag.SetDescription(add_tags[0].description); |
| 91 |
|
|
} |
| 92 |
|
|
|
| 93 |
|
|
// The tag database is edited by the receiver; the catalog itself does not |
| 94 |
|
|
// change, so the commit carries the current root hash as both the old and |
| 95 |
|
|
// the new hash (an empty catalog diff). |
| 96 |
|
✗ |
const std::string root_hash = manifest_->catalog_hash().ToString( |
| 97 |
|
✗ |
true /* with_suffix */); |
| 98 |
|
|
|
| 99 |
|
|
// Acquire a gateway lease and write the session token before constructing the |
| 100 |
|
|
// spoolers, which read it. |
| 101 |
|
✗ |
session_->Acquire(); |
| 102 |
|
✗ |
ConstructSpoolers(); |
| 103 |
|
|
|
| 104 |
|
✗ |
spooler_files_->FinalizeSession(false /* commit */); |
| 105 |
|
✗ |
const bool rvb = spooler_catalogs_->FinalizeSession( |
| 106 |
|
|
true /* commit */, root_hash, root_hash, repo_tag); |
| 107 |
|
|
|
| 108 |
|
✗ |
if (!rvb) { |
| 109 |
|
|
// Leave the lease in place so the Session destructor releases it. |
| 110 |
|
✗ |
throw EPublish("failed to commit tag changes to the gateway"); |
| 111 |
|
|
} |
| 112 |
|
|
|
| 113 |
|
|
// A successful commit makes the gateway drop the lease; remove the local |
| 114 |
|
|
// session token and keep the Session object from trying to drop it again. |
| 115 |
|
✗ |
session_->SetKeepAlive(true); |
| 116 |
|
✗ |
unlink(session_->token_path().c_str()); |
| 117 |
|
✗ |
unlink(session_->api_version_path().c_str()); |
| 118 |
|
|
|
| 119 |
|
✗ |
LogCvmfs(kLogCvmfs, kLogStdout, "Tags updated"); |
| 120 |
|
|
} |
| 121 |
|
|
|
| 122 |
|
|
} // namespace publish |
| 123 |
|
|
|