-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Use UUID for clouds #26233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Use UUID for clouds #26233
Changes from all commits
f82800f
66c1f56
7addb81
7054e1f
9554917
5283a32
6873377
02c38c4
1b5fcbf
51d55ce
3185ac4
b43bcdb
4405333
9034ab5
4897a40
d1f21ba
e8cd89c
7a64b00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
|
|
||
| package jenkins.agents; | ||
|
|
||
| import edu.umd.cs.findbugs.annotations.CheckForNull; | ||
| import hudson.Extension; | ||
| import hudson.ExtensionList; | ||
| import hudson.Functions; | ||
|
|
@@ -73,7 +74,7 @@ | |
| } | ||
|
|
||
| public Cloud getDynamic(String token) { | ||
| return Jenkins.get().getCloud(token); | ||
| return getById(token); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -113,11 +114,8 @@ | |
| @Restricted(DoNotUse.class) // stapler | ||
| public String getCloudUrl(StaplerRequest2 request, Jenkins jenkins, Cloud cloud) { | ||
| String context = Functions.getNearestAncestorUrl(request, jenkins); | ||
| if (Jenkins.get().getCloud(cloud.name) != cloud) { // this cloud is not the first occurrence with this name | ||
| return context + "/cloud/cloudByIndex/" + getClouds().indexOf(cloud) + "/"; | ||
| } else { | ||
| return context + "/" + cloud.getUrl(); | ||
| } | ||
| // Always use UUID-based URLs for stability across renames, reordering, and duplicates | ||
| return context + "/" + cloud.getUrl(); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -130,140 +128,152 @@ | |
| return getCloudUrl(StaplerRequest.toStaplerRequest2(request), jenkins, cloud); | ||
| } | ||
|
|
||
| @SuppressWarnings("unused") // stapler | ||
| @Restricted(DoNotUse.class) // stapler | ||
| public Cloud getCloudByIndex(int index) { | ||
| return Jenkins.get().clouds.get(index); | ||
| /** | ||
| * Gets a cloud by its unique ID. | ||
| */ | ||
| @CheckForNull | ||
| public Cloud getById(String id) { | ||
| if (id == null || id.trim().isEmpty()) { | ||
| return null; | ||
| } | ||
| for (Cloud c : Jenkins.get().clouds) { | ||
| if (id.equals(c.getUniqueId())) { | ||
| return c; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| @SuppressWarnings("unused") // stapler | ||
| public boolean isCloudAvailable() { | ||
| return !Cloud.all().isEmpty(); | ||
| } | ||
|
|
||
| @SuppressWarnings("unused") // stapler | ||
| public String getCloudUpdateCenterCategoryLabel() { | ||
| return URLEncoder.encode(UpdateCenter.getCategoryDisplayName("cloud"), StandardCharsets.UTF_8); | ||
| } | ||
|
|
||
| @Override | ||
| public ModelObjectWithContextMenu.ContextMenu doChildrenContextMenu(StaplerRequest2 request, StaplerResponse2 response) throws Exception { | ||
| ModelObjectWithContextMenu.ContextMenu m = new ModelObjectWithContextMenu.ContextMenu(); | ||
| Jenkins.get().clouds.forEach(m::add); | ||
| return m; | ||
| } | ||
|
|
||
| public Cloud getDynamic(String name, StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException { | ||
| return Jenkins.get().clouds.getByName(name); | ||
| public Cloud getDynamic(String token, StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException { | ||
| return getById(token); | ||
| } | ||
|
|
||
| @SuppressWarnings("unused") // stapler | ||
| @Restricted(DoNotUse.class) // stapler | ||
| public Jenkins.CloudList getClouds() { | ||
| return Jenkins.get().clouds; | ||
| } | ||
|
|
||
| @SuppressWarnings("unused") // stapler | ||
| @Restricted(DoNotUse.class) // stapler | ||
| public boolean hasClouds() { | ||
| return !Jenkins.get().clouds.isEmpty(); | ||
| } | ||
|
|
||
| /** | ||
| * Makes sure that the given name is good as an agent name. | ||
| * @return trimmed name if valid; throws ParseException if not | ||
| */ | ||
| public String checkName(String name) throws Failure { | ||
| if (name == null) | ||
| throw new Failure("Query parameter 'name' is required"); | ||
|
|
||
| name = name.trim(); | ||
| Jenkins.checkGoodName(name); | ||
|
|
||
| if (Jenkins.get().getCloud(name) != null) | ||
| throw new Failure(Messages.CloudSet_CloudAlreadyExists(name)); | ||
|
|
||
| // looks good | ||
| return name; | ||
| } | ||
|
|
||
| @SuppressWarnings("unused") // stapler | ||
| public FormValidation doCheckName(@QueryParameter String value) { | ||
| Jenkins.get().checkPermission(Jenkins.ADMINISTER); | ||
| if (Util.fixEmpty(value) == null) { | ||
| return FormValidation.ok(); | ||
| } | ||
| try { | ||
| checkName(value); | ||
| return FormValidation.ok(); | ||
| } catch (Failure e) { | ||
| return FormValidation.error(e.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * First check point in creating a new cloud. | ||
| */ | ||
| @RequirePOST | ||
| public synchronized void doCreate(StaplerRequest2 req, StaplerResponse2 rsp, | ||
| @QueryParameter String name, @QueryParameter String mode, | ||
| @QueryParameter String from) throws IOException, ServletException, Descriptor.FormException { | ||
| @QueryParameter String name, @QueryParameter String mode, | ||
| @QueryParameter String from) throws IOException, ServletException, Descriptor.FormException { | ||
| final Jenkins jenkins = Jenkins.get(); | ||
| jenkins.checkPermission(Jenkins.ADMINISTER); | ||
|
|
||
| if (mode != null && mode.equals("copy")) { | ||
| name = checkName(name); | ||
|
|
||
| Cloud src = jenkins.getCloud(from); | ||
| if (src == null) { | ||
| if (Util.fixEmpty(from) == null) { | ||
| throw new Failure(Messages.CloudSet_SpecifyCloudToCopy()); | ||
| } else { | ||
| throw new Failure(Messages.CloudSet_NoSuchCloud(from)); | ||
| } | ||
| } | ||
|
|
||
| // copy through XStream | ||
| String xml = Jenkins.XSTREAM.toXML(src); | ||
| Cloud result = (Cloud) Jenkins.XSTREAM.fromXML(xml); | ||
| result.name = name; | ||
| // Provision a new unique ID for the copied cloud to ensure distinct identity | ||
| result.provisionNewId(); | ||
| jenkins.clouds.add(result); | ||
| // send the browser to the config page | ||
| rsp.sendRedirect2(Functions.getNearestAncestorUrl(req, jenkins) + "/" + result.getUrl() + "configure"); | ||
| } else { | ||
| // proceed to step 2 | ||
| if (mode == null) { | ||
| throw new Failure("No mode given"); | ||
| } | ||
|
|
||
| Descriptor<Cloud> d = Cloud.all().findByName(mode); | ||
| if (d == null) { | ||
| throw new Failure("No node type ‘" + mode + "’ is known"); | ||
| } | ||
| handleNewCloudPage(d, name, req, rsp); | ||
| } | ||
| } | ||
|
|
||
| private void handleNewCloudPage(Descriptor<Cloud> descriptor, String name, StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException, Descriptor.FormException { | ||
| checkName(name); | ||
| JSONObject formData = req.getSubmittedForm(); | ||
| formData.put("name", name); | ||
| formData.remove("mode"); // Cloud descriptors won't have this field. | ||
| req.setAttribute("instance", formData); | ||
| req.setAttribute("descriptor", descriptor); | ||
| req.getView(this, "_new.jelly").forward(req, rsp); | ||
| } | ||
|
|
||
| /** | ||
| * Really creates a new agent. | ||
| */ | ||
| @POST | ||
| public synchronized void doDoCreate(StaplerRequest2 req, StaplerResponse2 rsp, | ||
| @QueryParameter String cloudDescriptorName) throws IOException, ServletException, Descriptor.FormException { | ||
| @QueryParameter String cloudDescriptorName) throws IOException, ServletException, Descriptor.FormException { | ||
| Jenkins.get().checkPermission(Jenkins.ADMINISTER); | ||
| Descriptor<Cloud> cloudDescriptor = Cloud.all().findByName(cloudDescriptorName); | ||
| if (cloudDescriptor == null) { | ||
| throw new Failure(String.format("No cloud type ‘%s’ is known", cloudDescriptorName)); | ||
| throw new Failure(String.format("No cloud type '%s' is known", cloudDescriptorName)); | ||
| } | ||
| Cloud cloud = cloudDescriptor.newInstance(req, req.getSubmittedForm()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Already here the new uuid should be set I think
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, mostly it would be set but but the condition was there so i didnt remove that shall i omit it?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that here you should call |
||
| if (!Jenkins.get().clouds.add(cloud)) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this doesn't work. I tried this with a google cloud and the method is not called on Jenkins startup. You need to add
to the readResolve method in the
Jenkinsclass.This also ensures that the new ids are persisted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay i will implement that