diff options
Diffstat (limited to 'node-repository')
5 files changed, 77 insertions, 33 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java index 1a75e5c4c74..eaa5aebda90 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainer.java @@ -67,16 +67,17 @@ public class AutoscalingMaintainer extends Maintainer { int currentGroups = (int) clusterNodes.stream().map(node -> node.allocation().get().membership().cluster().group()).distinct().count(); ClusterSpec.Type clusterType = clusterNodes.get(0).allocation().get().membership().cluster().type(); log.info("Autoscale: " + application + " " + clusterType + " " + clusterId + - " from " + toString(clusterNodes.size(), currentGroups, clusterNodes.get(0).flavor().resources()) + - " to " + toString(target.get().nodes(), target.get().groups(), target.get().advertisedResources())); + "\nfrom " + toString(clusterNodes.size(), currentGroups, clusterNodes.get(0).flavor().resources()) + + "\nto " + toString(target.get().nodes(), target.get().groups(), target.get().advertisedResources())); lastLogged.put(new Pair<>(application, clusterId), nodeRepository().clock().instant()); } private String toString(int nodes, int groups, NodeResources resources) { - return nodes + - (groups > 1 ? " in " + groups + " groups " : " ") + - " * " + resources + - " (total: " + "[vcpu: " + nodes * resources.vcpu() + ", memory: " + nodes * resources.memoryGb() + " Gb, disk " + nodes * resources.diskGb() + " Gb])"; + return String.format(nodes + (groups > 1 ? " (in " + groups + " groups)" : "") + + " * [vcpu: %1$.1f, memory: %2$.1f Gb, disk %3$.1f Gb]" + + " (total: [vcpu: %4$.1f, memory: %5$.1f Gb, disk %6$.1f Gb])," + + resources.vcpu(), resources.memoryGb(), resources.diskGb(), + nodes * resources.vcpu(), nodes * resources.memoryGb(), nodes * resources.diskGb()); } private Map<ClusterSpec.Id, List<Node>> nodesByCluster(List<Node> applicationNodes) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java index be43c0139e0..94f4dab1245 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java @@ -187,8 +187,7 @@ public class NodeSerializer { object.setLong(currentRestartGenerationKey, allocation.restartGeneration().current()); object.setBool(removableKey, allocation.isRemovable()); object.setString(wantedVespaVersionKey, allocation.membership().cluster().vespaVersion().toString()); - // TODO serialize dockerImageRepo - //object.setString(wantedDockerImageRepoKey, allocation.membership().cluster().dockerImageRepo().orElse("")); + allocation.membership().cluster().dockerImageRepo().ifPresent(repo -> object.setString(wantedDockerImageRepoKey, repo)); allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray(networkPortsKey))); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index 7a49ad9c44d..73061acd9c1 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -5,6 +5,7 @@ import com.google.inject.Inject; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; @@ -148,7 +149,8 @@ public class NodeRepositoryProvisioner implements Provisioner { Optional.of(nodeAllocation.membership()), node.status().vespaVersion(), nodeAllocation.networkPorts(), - requestedResources)); + requestedResources, + node.status().dockerImage().map(DockerImage::repository))); if (nodeAllocation.networkPorts().isPresent()) { log.log(LogLevel.DEBUG, () -> "Prepared node " + node.hostname() + " has port allocations"); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java index 7f283452538..248cfbec662 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesResponse.java @@ -157,7 +157,8 @@ class NodesResponse extends HttpResponse { toSlime(allocation.membership(), object.setObject("membership")); object.setLong("restartGeneration", allocation.restartGeneration().wanted()); object.setLong("currentRestartGeneration", allocation.restartGeneration().current()); - object.setString("wantedDockerImage", nodeRepository.dockerImage(node).withTag(allocation.membership().cluster().vespaVersion()).asString()); + object.setString("wantedDockerImage", allocation.membership().cluster().dockerImage() + .orElseGet(() -> nodeRepository.dockerImage(node).withTag(allocation.membership().cluster().vespaVersion()).asString())); object.setString("wantedVespaVersion", allocation.membership().cluster().vespaVersion().toFullString()); toSlime(allocation.requestedResources(), object.setObject("requestedResources")); allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray("networkPorts"))); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java index 1f9d7ce5126..931d87a3265 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; @@ -157,6 +158,34 @@ public class ProvisioningTest { } @Test + public void dockerImageRepoIsReturnedIfSet() { + ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); + + tester.makeReadyNodes(4, defaultResources, NodeType.host, 1); + tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); + + // deploy + ApplicationId application1 = tester.makeApplicationId(); + SystemState state1 = prepare(application1, tester, 1, 1, 1, 1, defaultResources, "1.2.3"); + String dockerImageRepo = "docker.domain.tld/my/image"; + prepare(application1, tester, 1, 1, 1 , 1 , false, defaultResources, "1.2.3", Optional.of(dockerImageRepo)); + tester.activate(application1, state1.allHosts); + + HostSpec host1 = state1.container0.iterator().next(); + Node node1 = tester.nodeRepository().getNode(host1.hostname()).get(); + DockerImage dockerImage = DockerImage.fromString(dockerImageRepo).withTag(Version.fromString("1.2.3")); + tester.nodeRepository().write(node1.with(node1.status().withDockerImage(dockerImage)), () -> {}); + + // redeploy + SystemState state2 = prepare(application1, tester, 1, 1, 1 ,1 , false, defaultResources, "1.2.3", Optional.of(dockerImageRepo)); + tester.activate(application1, state2.allHosts); + + host1 = state2.container0.iterator().next(); + node1 = tester.nodeRepository().getNode(host1.hostname()).get(); + assertEquals(dockerImage, node1.status().dockerImage().get()); + } + + @Test public void application_deployment_variable_application_size() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); @@ -334,7 +363,21 @@ public class ProvisioningTest { tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); ApplicationId application = tester.makeApplicationId(); - SystemState state = prepare(application, 2, 2, 3, 3, defaultResources, Version.fromString("6.91"), tester); + SystemState state = prepare(application, tester, 2, 2, 3, 3, defaultResources, "6.91"); + assertEquals(4, state.allHosts.size()); + tester.activate(application, state.allHosts); + } + + @Test + public void deploy_specific_vespa_version_and_docker_image() { + ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); + + tester.makeReadyNodes(4, defaultResources, NodeType.host, 1); + tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); + + ApplicationId application = tester.makeApplicationId(); + String dockerImageRepo = "docker.domain.tld/my/image"; + SystemState state = prepare(application, tester, 2, 2, 3, 3, false, defaultResources, "6.91", Optional.of(dockerImageRepo)); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -630,7 +673,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 1, 0, 1, 0, true, defaultResources, Version.fromString("6.42"), tester); + prepare(application, tester, 1, 0, 1, 0, true, defaultResources, "6.42", Optional.empty()); fail("Expected exception"); } catch (IllegalArgumentException ignored) {} } @@ -652,17 +695,17 @@ public class ProvisioningTest { public void cluster_spec_update_for_already_reserved_nodes() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - Version version1 = Version.fromString("6.42"); - Version version2 = Version.fromString("6.43"); + String version1 = "6.42"; + String version2 = "6.43"; tester.makeReadyNodes(2, defaultResources); - prepare(application, 1, 0, 1, 0, true, defaultResources, version1, tester); + prepare(application, tester, 1, 0, 1, 0, true, defaultResources, version1, Optional.empty()); tester.getNodes(application, Node.State.reserved).forEach(node -> - assertEquals(version1, node.allocation().get().membership().cluster().vespaVersion())); + assertEquals(Version.fromString(version1), node.allocation().get().membership().cluster().vespaVersion())); - prepare(application, 1, 0, 1, 0, true, defaultResources, version2, tester); + prepare(application, tester, 1, 0, 1, 0, true, defaultResources, version2, Optional.empty()); tester.getNodes(application, Node.State.reserved).forEach(node -> - assertEquals(version2, node.allocation().get().membership().cluster().vespaVersion())); + assertEquals(Version.fromString(version2), node.allocation().get().membership().cluster().vespaVersion())); } @Test @@ -702,29 +745,27 @@ public class ProvisioningTest { private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, int content1Size, NodeResources flavor, ProvisioningTester tester) { - return prepare(application, container0Size, container1Size, content0Size, content1Size, flavor, - Version.fromString("6.42"), tester); + return prepare(application, tester, container0Size, container1Size, content0Size, content1Size, flavor, "6.42"); } - private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, NodeResources nodeResources, Version wantedVersion, ProvisioningTester tester) { - return prepare(application, container0Size, container1Size, content0Size, content1Size, false, nodeResources, - wantedVersion, tester); + private SystemState prepare(ApplicationId application, ProvisioningTester tester, int container0Size, int container1Size, int content0Size, + int content1Size, NodeResources nodeResources, String wantedVersion) { + return prepare(application, tester, container0Size, container1Size, content0Size, content1Size, false, nodeResources, + wantedVersion, Optional.empty()); } - private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, boolean required, NodeResources nodeResources, Version wantedVersion, - ProvisioningTester tester) { + private SystemState prepare(ApplicationId application, ProvisioningTester tester, int container0Size, int container1Size, int content0Size, + int content1Size, boolean required, NodeResources nodeResources, String wantedVersion, Optional<String> dockerImageRepo) { // "deploy prepare" with a two container clusters and a storage cluster having of two groups ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0")).vespaVersion(wantedVersion).build(); ClusterSpec containerCluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1")).vespaVersion(wantedVersion).build(); ClusterSpec contentCluster0 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content0")).vespaVersion(wantedVersion).build(); ClusterSpec contentCluster1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1")).vespaVersion(wantedVersion).build(); - Set<HostSpec> container0 = prepare(application, containerCluster0, container0Size, 1, required, nodeResources, tester); - Set<HostSpec> container1 = prepare(application, containerCluster1, container1Size, 1, required, nodeResources, tester); - Set<HostSpec> content0 = prepare(application, contentCluster0, content0Size, 1, required, nodeResources, tester); - Set<HostSpec> content1 = prepare(application, contentCluster1, content1Size, 1, required, nodeResources, tester); + Set<HostSpec> container0 = prepare(application, tester, containerCluster0, container0Size, 1, required, nodeResources); + Set<HostSpec> container1 = prepare(application, tester, containerCluster1, container1Size, 1, required, nodeResources); + Set<HostSpec> content0 = prepare(application, tester, contentCluster0, content0Size, 1, required, nodeResources); + Set<HostSpec> content1 = prepare(application, tester, contentCluster1, content1Size, 1, required, nodeResources); Set<HostSpec> allHosts = new HashSet<>(); allHosts.addAll(container0); @@ -755,8 +796,8 @@ public class ProvisioningTest { return new SystemState(allHosts, container0, container1, content0, content1); } - private Set<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, - boolean required, NodeResources nodeResources, ProvisioningTester tester) { + private Set<HostSpec> prepare(ApplicationId application, ProvisioningTester tester, ClusterSpec cluster, int nodeCount, int groups, + boolean required, NodeResources nodeResources) { if (nodeCount == 0) return Collections.emptySet(); // this is a shady practice return new HashSet<>(tester.prepare(application, cluster, nodeCount, groups, required, nodeResources)); } |