|
| 1 | +/* |
| 2 | + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one |
| 3 | + * or more contributor license agreements. Licensed under the "Elastic License |
| 4 | + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side |
| 5 | + * Public License v 1"; you may not use this file except in compliance with, at |
| 6 | + * your election, the "Elastic License 2.0", the "GNU Affero General Public |
| 7 | + * License v3.0 only", or the "Server Side Public License, v 1". |
| 8 | + */ |
| 9 | + |
| 10 | +package org.elasticsearch.gradle.internal.transport; |
| 11 | + |
| 12 | +import org.elasticsearch.gradle.internal.transport.TransportVersionResourcesService.IdAndDefinition; |
| 13 | +import org.gradle.api.DefaultTask; |
| 14 | +import org.gradle.api.file.RegularFileProperty; |
| 15 | +import org.gradle.api.provider.Property; |
| 16 | +import org.gradle.api.services.ServiceReference; |
| 17 | +import org.gradle.api.tasks.Input; |
| 18 | +import org.gradle.api.tasks.InputFile; |
| 19 | +import org.gradle.api.tasks.Optional; |
| 20 | +import org.gradle.api.tasks.TaskAction; |
| 21 | +import org.gradle.api.tasks.options.Option; |
| 22 | + |
| 23 | +import java.io.IOException; |
| 24 | +import java.nio.charset.StandardCharsets; |
| 25 | +import java.nio.file.Files; |
| 26 | +import java.nio.file.Path; |
| 27 | +import java.util.ArrayList; |
| 28 | +import java.util.Collections; |
| 29 | +import java.util.List; |
| 30 | +import java.util.Map; |
| 31 | +import java.util.Set; |
| 32 | + |
| 33 | +public abstract class AbstractGenerateTransportVersionDefinitionTask extends DefaultTask { |
| 34 | + |
| 35 | + @ServiceReference("transportVersionResources") |
| 36 | + abstract Property<TransportVersionResourcesService> getResourceService(); |
| 37 | + |
| 38 | + @Input |
| 39 | + @Optional |
| 40 | + @Option(option = "increment", description = "The amount to increment the id from the current upper bounds file by") |
| 41 | + public abstract Property<Integer> getIncrement(); |
| 42 | + |
| 43 | + /** |
| 44 | + * The name of the upper bounds file which will be used at runtime on the current branch. Normally |
| 45 | + * this equates to VersionProperties.getElasticsearchVersion(). |
| 46 | + */ |
| 47 | + @Input |
| 48 | + public abstract Property<String> getCurrentUpperBoundName(); |
| 49 | + |
| 50 | + /** |
| 51 | + * An additional upper bound file that will be consulted when generating a transport version. |
| 52 | + * The larger of this and the current upper bound will be used to create the new primary id. |
| 53 | + */ |
| 54 | + @InputFile |
| 55 | + @Optional |
| 56 | + public abstract RegularFileProperty getAlternateUpperBoundFile(); |
| 57 | + |
| 58 | + protected abstract void runGeneration(TransportVersionResourcesService resources, List<TransportVersionUpperBound> upstreamUpperBounds) |
| 59 | + throws IOException; |
| 60 | + |
| 61 | + protected abstract Set<String> getTargetUpperBoundNames( |
| 62 | + TransportVersionResourcesService resources, |
| 63 | + List<TransportVersionUpperBound> upstreamUpperBounds, |
| 64 | + String targetDefinitionName |
| 65 | + ) throws IOException; |
| 66 | + |
| 67 | + protected abstract void writeUpperBound(TransportVersionResourcesService resources, TransportVersionUpperBound newUpperBound) |
| 68 | + throws IOException; |
| 69 | + |
| 70 | + @TaskAction |
| 71 | + public void run() throws IOException { |
| 72 | + TransportVersionResourcesService resources = getResourceService().get(); |
| 73 | + List<TransportVersionUpperBound> upstreamUpperBounds = resources.getUpperBoundsFromGitBase(); |
| 74 | + boolean onReleaseBranch = resources.checkIfDefinitelyOnReleaseBranch(upstreamUpperBounds, getCurrentUpperBoundName().get()); |
| 75 | + if (onReleaseBranch) { |
| 76 | + throw new IllegalArgumentException("Transport version generation cannot run on release branches"); |
| 77 | + } |
| 78 | + |
| 79 | + runGeneration(resources, upstreamUpperBounds); |
| 80 | + } |
| 81 | + |
| 82 | + protected void generateTransportVersionDefinition( |
| 83 | + TransportVersionResourcesService resources, |
| 84 | + String targetDefinitionName, |
| 85 | + List<TransportVersionUpperBound> upstreamUpperBounds, |
| 86 | + Map<Integer, List<IdAndDefinition>> idsByBase |
| 87 | + ) throws IOException { |
| 88 | + getLogger().lifecycle("Generating transport version name: " + targetDefinitionName); |
| 89 | + |
| 90 | + Set<String> targetUpperBoundNames = getTargetUpperBoundNames(resources, upstreamUpperBounds, targetDefinitionName); |
| 91 | + |
| 92 | + List<TransportVersionId> ids = updateUpperBounds( |
| 93 | + resources, |
| 94 | + upstreamUpperBounds, |
| 95 | + targetUpperBoundNames, |
| 96 | + idsByBase, |
| 97 | + targetDefinitionName |
| 98 | + ); |
| 99 | + // (Re)write the definition file. |
| 100 | + resources.writeDefinition(new TransportVersionDefinition(targetDefinitionName, ids, true)); |
| 101 | + } |
| 102 | + |
| 103 | + private List<TransportVersionId> updateUpperBounds( |
| 104 | + TransportVersionResourcesService resources, |
| 105 | + List<TransportVersionUpperBound> existingUpperBounds, |
| 106 | + Set<String> targetUpperBoundNames, |
| 107 | + Map<Integer, List<IdAndDefinition>> idsByBase, |
| 108 | + String definitionName |
| 109 | + ) throws IOException { |
| 110 | + String currentUpperBoundName = getCurrentUpperBoundName().get(); |
| 111 | + int increment = getIncrement().get(); |
| 112 | + if (increment <= 0) { |
| 113 | + throw new IllegalArgumentException("Invalid increment " + increment + ", must be a positive integer"); |
| 114 | + } |
| 115 | + if (increment > 1000) { |
| 116 | + throw new IllegalArgumentException("Invalid increment " + increment + ", must be no larger than 1000"); |
| 117 | + } |
| 118 | + List<TransportVersionId> ids = new ArrayList<>(); |
| 119 | + |
| 120 | + TransportVersionDefinition existingDefinition = resources.getReferableDefinitionFromGitBase(definitionName); |
| 121 | + for (TransportVersionUpperBound existingUpperBound : existingUpperBounds) { |
| 122 | + String upperBoundName = existingUpperBound.name(); |
| 123 | + |
| 124 | + if (targetUpperBoundNames.contains(upperBoundName)) { |
| 125 | + // Case: targeting this upper bound, find an existing id if it exists |
| 126 | + TransportVersionId targetId = maybeGetExistingId(existingUpperBound, existingDefinition, definitionName); |
| 127 | + if (targetId == null) { |
| 128 | + // Case: an id doesn't yet exist for this upper bound, so create one |
| 129 | + int targetIncrement = upperBoundName.equals(currentUpperBoundName) ? increment : 1; |
| 130 | + targetId = createTargetId(existingUpperBound, targetIncrement); |
| 131 | + var newUpperBound = new TransportVersionUpperBound(upperBoundName, definitionName, targetId); |
| 132 | + writeUpperBound(resources, newUpperBound); |
| 133 | + } |
| 134 | + ids.add(targetId); |
| 135 | + } else if (resources.getChangedUpperBoundNames().contains(upperBoundName)) { |
| 136 | + // Default case: we're not targeting this branch so reset it |
| 137 | + resetUpperBound(resources, existingUpperBound, idsByBase, definitionName); |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + Collections.sort(ids); |
| 142 | + return ids; |
| 143 | + } |
| 144 | + |
| 145 | + private void resetUpperBound( |
| 146 | + TransportVersionResourcesService resources, |
| 147 | + TransportVersionUpperBound upperBound, |
| 148 | + Map<Integer, List<IdAndDefinition>> idsByBase, |
| 149 | + String ignoreDefinitionName |
| 150 | + ) throws IOException { |
| 151 | + List<IdAndDefinition> idsForUpperBound = idsByBase.get(upperBound.definitionId().base()); |
| 152 | + if (idsForUpperBound == null) { |
| 153 | + throw new RuntimeException("Could not find base id: " + upperBound.definitionId().base()); |
| 154 | + } |
| 155 | + IdAndDefinition resetValue = idsForUpperBound.getLast(); |
| 156 | + if (resetValue.definition().name().equals(ignoreDefinitionName)) { |
| 157 | + // there must be another definition in this base since the ignored definition is new |
| 158 | + assert idsForUpperBound.size() >= 2; |
| 159 | + resetValue = idsForUpperBound.get(idsForUpperBound.size() - 2); |
| 160 | + } |
| 161 | + var resetUpperBound = new TransportVersionUpperBound(upperBound.name(), resetValue.definition().name(), resetValue.id()); |
| 162 | + resources.writeUpperBound(resetUpperBound, false); |
| 163 | + } |
| 164 | + |
| 165 | + private TransportVersionId maybeGetExistingId( |
| 166 | + TransportVersionUpperBound upperBound, |
| 167 | + TransportVersionDefinition existingDefinition, |
| 168 | + String name |
| 169 | + ) { |
| 170 | + if (existingDefinition == null) { |
| 171 | + // the name doesn't yet exist, so there is no id to return |
| 172 | + return null; |
| 173 | + } |
| 174 | + if (upperBound.definitionName().equals(name)) { |
| 175 | + // the name exists and this upper bound already points at it |
| 176 | + return upperBound.definitionId(); |
| 177 | + } |
| 178 | + if (upperBound.name().equals(getCurrentUpperBoundName().get())) { |
| 179 | + // this is the upper bound of the current branch, so use the primary id |
| 180 | + return existingDefinition.ids().getFirst(); |
| 181 | + } |
| 182 | + // the upper bound is for a non-current branch, so find the id with the same base |
| 183 | + for (TransportVersionId id : existingDefinition.ids()) { |
| 184 | + if (id.base() == upperBound.definitionId().base()) { |
| 185 | + return id; |
| 186 | + } |
| 187 | + } |
| 188 | + return null; // no existing id for this upper bound |
| 189 | + } |
| 190 | + |
| 191 | + private TransportVersionId createTargetId(TransportVersionUpperBound existingUpperBound, int increment) throws IOException { |
| 192 | + int currentId = existingUpperBound.definitionId().complete(); |
| 193 | + |
| 194 | + // allow for an alternate upper bound file to be consulted. This supports Serverless basing its |
| 195 | + // own transport version ids on the greater of server or serverless |
| 196 | + if (getAlternateUpperBoundFile().isPresent()) { |
| 197 | + Path altUpperBoundPath = getAlternateUpperBoundFile().get().getAsFile().toPath(); |
| 198 | + String contents = Files.readString(altUpperBoundPath, StandardCharsets.UTF_8); |
| 199 | + var altUpperBound = TransportVersionUpperBound.fromString(altUpperBoundPath, contents); |
| 200 | + if (altUpperBound.definitionId().complete() > currentId) { |
| 201 | + currentId = altUpperBound.definitionId().complete(); |
| 202 | + } |
| 203 | + } |
| 204 | + |
| 205 | + return TransportVersionId.fromInt(currentId + increment); |
| 206 | + } |
| 207 | +} |
0 commit comments