Skip to content

Commit

Permalink
#1906 DescribesInstance
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Feb 28, 2024
1 parent f9997a7 commit f598669
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 45 deletions.
3 changes: 2 additions & 1 deletion src/main/java/com/rultor/agents/Agents.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.jcabi.ssh.Ssh;
import com.rultor.agents.aws.AwsEc2;
import com.rultor.agents.aws.ConnectsInstance;
import com.rultor.agents.aws.DescribesInstance;
import com.rultor.agents.aws.KillsInstance;
import com.rultor.agents.aws.PingsInstance;
import com.rultor.agents.aws.StartsInstance;
Expand Down Expand Up @@ -284,9 +285,9 @@ public Agent agent(final Talk talk, final Profile profile)
false
)
),
new Agent.Quiet(new DescribesInstance(aws)),
new Agent.Quiet(
new ConnectsInstance(
aws,
new PfShell(
profile,
"none",
Expand Down
90 changes: 49 additions & 41 deletions src/main/java/com/rultor/agents/aws/ConnectsInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@
*/
package com.rultor.agents.aws;

import com.amazonaws.services.ec2.model.DescribeInstanceStatusRequest;
import com.amazonaws.services.ec2.model.DescribeInstanceStatusResult;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.jcabi.aspects.Immutable;
import com.jcabi.log.Logger;
import com.jcabi.ssh.Shell;
import com.jcabi.xml.XML;
import com.rultor.agents.AbstractAgent;
import com.rultor.agents.shells.PfShell;
Expand All @@ -53,59 +49,71 @@
@ToString
public final class ConnectsInstance extends AbstractAgent {

/**
* AWS Client.
*/
private final transient AwsEc2 api;

/**
* Shell.
*/
private final transient PfShell shell;

/**
* Ctor.
* @param aws API
* @param shll The shell
*/
public ConnectsInstance(final AwsEc2 aws, final PfShell shll) {
super("/talk[daemon and ec2 and not(shell)]");
this.api = aws;
public ConnectsInstance(final PfShell shll) {
super("/talk[daemon and ec2/host and not(shell)]");
this.shell = shll;
}

@Override
public Iterable<Directive> process(final XML xml) throws IOException {
final String instance = xml.xpath("/talk/ec2/@id").get(0);
while (true) {
final DescribeInstanceStatusResult res = this.api.aws().describeInstanceStatus(
new DescribeInstanceStatusRequest()
.withIncludeAllInstances(true)
.withInstanceIds(instance)
final String host = xml.xpath("/talk/ec2/host").get(0);
final Directives dirs = new Directives();
if (this.alive(host)) {
Logger.warn(
this, "AWS instance %s is alive at %s for %s",
instance, host, xml.xpath("/talk/@name").get(0)
);
dirs.xpath("/talk").add("shell")
.attr("id", xml.xpath("/talk/daemon/@id").get(0))
.add("host").set(host).up()
.add("port").set(Integer.toString(this.shell.port())).up()
.add("login").set(this.shell.login()).up()
.add("key").set(this.shell.key());
Logger.info(
this, "AWS instance %s launched and running at %s",
instance, host
);
} else {
Logger.warn(
this, "Can't connect to AWS instance %s at %s",
instance, host
);
final InstanceState state = res.getInstanceStatuses().get(0).getInstanceState();
Logger.info(this, "AWS instance %s state: %s", instance, state.getName());
if ("running".equals(state.getName())) {
break;
}
new Sleep(5L).now();
}
final Instance ready = this.api.aws().describeInstances(
new DescribeInstancesRequest()
.withInstanceIds(instance)
).getReservations().get(0).getInstances().get(0);
Logger.info(
this, "AWS instance %s launched and running at %s",
ready.getInstanceId(), ready.getPublicIpAddress()
);
new Sleep(60L).now();
final Directives dirs = new Directives();
dirs.xpath("/talk").add("shell")
.attr("id", xml.xpath("/talk/daemon/@id").get(0))
.add("host").set(ready.getPublicIpAddress()).up()
.add("port").set(Integer.toString(this.shell.port())).up()
.add("login").set(this.shell.login()).up()
.add("key").set(this.shell.key());
return dirs;
}

/**
* Tries to connect to it via SSH and returns TRUE if it's possible.
* @param host IP of the host
* @return TRUE if alive
*/
@SuppressWarnings("PMD.AvoidCatchingGenericException")
private boolean alive(final String host) {
boolean alive = false;
try {
new Shell.Empty(
new Shell.Safe(
this.shell.withHost(host).toSsh()
)
).exec("whoami");
alive = true;
// @checkstyle IllegalCatchCheck (1 line)
} catch (final Exception ex) {
Logger.warn(
this, "Failed to SSH-connect to %s: %s",
host, ex.getMessage()
);
}
return alive;
}
}
93 changes: 93 additions & 0 deletions src/main/java/com/rultor/agents/aws/DescribesInstance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2009-2024 Yegor Bugayenko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1) Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2) Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution. 3) Neither the name of the rultor.com nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.rultor.agents.aws;

import com.amazonaws.services.ec2.model.DescribeInstanceStatusRequest;
import com.amazonaws.services.ec2.model.DescribeInstanceStatusResult;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.jcabi.aspects.Immutable;
import com.jcabi.log.Logger;
import com.jcabi.xml.XML;
import com.rultor.agents.AbstractAgent;
import java.io.IOException;
import lombok.ToString;
import org.xembly.Directive;
import org.xembly.Directives;

/**
* Finds IP of a running EC2 instance.
*
* @since 1.77
*/
@Immutable
@ToString
public final class DescribesInstance extends AbstractAgent {

/**
* AWS Client.
*/
private final transient AwsEc2 api;

/**
* Ctor.
* @param aws API
*/
public DescribesInstance(final AwsEc2 aws) {
super("/talk[daemon and ec2 and not(ec2/host)]");
this.api = aws;
}

@Override
public Iterable<Directive> process(final XML xml) throws IOException {
final String instance = xml.xpath("/talk/ec2/@id").get(0);
while (true) {
final DescribeInstanceStatusResult res = this.api.aws().describeInstanceStatus(
new DescribeInstanceStatusRequest()
.withIncludeAllInstances(true)
.withInstanceIds(instance)
);
final InstanceState state = res.getInstanceStatuses().get(0).getInstanceState();
Logger.info(this, "AWS instance %s state: %s", instance, state.getName());
if ("running".equals(state.getName())) {
break;
}
new Sleep(5L).now();
}
final Instance ready = this.api.aws().describeInstances(
new DescribeInstancesRequest()
.withInstanceIds(instance)
).getReservations().get(0).getInstances().get(0);
final String host = ready.getPublicIpAddress();
return new Directives().xpath("/talk/ec2").add("host").set(host);
}

}
20 changes: 20 additions & 0 deletions src/main/java/com/rultor/agents/shells/PfShell.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
package com.rultor.agents.shells;

import com.jcabi.aspects.Immutable;
import com.jcabi.ssh.Ssh;
import com.rultor.spi.Profile;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import lombok.EqualsAndHashCode;
import lombok.ToString;
Expand Down Expand Up @@ -91,6 +93,15 @@ public PfShell(final Profile prof, final String host,
this.pvt = key;
}

/**
* Make a new shell, with a different address.
* @param host New address
* @return New shell
*/
public PfShell withHost(final String host) {
return new PfShell(this.profile, host, this.prt, this.user, this.pvt);
}

/**
* Get host.
* @return Host name
Expand Down Expand Up @@ -158,4 +169,13 @@ public String key() throws IOException {
return key;
}

/**
* Make SSH shell.
* @return SSH shell
* @throws UnknownHostException If fails
*/
public Ssh toSsh() throws UnknownHostException {
return new Ssh(this.addr, this.prt, this.user, this.pvt);
}

}
7 changes: 4 additions & 3 deletions src/main/java/com/rultor/agents/shells/TalkShells.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

import com.jcabi.aspects.Immutable;
import com.jcabi.ssh.Shell;
import com.jcabi.ssh.Ssh;
import com.jcabi.xml.XML;
import com.rultor.spi.Profile;
import java.net.UnknownHostException;
import lombok.EqualsAndHashCode;
import lombok.ToString;
Expand Down Expand Up @@ -67,11 +67,12 @@ public TalkShells(final XML talk) {
*/
public Shell get() throws UnknownHostException {
final XML shell = this.xml.nodes("/talk/shell").get(0);
return new Ssh(
return new PfShell(
Profile.EMPTY,
shell.xpath("host/text()").get(0),
Integer.parseInt(shell.xpath("port/text()").get(0)),
shell.xpath("login/text()").get(0),
shell.xpath("key/text()").get(0)
);
).toSsh();
}
}

0 comments on commit f598669

Please sign in to comment.