How to use Drain class of org.openqa.selenium.grid.node package

Best Selenium code snippet using org.openqa.selenium.grid.node.Drain

Source:SauceNode.java Github

copy

Full Screen

...34import org.openqa.selenium.events.EventBus;35import org.openqa.selenium.grid.data.Availability;36import org.openqa.selenium.grid.data.CreateSessionRequest;37import org.openqa.selenium.grid.data.CreateSessionResponse;38import org.openqa.selenium.grid.data.NodeDrainComplete;39import org.openqa.selenium.grid.data.NodeDrainStarted;40import org.openqa.selenium.grid.data.NodeHeartBeatEvent;41import org.openqa.selenium.grid.data.NodeId;42import org.openqa.selenium.grid.data.NodeStatus;43import org.openqa.selenium.grid.data.Session;44import org.openqa.selenium.grid.data.SessionClosedEvent;45import org.openqa.selenium.grid.data.Slot;46import org.openqa.selenium.grid.data.SlotId;47import org.openqa.selenium.grid.jmx.JMXHelper;48import org.openqa.selenium.grid.jmx.ManagedAttribute;49import org.openqa.selenium.grid.jmx.ManagedService;50import org.openqa.selenium.grid.node.ActiveSession;51import org.openqa.selenium.grid.node.HealthCheck;52import org.openqa.selenium.grid.node.Node;53import org.openqa.selenium.grid.node.SessionFactory;54import org.openqa.selenium.grid.node.config.NodeOptions;55import org.openqa.selenium.grid.node.docker.DockerAssetsPath;56import org.openqa.selenium.grid.node.local.LocalNode;57import org.openqa.selenium.grid.node.local.SessionSlot;58import org.openqa.selenium.grid.security.Secret;59import org.openqa.selenium.internal.Debug;60import org.openqa.selenium.internal.Either;61import org.openqa.selenium.internal.Require;62import org.openqa.selenium.io.TemporaryFilesystem;63import org.openqa.selenium.io.Zip;64import org.openqa.selenium.json.Json;65import org.openqa.selenium.remote.SessionId;66import org.openqa.selenium.remote.http.HttpMethod;67import org.openqa.selenium.remote.http.HttpRequest;68import org.openqa.selenium.remote.http.HttpResponse;69import org.openqa.selenium.remote.tracing.AttributeKey;70import org.openqa.selenium.remote.tracing.EventAttribute;71import org.openqa.selenium.remote.tracing.EventAttributeValue;72import org.openqa.selenium.remote.tracing.Span;73import org.openqa.selenium.remote.tracing.Status;74import org.openqa.selenium.remote.tracing.Tracer;75import java.io.File;76import java.io.IOException;77import java.io.InputStreamReader;78import java.io.UncheckedIOException;79import java.net.URI;80import java.net.URISyntaxException;81import java.nio.file.Files;82import java.nio.file.Paths;83import java.nio.file.StandardCopyOption;84import java.time.Duration;85import java.time.Instant;86import java.util.Collections;87import java.util.HashMap;88import java.util.List;89import java.util.Map;90import java.util.Optional;91import java.util.Set;92import java.util.UUID;93import java.util.concurrent.ExecutionException;94import java.util.concurrent.atomic.AtomicInteger;95import java.util.logging.Level;96import java.util.logging.Logger;97import java.util.stream.Collectors;98@ManagedService(objectName = "com.saucelabs.grid:type=Node,name=SauceNode",99 description = "SauceNode running the webdriver sessions and uploading results to Sauce.")100public class SauceNode extends Node {101 private static final Logger LOG = Logger.getLogger(LocalNode.class.getName());102 private final EventBus bus;103 private final URI externalUri;104 private final URI gridUri;105 private final Duration heartbeatPeriod;106 private final HealthCheck healthCheck;107 private final int maxSessionCount;108 private final List<SessionSlot> factories;109 private final Cache<SessionId, SessionSlot> currentSessions;110 private final Cache<SessionId, TemporaryFilesystem> tempFileSystems;111 private final AtomicInteger pendingSessions = new AtomicInteger();112 private SauceNode(113 Tracer tracer,114 EventBus bus,115 URI uri,116 URI gridUri,117 HealthCheck healthCheck,118 int maxSessionCount,119 Ticker ticker,120 Duration sessionTimeout,121 Duration heartbeatPeriod,122 List<SessionSlot> factories,123 Secret registrationSecret) {124 super(tracer, new NodeId(UUID.randomUUID()), uri, registrationSecret);125 this.bus = Require.nonNull("Event bus", bus);126 this.externalUri = Require.nonNull("Remote node URI", uri);127 this.gridUri = Require.nonNull("Grid URI", gridUri);128 this.maxSessionCount = Math.min(Require.positive("Max session count", maxSessionCount), factories.size());129 this.heartbeatPeriod = heartbeatPeriod;130 this.factories = ImmutableList.copyOf(factories);131 Require.nonNull("Registration secret", registrationSecret);132 this.healthCheck = healthCheck == null ?133 () -> new HealthCheck.Result(134 isDraining() ? DRAINING : UP,135 String.format("%s is %s", uri, isDraining() ? "draining" : "up")) :136 healthCheck;137 this.currentSessions = CacheBuilder.newBuilder()138 .expireAfterAccess(sessionTimeout)139 .ticker(ticker)140 .removalListener((RemovalListener<SessionId, SessionSlot>) notification -> {141 // Attempt to stop the session142 LOG.log(Debug.getDebugLogLevel(), "Stopping session {0}", notification.getKey().toString());143 SessionSlot slot = notification.getValue();144 if (!slot.isAvailable()) {145 slot.stop();146 }147 })148 .build();149 this.tempFileSystems = CacheBuilder.newBuilder()150 .expireAfterAccess(sessionTimeout)151 .ticker(ticker)152 .removalListener((RemovalListener<SessionId, TemporaryFilesystem>) notification -> {153 TemporaryFilesystem tempFS = notification.getValue();154 tempFS.deleteTemporaryFiles();155 tempFS.deleteBaseDir();156 })157 .build();158 Regularly sessionCleanup = new Regularly("Session Cleanup Node: " + externalUri);159 sessionCleanup.submit(currentSessions::cleanUp, Duration.ofSeconds(30), Duration.ofSeconds(30));160 Regularly tmpFileCleanup = new Regularly("TempFile Cleanup Node: " + externalUri);161 tmpFileCleanup.submit(tempFileSystems::cleanUp, Duration.ofSeconds(30), Duration.ofSeconds(30));162 Regularly regularHeartBeat = new Regularly("Heartbeat Node: " + externalUri);163 regularHeartBeat.submit(() -> bus.fire(new NodeHeartBeatEvent(getStatus())), heartbeatPeriod,164 heartbeatPeriod);165 bus.addListener(SessionClosedEvent.listener(id -> {166 // Listen to session terminated events so we know when to fire the NodeDrainComplete event167 if (this.isDraining()) {168 int done = pendingSessions.decrementAndGet();169 if (done <= 0) {170 LOG.info("Firing node drain complete message");171 bus.fire(new NodeDrainComplete(this.getId()));172 }173 }174 }));175 Runtime.getRuntime().addShutdownHook(new Thread(this::stopAllSessions));176 new JMXHelper().register(this);177 }178 public static SauceNode.Builder builder(179 Tracer tracer,180 EventBus bus,181 URI uri,182 URI gridUri,183 Secret registrationSecret) {184 return new SauceNode.Builder(tracer, bus, uri, gridUri, registrationSecret);185 }186 @Override187 public boolean isReady() {188 return bus.isReady();189 }190 @VisibleForTesting191 public int getCurrentSessionCount() {192 // It seems wildly unlikely we'll overflow an int193 return Math.toIntExact(currentSessions.size());194 }195 @ManagedAttribute(name = "MaxSessions")196 public int getMaxSessionCount() {197 return maxSessionCount;198 }199 @ManagedAttribute(name = "Status")200 public Availability getAvailability() {201 return isDraining() ? DRAINING : UP;202 }203 @ManagedAttribute(name = "TotalSlots")204 public int getTotalSlots() {205 return factories.size();206 }207 @ManagedAttribute(name = "UsedSlots")208 public long getUsedSlots() {209 return factories.stream().filter(sessionSlot -> !sessionSlot.isAvailable()).count();210 }211 @ManagedAttribute(name = "Load")212 public float getLoad() {213 long inUse = factories.stream().filter(sessionSlot -> !sessionSlot.isAvailable()).count();214 return inUse / (float) maxSessionCount * 100f;215 }216 @ManagedAttribute(name = "RemoteNodeUri")217 public URI getExternalUri() {218 return this.getUri();219 }220 @ManagedAttribute(name = "GridUri")221 public URI getGridUri() {222 return this.gridUri;223 }224 @ManagedAttribute(name = "NodeId")225 public String getNodeId() {226 return getId().toString();227 }228 @Override229 public boolean isSupporting(Capabilities capabilities) {230 return factories.parallelStream().anyMatch(factory -> factory.test(capabilities));231 }232 @Override233 public Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {234 Require.nonNull("Session request", sessionRequest);235 try (Span span = tracer.getCurrentContext().createSpan("node.new_session")) {236 Map<String, EventAttributeValue> attributeMap = new HashMap<>();237 attributeMap238 .put(AttributeKey.LOGGER_CLASS.getKey(), EventAttribute.setValue(getClass().getName()));239 LOG.fine("Creating new session using span: " + span);240 attributeMap.put("session.request.capabilities",241 EventAttribute.setValue(sessionRequest.getDesiredCapabilities().toString()));242 attributeMap.put("session.request.downstreamdialect",243 EventAttribute.setValue(sessionRequest.getDownstreamDialects().toString()));244 int currentSessionCount = getCurrentSessionCount();245 span.setAttribute("current.session.count", currentSessionCount);246 attributeMap.put("current.session.count", EventAttribute.setValue(currentSessionCount));247 if (getCurrentSessionCount() >= maxSessionCount) {248 span.setAttribute("error", true);249 span.setStatus(Status.RESOURCE_EXHAUSTED);250 attributeMap.put("max.session.count", EventAttribute.setValue(maxSessionCount));251 span.addEvent("Max session count reached", attributeMap);252 return Either.left(new RetrySessionRequestException("Max session count reached."));253 }254 if (isDraining()) {255 span.setStatus(Status.UNAVAILABLE.withDescription("The node is draining. Cannot accept new sessions."));256 return Either.left(257 new RetrySessionRequestException("The node is draining. Cannot accept new sessions."));258 }259 // Identify possible slots to use as quickly as possible to enable concurrent session starting260 SessionSlot slotToUse = null;261 synchronized(factories) {262 for (SessionSlot factory : factories) {263 if (!factory.isAvailable() || !factory.test(sessionRequest.getDesiredCapabilities())) {264 continue;265 }266 factory.reserve();267 slotToUse = factory;268 break;269 }270 }271 if (slotToUse == null) {272 span.setAttribute("error", true);273 span.setStatus(Status.NOT_FOUND);274 span.addEvent("No slot matched capabilities ", attributeMap);275 return Either.left(276 new RetrySessionRequestException("No slot matched the requested capabilities."));277 }278 Either<WebDriverException, ActiveSession> possibleSession = slotToUse.apply(sessionRequest);279 if (possibleSession.isRight()) {280 ActiveSession session = possibleSession.right();281 currentSessions.put(session.getId(), slotToUse);282 SessionId sessionId = session.getId();283 Capabilities caps = session.getCapabilities();284 SESSION_ID.accept(span, sessionId);285 CAPABILITIES.accept(span, caps);286 String downstream = session.getDownstreamDialect().toString();287 String upstream = session.getUpstreamDialect().toString();288 String sessionUri = session.getUri().toString();289 span.setAttribute(AttributeKey.DOWNSTREAM_DIALECT.getKey(), downstream);290 span.setAttribute(AttributeKey.UPSTREAM_DIALECT.getKey(), upstream);291 span.setAttribute(AttributeKey.SESSION_URI.getKey(), sessionUri);292 // The session we return has to look like it came from the node, since we might be dealing293 // with a webdriver implementation that only accepts connections from localhost294 boolean isSupportingCdp = slotToUse.isSupportingCdp() ||295 caps.getCapability("se:cdp") != null;296 Session externalSession = createExternalSession(session, externalUri, isSupportingCdp);297 return Either.right(new CreateSessionResponse(298 externalSession,299 getEncoder(session.getDownstreamDialect()).apply(externalSession)));300 } else {301 slotToUse.release();302 span.setAttribute("error", true);303 span.addEvent("Unable to create session with the driver", attributeMap);304 return Either.left(possibleSession.left());305 }306 }307 }308 @Override309 public boolean isSessionOwner(SessionId id) {310 Require.nonNull("Session ID", id);311 return currentSessions.getIfPresent(id) != null;312 }313 @Override314 public Session getSession(SessionId id) throws NoSuchSessionException {315 Require.nonNull("Session ID", id);316 SessionSlot slot = currentSessions.getIfPresent(id);317 if (slot == null) {318 throw new NoSuchSessionException("Cannot find session with id: " + id);319 }320 return createExternalSession(slot.getSession(), externalUri, slot.isSupportingCdp());321 }322 @Override323 public TemporaryFilesystem getTemporaryFilesystem(SessionId id) throws IOException {324 try {325 return tempFileSystems.get(id, () -> TemporaryFilesystem.getTmpFsBasedOn(326 TemporaryFilesystem.getDefaultTmpFS().createTempDir("session", id.toString())));327 } catch (ExecutionException e) {328 throw new IOException(e);329 }330 }331 @Override332 public HttpResponse executeWebDriverCommand(HttpRequest req) {333 // True enough to be good enough334 SessionId id = getSessionId(req.getUri()).map(SessionId::new)335 .orElseThrow(() -> new NoSuchSessionException("Cannot find session: " + req));336 SessionSlot slot = currentSessions.getIfPresent(id);337 if (slot == null) {338 throw new NoSuchSessionException("Cannot find session with id: " + id);339 }340 ActiveSession activeSession = slot.getSession();341 if (activeSession.getClass().getName().contains("RelaySessionFactory")) {342 HttpResponse toReturn = slot.execute(req);343 if (req.getMethod() == DELETE && req.getUri().equals("/session/" + id)) {344 stop(id);345 }346 return toReturn;347 }348 SauceDockerSession session = (SauceDockerSession) activeSession;349 SauceCommandInfo.Builder builder = new SauceCommandInfo.Builder();350 builder.setStartTime(Instant.now().toEpochMilli());351 HttpResponse toReturn = slot.execute(req);352 if (req.getMethod() == DELETE && req.getUri().equals("/session/" + id)) {353 stop(id);354 builder.setScreenshotId(-1);355 } else {356 // Only taking screenshots after a url has been loaded357 if (!session.canTakeScreenshot() && req.getMethod() == POST358 && req.getUri().endsWith("/url")) {359 session.enableScreenshots();360 }361 int screenshotId = takeScreenshot(session, req, slot);362 builder.setScreenshotId(screenshotId);363 }364 Map<String, Object> parsedResponse =365 JSON.toType(new InputStreamReader(toReturn.getContent().get()), MAP_TYPE);366 builder.setRequest(getRequestContents(req))367 .setResult(parsedResponse)368 .setPath(req.getUri().replace(String.format("/session/%s", id), ""))369 .setHttpStatus(toReturn.getStatus())370 .setHttpMethod(req.getMethod().name())371 .setStatusCode(0);372 if (parsedResponse.containsKey("value") && parsedResponse.get("value") != null373 && parsedResponse.get("value").toString().contains("error")) {374 builder.setStatusCode(1);375 }376 builder.setEndTime(Instant.now().toEpochMilli());377 session.addSauceCommandInfo(builder.build());378 return toReturn;379 }380 @Override381 public HttpResponse uploadFile(HttpRequest req, SessionId id) {382 // When the session is running in a Docker container, the upload file command383 // needs to be forwarded to the container as well.384 SessionSlot slot = currentSessions.getIfPresent(id);385 if (slot != null && slot.getSession() instanceof SauceDockerSession) {386 return executeWebDriverCommand(req);387 }388 Map<String, Object> incoming = JSON.toType(string(req), Json.MAP_TYPE);389 File tempDir;390 try {391 TemporaryFilesystem tempfs = getTemporaryFilesystem(id);392 tempDir = tempfs.createTempDir("upload", "file");393 Zip.unzip((String) incoming.get("file"), tempDir);394 } catch (IOException e) {395 throw new UncheckedIOException(e);396 }397 // Select the first file398 File[] allFiles = tempDir.listFiles();399 if (allFiles == null) {400 throw new WebDriverException(401 String.format("Cannot access temporary directory for uploaded files %s", tempDir));402 }403 if (allFiles.length != 1) {404 throw new WebDriverException(405 String.format("Expected there to be only 1 file. There were: %s", allFiles.length));406 }407 ImmutableMap<String, Object> result = ImmutableMap.of(408 "value", allFiles[0].getAbsolutePath());409 return new HttpResponse().setContent(asJson(result));410 }411 @Override412 public void stop(SessionId id) throws NoSuchSessionException {413 Require.nonNull("Session ID", id);414 SessionSlot slot = currentSessions.getIfPresent(id);415 if (slot == null) {416 throw new NoSuchSessionException("Cannot find session with id: " + id);417 }418 currentSessions.invalidate(id);419 tempFileSystems.invalidate(id);420 }421 private void stopAllSessions() {422 if (currentSessions.size() > 0) {423 LOG.info("Trying to stop all running sessions before shutting down...");424 currentSessions.invalidateAll();425 }426 }427 private Session createExternalSession(ActiveSession other, URI externalUri, boolean isSupportingCdp) {428 Capabilities toUse = ImmutableCapabilities.copyOf(other.getCapabilities());429 // Rewrite the se:options if necessary to send the cdp url back430 if (isSupportingCdp) {431 String cdpPath = String.format("/session/%s/se/cdp", other.getId());432 toUse = new PersistentCapabilities(toUse).setCapability("se:cdp", rewrite(cdpPath));433 }434 return new Session(other.getId(), externalUri, other.getStereotype(), toUse, Instant.now());435 }436 private URI rewrite(String path) {437 try {438 String scheme = "https".equals(gridUri.getScheme()) ? "wss" : "ws";439 return new URI(440 scheme,441 gridUri.getUserInfo(),442 gridUri.getHost(),443 gridUri.getPort(),444 path,445 null,446 null);447 } catch (URISyntaxException e) {448 throw new RuntimeException(e);449 }450 }451 @Override452 public NodeStatus getStatus() {453 Set<Slot> slots = factories.stream()454 .map(slot -> {455 Instant lastStarted = Instant.EPOCH;456 Session session = null;457 if (!slot.isAvailable()) {458 ActiveSession activeSession = slot.getSession();459 if (activeSession != null) {460 lastStarted = activeSession.getStartTime();461 session = new Session(462 activeSession.getId(),463 activeSession.getUri(),464 slot.getStereotype(),465 activeSession.getCapabilities(),466 activeSession.getStartTime());467 }468 }469 return new Slot(470 new SlotId(getId(), slot.getId()),471 slot.getStereotype(),472 lastStarted,473 session);474 })475 .collect(toImmutableSet());476 return new NodeStatus(477 getId(),478 externalUri,479 maxSessionCount,480 slots,481 isDraining() ? DRAINING : UP,482 heartbeatPeriod,483 getNodeVersion(),484 getOsInfo());485 }486 @Override487 public HealthCheck getHealthCheck() {488 return healthCheck;489 }490 @Override491 public void drain() {492 bus.fire(new NodeDrainStarted(getId()));493 draining = true;494 int currentSessionCount = getCurrentSessionCount();495 if (currentSessionCount == 0) {496 LOG.info("Firing node drain complete message");497 bus.fire(new NodeDrainComplete(getId()));498 } else {499 pendingSessions.set(currentSessionCount);500 }501 }502 private Map<String, Object> toJson() {503 return ImmutableMap.of(504 "id", getId(),505 "uri", externalUri,506 "maxSessions", maxSessionCount,507 "draining", isDraining(),508 "capabilities", factories.stream()509 .map(SessionSlot::getStereotype)510 .collect(Collectors.toSet()));511 }512 public static class Builder {513 private final Tracer tracer;514 private final EventBus bus;515 private final URI uri;516 private final URI gridUri;517 private final Secret registrationSecret;518 private final ImmutableList.Builder<SessionSlot> factories;519 private int maxCount = Runtime.getRuntime().availableProcessors() * 5;520 private Ticker ticker = Ticker.systemTicker();521 private Duration sessionTimeout = Duration.ofMinutes(5);...

Full Screen

Full Screen

Source:LocalNode.java Github

copy

Full Screen

...30import org.openqa.selenium.concurrent.Regularly;31import org.openqa.selenium.events.EventBus;32import org.openqa.selenium.grid.data.CreateSessionRequest;33import org.openqa.selenium.grid.data.CreateSessionResponse;34import org.openqa.selenium.grid.data.NodeDrainComplete;35import org.openqa.selenium.grid.data.NodeDrainStarted;36import org.openqa.selenium.grid.data.NodeId;37import org.openqa.selenium.grid.data.NodeStatus;38import org.openqa.selenium.grid.data.Session;39import org.openqa.selenium.grid.data.SessionClosedEvent;40import org.openqa.selenium.grid.data.Slot;41import org.openqa.selenium.grid.data.SlotId;42import org.openqa.selenium.grid.node.ActiveSession;43import org.openqa.selenium.grid.node.HealthCheck;44import org.openqa.selenium.grid.node.Node;45import org.openqa.selenium.grid.node.SessionFactory;46import org.openqa.selenium.grid.security.Secret;47import org.openqa.selenium.internal.Require;48import org.openqa.selenium.io.TemporaryFilesystem;49import org.openqa.selenium.io.Zip;50import org.openqa.selenium.json.Json;51import org.openqa.selenium.remote.SessionId;52import org.openqa.selenium.remote.http.HttpRequest;53import org.openqa.selenium.remote.http.HttpResponse;54import org.openqa.selenium.remote.tracing.AttributeKey;55import org.openqa.selenium.remote.tracing.EventAttribute;56import org.openqa.selenium.remote.tracing.EventAttributeValue;57import org.openqa.selenium.remote.tracing.Span;58import org.openqa.selenium.remote.tracing.Status;59import org.openqa.selenium.remote.tracing.Tracer;60import java.io.File;61import java.io.IOException;62import java.io.UncheckedIOException;63import java.net.URI;64import java.net.URISyntaxException;65import java.time.Clock;66import java.time.Duration;67import java.time.Instant;68import java.util.HashMap;69import java.util.List;70import java.util.Map;71import java.util.Optional;72import java.util.Set;73import java.util.TreeMap;74import java.util.UUID;75import java.util.concurrent.ExecutionException;76import java.util.concurrent.atomic.AtomicInteger;77import java.util.logging.Logger;78import java.util.stream.Collectors;79import static com.google.common.collect.ImmutableSet.toImmutableSet;80import static org.openqa.selenium.grid.data.Availability.DRAINING;81import static org.openqa.selenium.grid.data.Availability.UP;82import static org.openqa.selenium.grid.node.CapabilityResponseEncoder.getEncoder;83import static org.openqa.selenium.remote.HttpSessionId.getSessionId;84import static org.openqa.selenium.remote.RemoteTags.CAPABILITIES;85import static org.openqa.selenium.remote.RemoteTags.CAPABILITIES_EVENT;86import static org.openqa.selenium.remote.RemoteTags.SESSION_ID;87import static org.openqa.selenium.remote.RemoteTags.SESSION_ID_EVENT;88import static org.openqa.selenium.remote.http.Contents.asJson;89import static org.openqa.selenium.remote.http.Contents.string;90import static org.openqa.selenium.remote.http.HttpMethod.DELETE;91public class LocalNode extends Node {92 private static final Json JSON = new Json();93 private static final Logger LOG = Logger.getLogger(LocalNode.class.getName());94 private final EventBus bus;95 private final URI externalUri;96 private final URI gridUri;97 private final HealthCheck healthCheck;98 private final int maxSessionCount;99 private final List<SessionSlot> factories;100 private final Cache<SessionId, SessionSlot> currentSessions;101 private final Cache<SessionId, TemporaryFilesystem> tempFileSystems;102 private final Regularly regularly;103 private final Secret registrationSecret;104 private AtomicInteger pendingSessions = new AtomicInteger();105 private LocalNode(106 Tracer tracer,107 EventBus bus,108 URI uri,109 URI gridUri,110 HealthCheck healthCheck,111 int maxSessionCount,112 Ticker ticker,113 Duration sessionTimeout,114 List<SessionSlot> factories,115 Secret registrationSecret) {116 super(tracer, new NodeId(UUID.randomUUID()), uri, registrationSecret);117 this.bus = Require.nonNull("Event bus", bus);118 this.externalUri = Require.nonNull("Remote node URI", uri);119 this.gridUri = Require.nonNull("Grid URI", gridUri);120 this.maxSessionCount = Math.min(Require.positive("Max session count", maxSessionCount), factories.size());121 this.factories = ImmutableList.copyOf(factories);122 this.registrationSecret = registrationSecret;123 this.healthCheck = healthCheck == null ?124 () -> new HealthCheck.Result(125 isDraining() ? DRAINING : UP,126 String.format("%s is %s", uri, isDraining() ? "draining" : "up")) :127 healthCheck;128 this.currentSessions = CacheBuilder.newBuilder()129 .expireAfterAccess(sessionTimeout)130 .ticker(ticker)131 .removalListener((RemovalListener<SessionId, SessionSlot>) notification -> {132 // If we were invoked explicitly, then return: we know what we're doing.133 if (!notification.wasEvicted()) {134 return;135 }136 killSession(notification.getValue());137 })138 .build();139 this.tempFileSystems = CacheBuilder.newBuilder()140 .expireAfterAccess(sessionTimeout)141 .ticker(ticker)142 .removalListener((RemovalListener<SessionId, TemporaryFilesystem>) notification -> {143 TemporaryFilesystem tempFS = notification.getValue();144 tempFS.deleteTemporaryFiles();145 tempFS.deleteBaseDir();146 })147 .build();148 this.regularly = new Regularly("Local Node: " + externalUri);149 regularly.submit(currentSessions::cleanUp, Duration.ofSeconds(30), Duration.ofSeconds(30));150 regularly.submit(tempFileSystems::cleanUp, Duration.ofSeconds(30), Duration.ofSeconds(30));151 bus.addListener(SessionClosedEvent.listener(id -> {152 try {153 this.stop(id);154 } catch (NoSuchSessionException ignore) {155 }156 if (this.isDraining()) {157 int done = pendingSessions.decrementAndGet();158 if (done <= 0) {159 LOG.info("Firing node drain complete message");160 bus.fire(new NodeDrainComplete(this.getId()));161 }162 }163 }));164 }165 @Override166 public boolean isReady() {167 return bus.isReady();168 }169 @VisibleForTesting170 public int getCurrentSessionCount() {171 // It seems wildly unlikely we'll overflow an int172 return Math.toIntExact(currentSessions.size());173 }174 @Override175 public boolean isSupporting(Capabilities capabilities) {176 return factories.parallelStream().anyMatch(factory -> factory.test(capabilities));177 }178 @Override179 public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {180 Require.nonNull("Session request", sessionRequest);181 try (Span span = tracer.getCurrentContext().createSpan("node.new_session")) {182 Map<String, EventAttributeValue> attributeMap = new HashMap<>();183 attributeMap184 .put(AttributeKey.LOGGER_CLASS.getKey(), EventAttribute.setValue(getClass().getName()));185 LOG.fine("Creating new session using span: " + span);186 attributeMap.put("session.request.capabilities",187 EventAttribute.setValue(sessionRequest.getCapabilities().toString()));188 attributeMap.put("session.request.downstreamdialect",189 EventAttribute.setValue(sessionRequest.getDownstreamDialects().toString()));190 int currentSessionCount = getCurrentSessionCount();191 span.setAttribute("current.session.count", currentSessionCount);192 attributeMap.put("current.session.count", EventAttribute.setValue(currentSessionCount));193 if (getCurrentSessionCount() >= maxSessionCount) {194 span.setAttribute("error", true);195 span.setStatus(Status.RESOURCE_EXHAUSTED);196 attributeMap.put("max.session.count", EventAttribute.setValue(maxSessionCount));197 span.addEvent("Max session count reached", attributeMap);198 return Optional.empty();199 }200 if (isDraining()) {201 span.setStatus(Status.UNAVAILABLE.withDescription("The node is draining. Cannot accept new sessions."));202 return Optional.empty();203 }204 // Identify possible slots to use as quickly as possible to enable concurrent session starting205 SessionSlot slotToUse = null;206 synchronized(factories) {207 for (SessionSlot factory : factories) {208 if (!factory.isAvailable() || !factory.test(sessionRequest.getCapabilities())) {209 continue;210 }211 factory.reserve();212 slotToUse = factory;213 break;214 }215 }216 if (slotToUse == null) {217 span.setAttribute("error", true);218 span.setStatus(Status.NOT_FOUND);219 span.addEvent("No slot matched capabilities ", attributeMap);220 return Optional.empty();221 }222 Optional<ActiveSession> possibleSession = slotToUse.apply(sessionRequest);223 if (!possibleSession.isPresent()) {224 slotToUse.release();225 span.setAttribute("error", true);226 span.setStatus(Status.NOT_FOUND);227 span.addEvent("No slots available for capabilities ", attributeMap);228 return Optional.empty();229 }230 ActiveSession session = possibleSession.get();231 currentSessions.put(session.getId(), slotToUse);232 SessionId sessionId = session.getId();233 Capabilities caps = session.getCapabilities();234 SESSION_ID.accept(span, sessionId);235 CAPABILITIES.accept(span, caps);236 SESSION_ID_EVENT.accept(attributeMap, sessionId);237 CAPABILITIES_EVENT.accept(attributeMap, caps);238 String downstream = session.getDownstreamDialect().toString();239 String upstream = session.getUpstreamDialect().toString();240 String sessionUri = session.getUri().toString();241 span.setAttribute(AttributeKey.DOWNSTREAM_DIALECT.getKey(), downstream);242 span.setAttribute(AttributeKey.UPSTREAM_DIALECT.getKey(), upstream);243 span.setAttribute(AttributeKey.SESSION_URI.getKey(), sessionUri);244 attributeMap.put(AttributeKey.DOWNSTREAM_DIALECT.getKey(), EventAttribute.setValue(downstream));245 attributeMap.put(AttributeKey.UPSTREAM_DIALECT.getKey(), EventAttribute.setValue(upstream));246 attributeMap.put(AttributeKey.SESSION_URI.getKey(), EventAttribute.setValue(sessionUri));247 span.addEvent("Session created by node", attributeMap);248 // The session we return has to look like it came from the node, since we might be dealing249 // with a webdriver implementation that only accepts connections from localhost250 Session externalSession = createExternalSession(session, externalUri);251 return Optional.of(new CreateSessionResponse(252 externalSession,253 getEncoder(session.getDownstreamDialect()).apply(externalSession)));254 }255 }256 @Override257 public boolean isSessionOwner(SessionId id) {258 Require.nonNull("Session ID", id);259 return currentSessions.getIfPresent(id) != null;260 }261 @Override262 public Session getSession(SessionId id) throws NoSuchSessionException {263 Require.nonNull("Session ID", id);264 SessionSlot slot = currentSessions.getIfPresent(id);265 if (slot == null) {266 throw new NoSuchSessionException("Cannot find session with id: " + id);267 }268 return createExternalSession(slot.getSession(), externalUri);269 }270 @Override271 public TemporaryFilesystem getTemporaryFilesystem(SessionId id) throws IOException {272 try {273 return tempFileSystems.get(id, () -> TemporaryFilesystem.getTmpFsBasedOn(274 TemporaryFilesystem.getDefaultTmpFS().createTempDir("session", id.toString())));275 } catch (ExecutionException e) {276 throw new IOException(e);277 }278 }279 @Override280 public HttpResponse executeWebDriverCommand(HttpRequest req) {281 // True enough to be good enough282 SessionId id = getSessionId(req.getUri()).map(SessionId::new)283 .orElseThrow(() -> new NoSuchSessionException("Cannot find session: " + req));284 SessionSlot slot = currentSessions.getIfPresent(id);285 if (slot == null) {286 throw new NoSuchSessionException("Cannot find session with id: " + id);287 }288 HttpResponse toReturn = slot.execute(req);289 if (req.getMethod() == DELETE && req.getUri().equals("/session/" + id)) {290 stop(id);291 }292 return toReturn;293 }294 @Override295 public HttpResponse uploadFile(HttpRequest req, SessionId id) {296 Map<String, Object> incoming = JSON.toType(string(req), Json.MAP_TYPE);297 File tempDir;298 try {299 TemporaryFilesystem tempfs = getTemporaryFilesystem(id);300 tempDir = tempfs.createTempDir("upload", "file");301 Zip.unzip((String) incoming.get("file"), tempDir);302 } catch (IOException e) {303 throw new UncheckedIOException(e);304 }305 // Select the first file306 File[] allFiles = tempDir.listFiles();307 if (allFiles == null) {308 throw new WebDriverException(309 String.format("Cannot access temporary directory for uploaded files %s", tempDir));310 }311 if (allFiles.length != 1) {312 throw new WebDriverException(313 String.format("Expected there to be only 1 file. There were: %s", allFiles.length));314 }315 ImmutableMap<String, Object> result = ImmutableMap.of(316 "value", allFiles[0].getAbsolutePath());317 return new HttpResponse().setContent(asJson(result));318 }319 @Override320 public void stop(SessionId id) throws NoSuchSessionException {321 Require.nonNull("Session ID", id);322 SessionSlot slot = currentSessions.getIfPresent(id);323 if (slot == null) {324 throw new NoSuchSessionException("Cannot find session with id: " + id);325 }326 killSession(slot);327 tempFileSystems.invalidate(id);328 }329 private Session createExternalSession(ActiveSession other, URI externalUri) {330 Capabilities toUse = ImmutableCapabilities.copyOf(other.getCapabilities());331 // Rewrite the se:options if necessary332 Object rawSeleniumOptions = other.getCapabilities().getCapability("se:options");333 if (rawSeleniumOptions instanceof Map) {334 @SuppressWarnings("unchecked") Map<String, Object> original = (Map<String, Object>) rawSeleniumOptions;335 Map<String, Object> updated = new TreeMap<>(original);336 Object cdp = original.get("cdp");337 String cdpPath = String.format("/session/%s/se/cdp", other.getId());338 updated.put("cdp", rewrite(cdpPath));339 toUse = new PersistentCapabilities(toUse).setCapability("se:options", updated);340 }341 return new Session(other.getId(), externalUri, other.getStereotype(), toUse, Instant.now());342 }343 private URI rewrite(String path) {344 try {345 return new URI(346 gridUri.getScheme(),347 gridUri.getUserInfo(),348 gridUri.getHost(),349 gridUri.getPort(),350 path,351 null,352 null);353 } catch (URISyntaxException e) {354 throw new RuntimeException(e);355 }356 }357 private void killSession(SessionSlot slot) {358 currentSessions.invalidate(slot.getSession().getId());359 // Attempt to stop the session360 if (!slot.isAvailable()) {361 slot.stop();362 }363 }364 @Override365 public NodeStatus getStatus() {366 Set<Slot> slots = factories.stream()367 .map(slot -> {368 Optional<Session> session = Optional.empty();369 if (!slot.isAvailable()) {370 ActiveSession activeSession = slot.getSession();371 session = Optional.of(372 new Session(373 activeSession.getId(),374 activeSession.getUri(),375 slot.getStereotype(),376 activeSession.getCapabilities(),377 activeSession.getStartTime()));378 }379 return new Slot(380 new SlotId(getId(), slot.getId()),381 slot.getStereotype(),382 Instant.EPOCH,383 session);384 })385 .collect(toImmutableSet());386 return new NodeStatus(387 getId(),388 externalUri,389 maxSessionCount,390 slots,391 isDraining() ? DRAINING : UP,392 registrationSecret);393 }394 @Override395 public HealthCheck getHealthCheck() {396 return healthCheck;397 }398 @Override399 public void drain() {400 bus.fire(new NodeDrainStarted(getId()));401 draining = true;402 int currentSessionCount = getCurrentSessionCount();403 if (currentSessionCount == 0) {404 LOG.info("Firing node drain complete message");405 bus.fire(new NodeDrainComplete(getId()));406 } else {407 pendingSessions.set(currentSessionCount);408 }409 }410 private Map<String, Object> toJson() {411 return ImmutableMap.of(412 "id", getId(),413 "uri", externalUri,414 "maxSessions", maxSessionCount,415 "draining", isDraining(),416 "capabilities", factories.stream()417 .map(SessionSlot::getStereotype)418 .collect(Collectors.toSet()));419 }420 public static Builder builder(421 Tracer tracer,422 EventBus bus,423 URI uri,424 URI gridUri,425 Secret registrationSecret) {426 return new Builder(tracer, bus, uri, gridUri, registrationSecret);427 }428 public static class Builder {429 private final Tracer tracer;...

Full Screen

Full Screen

Source:KubernetesNode.java Github

copy

Full Screen

...92 sessionCleanup.submit(currentSessions::cleanUp, Duration.ofSeconds(30), Duration.ofSeconds(30));93 var regularHeartBeat = new Regularly("Heartbeat Node: " + uri);94 regularHeartBeat.submit(() -> bus.fire(new NodeHeartBeatEvent(getStatus())), heartbeatPeriod, heartbeatPeriod);95 bus.addListener(SessionClosedEvent.listener(id -> {96 if (this.isDraining() && pendingSessions.decrementAndGet() <= 0) {97 LOG.info("Firing node drain complete message");98 bus.fire(new NodeDrainComplete(this.getId()));99 }100 }));101 additionalRoutes = combine(102 get("/downloads/{sessionId}/{fileName}")103 .to(params -> new GetFile(getActiveSession(sessionIdFrom(params)), fileNameFrom(params))),104 delete("/downloads/{sessionId}/{fileName}")105 .to(params -> new DeleteFile(getActiveSession(sessionIdFrom(params)), fileNameFrom(params))),106 get("/downloads/{sessionId}")107 .to(params -> new ListFiles(getActiveSession(sessionIdFrom(params)))),108 delete("/downloads/{sessionId}")109 .to(params -> new DeleteFiles(getActiveSession(sessionIdFrom(params)))));110 Runtime.getRuntime().addShutdownHook(new Thread(this::stopAllSessions));111 new JMXHelper().register(this);112 }113 public static Node create(Config config) {114 var loggingOptions = new LoggingOptions(config);115 var eventOptions = new EventBusOptions(config);116 var serverOptions = new BaseServerOptions(config);117 var secretOptions = new SecretOptions(config);118 var networkOptions = new NetworkOptions(config);119 var k8sOptions = new KubernetesOptions(config);120 var tracer = loggingOptions.getTracer();121 var bus = eventOptions.getEventBus();122 var clientFactory = networkOptions.getHttpClientFactory(tracer);123 var k8s = new KubernetesDriver(new DefaultKubernetesClient());124 var factories = createFactories(k8sOptions, tracer, clientFactory, bus, k8s);125 LOG.info("Creating kubernetes node");126 return new KubernetesNode(tracer, bus, secretOptions.getRegistrationSecret(), new NodeId(UUID.randomUUID()),127 serverOptions.getExternalUri(), factories, k8sOptions.getMaxSessions(), k8sOptions.getSessionTimeout(),128 k8sOptions.getHeartbeatPeriod()129 );130 }131 static List<SessionSlot> createFactories(KubernetesOptions k8sOptions, Tracer tracer,132 HttpClient.Factory clientFactory, EventBus eventBus,133 KubernetesDriver driver) {134 var configs = k8sOptions.getConfigs();135 if (configs.isEmpty()) {136 throw new ConfigException("Unable to find kubernetes configs");137 }138 return configs.stream().flatMap(c -> Collections.nCopies(k8sOptions.getMaxSessions(), c).stream())139 .map(config -> {140 var image = config.getImage();141 var stereoType = config.getStereoType();142 var factory = new KubernetesSessionFactory(tracer, clientFactory, driver,143 k8sOptions.getWorkerResourceRequests(), image, stereoType, k8sOptions.getVideoImage(),144 k8sOptions.getVideosPath());145 return new SessionSlot(eventBus, stereoType, factory);146 }).collect(Collectors.toList());147 }148 @Override149 public Either<WebDriverException, CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {150 try (var span = tracer.getCurrentContext().createSpan("kubernetes_node.new_session")) {151 Map<String, EventAttributeValue> attributeMap = new HashMap<>();152 attributeMap.put(LOGGER_CLASS.getKey(), setValue(getClass().getName()));153 attributeMap.put("session.request.capabilities", setValue(sessionRequest.getDesiredCapabilities().toString()));154 attributeMap.put("session.request.downstreamdialect", setValue(sessionRequest.getDownstreamDialects()155 .toString()));156 var currentSessionCount = getCurrentSessionCount();157 span.setAttribute("current.session.count", currentSessionCount);158 attributeMap.put("current.session.count", setValue(currentSessionCount));159 if (getCurrentSessionCount() >= maxSessionCount) {160 span.setAttribute("error", true);161 span.setStatus(Status.RESOURCE_EXHAUSTED);162 attributeMap.put("max.session.count", setValue(maxSessionCount));163 span.addEvent("Max session count reached", attributeMap);164 return Either.left(new RetrySessionRequestException("Max session count reached."));165 }166 SessionSlot slotToUse = null;167 synchronized (factories) {168 for (var factory : factories) {169 if (!factory.isAvailable() || !factory.test(sessionRequest.getDesiredCapabilities())) {170 continue;171 }172 factory.reserve();173 slotToUse = factory;174 break;175 }176 }177 if (slotToUse == null) {178 span.setAttribute("error", true);179 span.setStatus(Status.NOT_FOUND);180 span.addEvent("No slot matched the requested capabilities. ", attributeMap);181 return Either.left(new RetrySessionRequestException("No slot matched the requested capabilities."));182 }183 Either<WebDriverException, ActiveSession> possibleSession = slotToUse.apply(sessionRequest);184 if (possibleSession.isRight()) {185 var session = possibleSession.right();186 currentSessions.put(session.getId(), slotToUse);187 SESSION_ID.accept(span, session.getId());188 var caps = session.getCapabilities();189 CAPABILITIES.accept(span, caps);190 span.setAttribute(DOWNSTREAM_DIALECT.getKey(), session.getDownstreamDialect().toString());191 span.setAttribute(UPSTREAM_DIALECT.getKey(), session.getUpstreamDialect().toString());192 span.setAttribute(SESSION_URI.getKey(), session.getUri().toString());193 var isSupportingCdp = slotToUse.isSupportingCdp() || caps.getCapability("se:cdp") != null;194 var externalSession = createExternalSession(session, uri, isSupportingCdp);195 return Either.right(new CreateSessionResponse(externalSession,196 getEncoder(session.getDownstreamDialect()).apply(externalSession)));197 } else {198 slotToUse.release();199 span.setAttribute("error", true);200 span.addEvent("Unable to create session with the driver", attributeMap);201 return Either.left(possibleSession.left());202 }203 }204 }205 @Override206 public HttpResponse executeWebDriverCommand(HttpRequest req) {207 var id = getSessionId(req.getUri()).map(SessionId::new)208 .orElseThrow(() -> new NoSuchSessionException("Cannot find session: " + req));209 var slot = getSessionSlot(id);210 var toReturn = slot.execute(req);211 if (req.getMethod() == DELETE && req.getUri().equals("/session/" + id)) {212 stop(id);213 }214 return toReturn;215 }216 @Override217 public Session getSession(SessionId id) throws NoSuchSessionException {218 var slot = getSessionSlot(id);219 return createExternalSession(slot.getSession(), uri, slot.isSupportingCdp());220 }221 @Override222 public HttpResponse uploadFile(HttpRequest req, SessionId id) {223 return executeWebDriverCommand(req);224 }225 @Override226 public void stop(SessionId id) throws NoSuchSessionException {227 getSessionSlot(id);228 currentSessions.invalidate(id);229 }230 @Override231 public boolean isSessionOwner(SessionId id) {232 return currentSessions.getIfPresent(id) != null;233 }234 @Override235 public boolean isSupporting(Capabilities capabilities) {236 return factories.parallelStream().anyMatch(factory -> factory.test(capabilities));237 }238 @Override239 public NodeStatus getStatus() {240 return new NodeStatus(getId(), uri, maxSessionCount, getSlots(), isDraining() ? DRAINING : UP, heartbeatPeriod,241 getNodeVersion(), getOsInfo());242 }243 @Override244 public HealthCheck getHealthCheck() {245 var availability = isDraining() ? DRAINING : UP;246 return () -> new HealthCheck.Result(availability, String.format("%s is %s", uri,247 availability.name().toLowerCase()));248 }249 @Override250 public void drain() {251 bus.fire(new NodeDrainStarted(getId()));252 draining = true;253 var currentSessionCount = getCurrentSessionCount();254 if (currentSessionCount == 0) {255 LOG.info("Firing node drain complete message");256 bus.fire(new NodeDrainComplete(getId()));257 } else {258 pendingSessions.set(currentSessionCount);259 }260 }261 @Override262 public boolean isReady() {263 return bus.isReady();264 }265 @ManagedAttribute(name = "CurrentSessions")266 public int getCurrentSessionCount() {267 return Math.toIntExact(currentSessions.size());268 }269 @Override270 public boolean matches(HttpRequest req) {...

Full Screen

Full Screen

Source:OneShotNode.java Github

copy

Full Screen

...27import org.openqa.selenium.grid.config.Config;28import org.openqa.selenium.grid.config.ConfigException;29import org.openqa.selenium.grid.data.CreateSessionRequest;30import org.openqa.selenium.grid.data.CreateSessionResponse;31import org.openqa.selenium.grid.data.NodeDrainComplete;32import org.openqa.selenium.grid.data.NodeDrainStarted;33import org.openqa.selenium.grid.data.NodeId;34import org.openqa.selenium.grid.data.NodeStatus;35import org.openqa.selenium.grid.data.Session;36import org.openqa.selenium.grid.data.SessionClosedEvent;37import org.openqa.selenium.grid.data.Slot;38import org.openqa.selenium.grid.data.SlotId;39import org.openqa.selenium.grid.log.LoggingOptions;40import org.openqa.selenium.grid.node.HealthCheck;41import org.openqa.selenium.grid.node.Node;42import org.openqa.selenium.grid.node.config.NodeOptions;43import org.openqa.selenium.grid.security.Secret;44import org.openqa.selenium.grid.server.BaseServerOptions;45import org.openqa.selenium.grid.server.EventBusOptions;46import org.openqa.selenium.internal.Require;47import org.openqa.selenium.json.Json;48import org.openqa.selenium.remote.CommandExecutor;49import org.openqa.selenium.remote.RemoteWebDriver;50import org.openqa.selenium.remote.SessionId;51import org.openqa.selenium.remote.http.HttpClient;52import org.openqa.selenium.remote.http.HttpRequest;53import org.openqa.selenium.remote.http.HttpResponse;54import org.openqa.selenium.remote.tracing.Tracer;55import java.lang.reflect.Field;56import java.net.URI;57import java.net.URISyntaxException;58import java.time.Instant;59import java.util.HashMap;60import java.util.Map;61import java.util.Optional;62import java.util.ServiceLoader;63import java.util.TreeMap;64import java.util.UUID;65import java.util.logging.Logger;66import java.util.stream.StreamSupport;67import static java.nio.charset.StandardCharsets.UTF_8;68import static org.openqa.selenium.grid.data.Availability.DRAINING;69import static org.openqa.selenium.grid.data.Availability.UP;70import static org.openqa.selenium.json.Json.MAP_TYPE;71import static org.openqa.selenium.remote.http.HttpMethod.DELETE;72/**73 * An implementation of {@link Node} that marks itself as draining immediately74 * after starting, and which then shuts down after usage. This will allow an75 * appropriately configured Kubernetes cluster to start a new node once the76 * session is finished.77 */78public class OneShotNode extends Node {79 private static final Logger LOG = Logger.getLogger(OneShotNode.class.getName());80 private static final Json JSON = new Json();81 private final EventBus events;82 private final WebDriverInfo driverInfo;83 private final Capabilities stereotype;84 private final Secret registrationSecret;85 private final URI gridUri;86 private final UUID slotId = UUID.randomUUID();87 private RemoteWebDriver driver;88 private SessionId sessionId;89 private HttpClient client;90 private Capabilities capabilities;91 private Instant sessionStart = Instant.EPOCH;92 private OneShotNode(93 Tracer tracer,94 EventBus events,95 Secret registrationSecret,96 NodeId id,97 URI uri,98 URI gridUri,99 Capabilities stereotype,100 WebDriverInfo driverInfo) {101 super(tracer, id, uri, registrationSecret);102 this.registrationSecret = registrationSecret;103 this.events = Require.nonNull("Event bus", events);104 this.gridUri = Require.nonNull("Public Grid URI", gridUri);105 this.stereotype = ImmutableCapabilities.copyOf(Require.nonNull("Stereotype", stereotype));106 this.driverInfo = Require.nonNull("Driver info", driverInfo);107 }108 public static Node create(Config config) {109 LoggingOptions loggingOptions = new LoggingOptions(config);110 EventBusOptions eventOptions = new EventBusOptions(config);111 BaseServerOptions serverOptions = new BaseServerOptions(config);112 NodeOptions nodeOptions = new NodeOptions(config);113 Map<String, Object> raw = new Json().toType(114 config.get("k8s", "stereotype")115 .orElseThrow(() -> new ConfigException("Unable to find node stereotype")),116 MAP_TYPE);117 Capabilities stereotype = new ImmutableCapabilities(raw);118 Optional<String> driverName = config.get("k8s", "driver_name").map(String::toLowerCase);119 // Find the webdriver info corresponding to the driver name120 WebDriverInfo driverInfo = StreamSupport.stream(ServiceLoader.load(WebDriverInfo.class).spliterator(), false)121 .filter(info -> info.isSupporting(stereotype))122 .filter(info -> driverName.map(name -> name.equals(info.getDisplayName().toLowerCase())).orElse(true))123 .findFirst()124 .orElseThrow(() -> new ConfigException(125 "Unable to find matching driver for %s and %s", stereotype, driverName.orElse("any driver")));126 LOG.info(String.format("Creating one-shot node for %s with stereotype %s", driverInfo, stereotype));127 LOG.info("Grid URI is: " + nodeOptions.getPublicGridUri());128 return new OneShotNode(129 loggingOptions.getTracer(),130 eventOptions.getEventBus(),131 serverOptions.getRegistrationSecret(),132 new NodeId(UUID.randomUUID()),133 serverOptions.getExternalUri(),134 nodeOptions.getPublicGridUri().orElseThrow(() -> new ConfigException("Unable to determine public grid address")),135 stereotype,136 driverInfo);137 }138 @Override139 public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {140 if (driver != null) {141 throw new IllegalStateException("Only expected one session at a time");142 }143 Optional<WebDriver> driver = driverInfo.createDriver(sessionRequest.getCapabilities());144 if (!driver.isPresent()) {145 return Optional.empty();146 }147 if (!(driver.get() instanceof RemoteWebDriver)) {148 driver.get().quit();149 return Optional.empty();150 }151 this.driver = (RemoteWebDriver) driver.get();152 this.sessionId = this.driver.getSessionId();153 this.client = extractHttpClient(this.driver);154 this.capabilities = rewriteCapabilities(this.driver);155 this.sessionStart = Instant.now();156 LOG.info("Encoded response: " + JSON.toJson(ImmutableMap.of(157 "value", ImmutableMap.of(158 "sessionId", sessionId,159 "capabilities", capabilities))));160 events.fire(new NodeDrainStarted(getId()));161 return Optional.of(162 new CreateSessionResponse(163 getSession(sessionId),164 JSON.toJson(ImmutableMap.of(165 "value", ImmutableMap.of(166 "sessionId", sessionId,167 "capabilities", capabilities))).getBytes(UTF_8)));168 }169 private HttpClient extractHttpClient(RemoteWebDriver driver) {170 CommandExecutor executor = driver.getCommandExecutor();171 try {172 Field client = null;173 Class<?> current = executor.getClass();174 while (client == null && (current != null || Object.class.equals(current))) {175 client = findClientField(current);176 current = current.getSuperclass();177 }178 if (client == null) {179 throw new IllegalStateException("Unable to find client field in " + executor.getClass());180 }181 if (!HttpClient.class.isAssignableFrom(client.getType())) {182 throw new IllegalStateException("Client field is not assignable to http client");183 }184 client.setAccessible(true);185 return (HttpClient) client.get(executor);186 } catch (ReflectiveOperationException e) {187 throw new IllegalStateException(e);188 }189 }190 private Field findClientField(Class<?> clazz) {191 try {192 return clazz.getDeclaredField("client");193 } catch (NoSuchFieldException e) {194 return null;195 }196 }197 private Capabilities rewriteCapabilities(RemoteWebDriver driver) {198 // Rewrite the se:options if necessary199 Object rawSeleniumOptions = driver.getCapabilities().getCapability("se:options");200 if (rawSeleniumOptions == null || rawSeleniumOptions instanceof Map) {201 @SuppressWarnings("unchecked") Map<String, Object> original = (Map<String, Object>) rawSeleniumOptions;202 Map<String, Object> updated = new TreeMap<>(original == null ? new HashMap<>() : original);203 String cdpPath = String.format("/session/%s/se/cdp", driver.getSessionId());204 updated.put("cdp", rewrite(cdpPath));205 return new PersistentCapabilities(driver.getCapabilities()).setCapability("se:options", updated);206 }207 return ImmutableCapabilities.copyOf(driver.getCapabilities());208 }209 private URI rewrite(String path) {210 try {211 return new URI(212 gridUri.getScheme(),213 gridUri.getUserInfo(),214 gridUri.getHost(),215 gridUri.getPort(),216 path,217 null,218 null);219 } catch (URISyntaxException e) {220 throw new RuntimeException(e);221 }222 }223 @Override224 public HttpResponse executeWebDriverCommand(HttpRequest req) {225 LOG.info("Executing " + req);226 HttpResponse res = client.execute(req);227 if (DELETE.equals(req.getMethod()) && req.getUri().equals("/session/" + sessionId)) {228 // Ensure the response is sent before we viciously kill the node229 new Thread(230 () -> {231 try {232 Thread.sleep(500);233 } catch (InterruptedException e) {234 Thread.currentThread().interrupt();235 throw new RuntimeException(e);236 }237 LOG.info("Stopping session: " + sessionId);238 stop(sessionId);239 },240 "Node clean up: " + getId())241 .start();242 }243 return res;244 }245 @Override246 public Session getSession(SessionId id) throws NoSuchSessionException {247 if (!isSessionOwner(id)) {248 throw new NoSuchSessionException("Unable to find session with id: " + id);249 }250 return new Session(251 sessionId,252 getUri(),253 stereotype,254 capabilities,255 sessionStart); }256 @Override257 public HttpResponse uploadFile(HttpRequest req, SessionId id) {258 return null;259 }260 @Override261 public void stop(SessionId id) throws NoSuchSessionException {262 LOG.info("Stop has been called: " + id);263 Require.nonNull("Session ID", id);264 if (!isSessionOwner(id)) {265 throw new NoSuchSessionException("Unable to find session " + id);266 }267 LOG.info("Quitting session " + id);268 try {269 driver.quit();270 } catch (Exception e) {271 // It's possible that the driver has already quit.272 }273 events.fire(new SessionClosedEvent(id));274 LOG.info("Firing node drain complete message");275 events.fire(new NodeDrainComplete(getId()));276 }277 @Override278 public boolean isSessionOwner(SessionId id) {279 return driver != null && sessionId.equals(id);280 }281 @Override282 public boolean isSupporting(Capabilities capabilities) {283 return driverInfo.isSupporting(capabilities);284 }285 @Override286 public NodeStatus getStatus() {287 return new NodeStatus(288 getId(),289 getUri(),290 1,291 ImmutableSet.of(292 new Slot(293 new SlotId(getId(), slotId),294 stereotype,295 Instant.EPOCH,296 driver == null ?297 Optional.empty() :298 Optional.of(new Session(sessionId, getUri(), stereotype, capabilities, Instant.now())))),299 isDraining() ? DRAINING : UP,300 registrationSecret);301 }302 @Override303 public void drain() {304 events.fire(new NodeDrainStarted(getId()));305 draining = true;306 }307 @Override308 public HealthCheck getHealthCheck() {309 return () -> new HealthCheck.Result(isDraining() ? DRAINING : UP, "Everything is fine");310 }311 @Override312 public boolean isReady() {313 return events.isReady();314 }315}...

Full Screen

Full Screen

Source:LocalDistributor.java Github

copy

Full Screen

...26import org.openqa.selenium.grid.data.CreateSessionRequest;27import org.openqa.selenium.grid.data.CreateSessionResponse;28import org.openqa.selenium.grid.data.DistributorStatus;29import org.openqa.selenium.grid.data.NodeAddedEvent;30import org.openqa.selenium.grid.data.NodeDrainComplete;31import org.openqa.selenium.grid.data.NodeId;32import org.openqa.selenium.grid.data.NodeRejectedEvent;33import org.openqa.selenium.grid.data.NodeRemovedEvent;34import org.openqa.selenium.grid.data.NodeStatus;35import org.openqa.selenium.grid.data.NodeStatusEvent;36import org.openqa.selenium.grid.data.Slot;37import org.openqa.selenium.grid.data.SlotId;38import org.openqa.selenium.grid.distributor.Distributor;39import org.openqa.selenium.grid.distributor.selector.DefaultSlotSelector;40import org.openqa.selenium.grid.log.LoggingOptions;41import org.openqa.selenium.grid.node.HealthCheck;42import org.openqa.selenium.grid.node.Node;43import org.openqa.selenium.grid.node.remote.RemoteNode;44import org.openqa.selenium.grid.security.Secret;45import org.openqa.selenium.grid.server.BaseServerOptions;46import org.openqa.selenium.grid.server.EventBusOptions;47import org.openqa.selenium.grid.server.NetworkOptions;48import org.openqa.selenium.grid.sessionmap.SessionMap;49import org.openqa.selenium.grid.sessionmap.config.SessionMapOptions;50import org.openqa.selenium.internal.Require;51import org.openqa.selenium.remote.http.HttpClient;52import org.openqa.selenium.remote.tracing.Tracer;53import org.openqa.selenium.status.HasReadyState;54import java.time.Duration;55import java.util.ArrayList;56import java.util.HashMap;57import java.util.List;58import java.util.Map;59import java.util.Objects;60import java.util.Optional;61import java.util.Set;62import java.util.concurrent.locks.Lock;63import java.util.concurrent.locks.ReadWriteLock;64import java.util.concurrent.locks.ReentrantReadWriteLock;65import java.util.function.Supplier;66import java.util.logging.Level;67import java.util.logging.Logger;68import static com.google.common.collect.ImmutableSet.toImmutableSet;69import static org.openqa.selenium.grid.data.Availability.DOWN;70import static org.openqa.selenium.grid.data.Availability.DRAINING;71public class LocalDistributor extends Distributor {72 private static final Logger LOG = Logger.getLogger(LocalDistributor.class.getName());73 private final Tracer tracer;74 private final EventBus bus;75 private final HttpClient.Factory clientFactory;76 private final SessionMap sessions;77 private final Regularly hostChecker = new Regularly("distributor host checker");78 private final Map<NodeId, Runnable> allChecks = new HashMap<>();79 private final ReadWriteLock lock = new ReentrantReadWriteLock(/* fair */ true);80 private final GridModel model;81 private final Map<NodeId, Node> nodes;82 public LocalDistributor(83 Tracer tracer,84 EventBus bus,85 HttpClient.Factory clientFactory,86 SessionMap sessions,87 Secret registrationSecret) {88 super(tracer, clientFactory, new DefaultSlotSelector(), sessions, registrationSecret);89 this.tracer = Require.nonNull("Tracer", tracer);90 this.bus = Require.nonNull("Event bus", bus);91 this.clientFactory = Require.nonNull("HTTP client factory", clientFactory);92 this.sessions = Require.nonNull("Session map", sessions);93 this.model = new GridModel(bus, registrationSecret);94 this.nodes = new HashMap<>();95 bus.addListener(NodeStatusEvent.listener(status -> register(registrationSecret, status)));96 bus.addListener(NodeStatusEvent.listener(status -> model.refresh(registrationSecret, status)));97 bus.addListener(NodeDrainComplete.listener(this::remove));98 }99 public static Distributor create(Config config) {100 Tracer tracer = new LoggingOptions(config).getTracer();101 EventBus bus = new EventBusOptions(config).getEventBus();102 HttpClient.Factory clientFactory = new NetworkOptions(config).getHttpClientFactory(tracer);103 SessionMap sessions = new SessionMapOptions(config).getSessionMap();104 BaseServerOptions serverOptions = new BaseServerOptions(config);105 return new LocalDistributor(tracer, bus, clientFactory, sessions, serverOptions.getRegistrationSecret());106 }107 @Override108 public boolean isReady() {109 try {110 return ImmutableSet.of(bus, sessions).parallelStream()111 .map(HasReadyState::isReady)112 .reduce(true, Boolean::logicalAnd);113 } catch (RuntimeException e) {114 return false;115 }116 }117 private void register(Secret registrationSecret, NodeStatus status) {118 Require.nonNull("Node", status);119 Secret nodeSecret = status.getRegistrationSecret() == null ? null : new Secret(status.getRegistrationSecret());120 if (!Objects.equals(registrationSecret, nodeSecret)) {121 LOG.severe(String.format("Node at %s failed to send correct registration secret. Node NOT registered.", status.getUri()));122 bus.fire(new NodeRejectedEvent(status.getUri()));123 return;124 }125 Lock writeLock = lock.writeLock();126 writeLock.lock();127 try {128 if (nodes.containsKey(status.getId())) {129 return;130 }131 Set<Capabilities> capabilities = status.getSlots().stream()132 .map(Slot::getStereotype)133 .map(ImmutableCapabilities::copyOf)134 .collect(toImmutableSet());135 // A new node! Add this as a remote node, since we've not called add136 RemoteNode remoteNode = new RemoteNode(137 tracer,138 clientFactory,139 status.getId(),140 status.getUri(),141 registrationSecret,142 capabilities);143 add(remoteNode);144 } finally {145 writeLock.unlock();146 }147 }148 @Override149 public LocalDistributor add(Node node) {150 Require.nonNull("Node", node);151 LOG.info(String.format("Added node %s at %s.", node.getId(), node.getUri()));152 nodes.put(node.getId(), node);153 model.add(node.getStatus());154 // Extract the health check155 Runnable runnableHealthCheck = asRunnableHealthCheck(node);156 allChecks.put(node.getId(), runnableHealthCheck);157 hostChecker.submit(runnableHealthCheck, Duration.ofMinutes(5), Duration.ofSeconds(30));158 bus.fire(new NodeAddedEvent(node.getId()));159 return this;160 }161 private Runnable asRunnableHealthCheck(Node node) {162 HealthCheck healthCheck = node.getHealthCheck();163 NodeId id = node.getId();164 return () -> {165 HealthCheck.Result result;166 try {167 result = healthCheck.check();168 } catch (Exception e) {169 LOG.log(Level.WARNING, "Unable to process node " + id, e);170 result = new HealthCheck.Result(DOWN, "Unable to run healthcheck. Assuming down");171 }172 Lock writeLock = lock.writeLock();173 writeLock.lock();174 try {175 model.setAvailability(id, result.getAvailability());176 } finally {177 writeLock.unlock();178 }179 };180 }181 @Override182 public boolean drain(NodeId nodeId) {183 Node node = nodes.get(nodeId);184 if (node == null) {185 LOG.info("Asked to drain unregistered node " + nodeId);186 return false;187 }188 Lock writeLock = lock.writeLock();189 writeLock.lock();190 try {191 node.drain();192 model.setAvailability(nodeId, DRAINING);193 } finally {194 writeLock.unlock();195 }196 return node.isDraining();197 }198 public void remove(NodeId nodeId) {199 Lock writeLock = lock.writeLock();200 writeLock.lock();201 try {202 model.remove(nodeId);203 Runnable runnable = allChecks.remove(nodeId);204 if (runnable != null) {205 hostChecker.remove(runnable);206 }207 } finally {208 writeLock.unlock();209 bus.fire(new NodeRemovedEvent(nodeId));210 }...

Full Screen

Full Screen

Source:RemoteNode.java Github

copy

Full Screen

1// Licensed to the Software Freedom Conservancy (SFC) under one2// or more contributor license agreements. See the NOTICE file3// distributed with this work for additional information4// regarding copyright ownership. The SFC licenses this file5// to you under the Apache License, Version 2.0 (the6// "License"); you may not use this file except in compliance7// with the License. You may obtain a copy of the License at8//9// http://www.apache.org/licenses/LICENSE-2.010//11// Unless required by applicable law or agreed to in writing,12// software distributed under the License is distributed on an13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY14// KIND, either express or implied. See the License for the15// specific language governing permissions and limitations16// under the License.17package org.openqa.selenium.grid.node.remote;18import com.google.common.collect.ImmutableMap;19import com.google.common.collect.ImmutableSet;20import org.openqa.selenium.Capabilities;21import org.openqa.selenium.NoSuchSessionException;22import org.openqa.selenium.grid.data.CreateSessionRequest;23import org.openqa.selenium.grid.data.CreateSessionResponse;24import org.openqa.selenium.grid.data.NodeId;25import org.openqa.selenium.grid.data.NodeStatus;26import org.openqa.selenium.grid.data.Session;27import org.openqa.selenium.grid.node.HealthCheck;28import org.openqa.selenium.grid.node.Node;29import org.openqa.selenium.grid.security.Secret;30import org.openqa.selenium.grid.web.Values;31import org.openqa.selenium.internal.Require;32import org.openqa.selenium.json.Json;33import org.openqa.selenium.json.JsonInput;34import org.openqa.selenium.remote.SessionId;35import org.openqa.selenium.remote.http.HttpClient;36import org.openqa.selenium.remote.http.HttpHandler;37import org.openqa.selenium.remote.http.HttpRequest;38import org.openqa.selenium.remote.http.HttpResponse;39import org.openqa.selenium.remote.tracing.HttpTracing;40import org.openqa.selenium.remote.tracing.Tracer;41import java.io.IOException;42import java.io.Reader;43import java.io.UncheckedIOException;44import java.net.URI;45import java.util.Collection;46import java.util.Map;47import java.util.Objects;48import java.util.Optional;49import java.util.Set;50import static java.net.HttpURLConnection.HTTP_OK;51import static org.openqa.selenium.grid.data.Availability.DOWN;52import static org.openqa.selenium.grid.data.Availability.DRAINING;53import static org.openqa.selenium.grid.data.Availability.UP;54import static org.openqa.selenium.net.Urls.fromUri;55import static org.openqa.selenium.remote.http.Contents.asJson;56import static org.openqa.selenium.remote.http.Contents.reader;57import static org.openqa.selenium.remote.http.HttpMethod.DELETE;58import static org.openqa.selenium.remote.http.HttpMethod.GET;59import static org.openqa.selenium.remote.http.HttpMethod.POST;60public class RemoteNode extends Node {61 public static final Json JSON = new Json();62 private final HttpHandler client;63 private final URI externalUri;64 private final Set<Capabilities> capabilities;65 private final HealthCheck healthCheck;66 public RemoteNode(67 Tracer tracer,68 HttpClient.Factory clientFactory,69 NodeId id,70 URI externalUri,71 Secret registrationSecret,72 Collection<Capabilities> capabilities) {73 super(tracer, id, externalUri, registrationSecret);74 this.externalUri = Require.nonNull("External URI", externalUri);75 this.capabilities = ImmutableSet.copyOf(capabilities);76 this.client = Require77 .nonNull("HTTP client factory", clientFactory).createClient(fromUri(externalUri));78 this.healthCheck = new RemoteCheck();79 }80 @Override81 public boolean isReady() {82 try {83 return client.execute(new HttpRequest(GET, "/readyz")).isSuccessful();84 } catch (Exception e) {85 return false;86 }87 }88 @Override89 public boolean isSupporting(Capabilities capabilities) {90 return this.capabilities.stream()91 .anyMatch(caps -> caps.getCapabilityNames().stream()92 .allMatch(name -> Objects.equals(93 caps.getCapability(name),94 capabilities.getCapability(name))));95 }96 @Override97 public Optional<CreateSessionResponse> newSession(CreateSessionRequest sessionRequest) {98 Require.nonNull("Capabilities for session", sessionRequest);99 HttpRequest req = new HttpRequest(POST, "/se/grid/node/session");100 HttpTracing.inject(tracer, tracer.getCurrentContext(), req);101 req.setContent(asJson(sessionRequest));102 HttpResponse res = client.execute(req);103 return Optional.ofNullable(Values.get(res, CreateSessionResponse.class));104 }105 @Override106 public boolean isSessionOwner(SessionId id) {107 Require.nonNull("Session ID", id);108 HttpRequest req = new HttpRequest(GET, "/se/grid/node/owner/" + id);109 HttpTracing.inject(tracer, tracer.getCurrentContext(), req);110 HttpResponse res = client.execute(req);111 return Values.get(res, Boolean.class) == Boolean.TRUE;112 }113 @Override114 public Session getSession(SessionId id) throws NoSuchSessionException {115 Require.nonNull("Session ID", id);116 HttpRequest req = new HttpRequest(GET, "/se/grid/node/session/" + id);117 HttpTracing.inject(tracer, tracer.getCurrentContext(), req);118 HttpResponse res = client.execute(req);119 return Values.get(res, Session.class);120 }121 @Override122 public HttpResponse executeWebDriverCommand(HttpRequest req) {123 return client.execute(req);124 }125 @Override126 public HttpResponse uploadFile(HttpRequest req, SessionId id) {127 return client.execute(req);128 }129 @Override130 public void stop(SessionId id) throws NoSuchSessionException {131 Require.nonNull("Session ID", id);132 HttpRequest req = new HttpRequest(DELETE, "/se/grid/node/session/" + id);133 HttpTracing.inject(tracer, tracer.getCurrentContext(), req);134 HttpResponse res = client.execute(req);135 Values.get(res, Void.class);136 }137 @Override138 public NodeStatus getStatus() {139 HttpRequest req = new HttpRequest(GET, "/status");140 HttpTracing.inject(tracer, tracer.getCurrentContext(), req);141 HttpResponse res = client.execute(req);142 try (Reader reader = reader(res);143 JsonInput in = JSON.newInput(reader)) {144 in.beginObject();145 // Skip everything until we find "value"146 while (in.hasNext()) {147 if ("value".equals(in.nextName())) {148 in.beginObject();149 while (in.hasNext()) {150 if ("node".equals(in.nextName())) {151 return in.read(NodeStatus.class);152 } else {153 in.skipValue();154 }155 }156 in.endObject();157 } else {158 in.skipValue();159 }160 }161 } catch (IOException e) {162 throw new UncheckedIOException(e);163 }164 throw new IllegalStateException("Unable to read status");165 }166 @Override167 public HealthCheck getHealthCheck() {168 return healthCheck;169 }170 @Override171 public void drain() {172 HttpRequest req = new HttpRequest(POST, "/se/grid/node/drain");173 HttpTracing.inject(tracer, tracer.getCurrentContext(), req);174 HttpResponse res = client.execute(req);175 if(res.getStatus() == HTTP_OK) {176 draining = true;177 }178 }179 private Map<String, Object> toJson() {180 return ImmutableMap.of(181 "id", getId(),182 "uri", externalUri,183 "capabilities", capabilities);184 }185 private class RemoteCheck implements HealthCheck {186 @Override187 public Result check() {188 try {189 NodeStatus status = getStatus();190 switch (status.getAvailability()) {191 case DOWN:192 return new Result(DOWN, externalUri + " is down");193 case DRAINING:194 return new Result(DRAINING, externalUri + " is draining");195 case UP:196 return new Result(UP, externalUri + " is ok");197 default:198 throw new IllegalStateException("Unknown node availability: " + status.getAvailability());199 }200 } catch (RuntimeException e) {201 return new Result(202 DOWN,203 "Unable to determine node status: " + e.getMessage());204 }205 }206 }207}...

Full Screen

Full Screen

Source:NodeServer.java Github

copy

Full Screen

...26import org.openqa.selenium.grid.TemplateGridCommand;27import org.openqa.selenium.grid.config.Config;28import org.openqa.selenium.grid.config.Role;29import org.openqa.selenium.grid.data.NodeAddedEvent;30import org.openqa.selenium.grid.data.NodeDrainComplete;31import org.openqa.selenium.grid.data.NodeStatusEvent;32import org.openqa.selenium.grid.log.LoggingOptions;33import org.openqa.selenium.grid.node.HealthCheck;34import org.openqa.selenium.grid.node.Node;35import org.openqa.selenium.grid.node.ProxyNodeCdp;36import org.openqa.selenium.grid.node.config.NodeOptions;37import org.openqa.selenium.grid.server.BaseServerOptions;38import org.openqa.selenium.grid.server.EventBusOptions;39import org.openqa.selenium.grid.server.NetworkOptions;40import org.openqa.selenium.grid.server.Server;41import org.openqa.selenium.netty.server.NettyServer;42import org.openqa.selenium.remote.http.Contents;43import org.openqa.selenium.remote.http.HttpClient;44import org.openqa.selenium.remote.http.HttpHandler;45import org.openqa.selenium.remote.http.HttpResponse;46import org.openqa.selenium.remote.http.Route;47import org.openqa.selenium.remote.tracing.Tracer;48import java.time.Duration;49import java.time.temporal.ChronoUnit;50import java.util.Collections;51import java.util.Set;52import java.util.concurrent.Executors;53import java.util.logging.Logger;54import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;55import static java.net.HttpURLConnection.HTTP_NO_CONTENT;56import static org.openqa.selenium.grid.config.StandardGridRoles.EVENT_BUS_ROLE;57import static org.openqa.selenium.grid.config.StandardGridRoles.HTTPD_ROLE;58import static org.openqa.selenium.grid.config.StandardGridRoles.NODE_ROLE;59import static org.openqa.selenium.grid.data.Availability.DOWN;60import static org.openqa.selenium.remote.http.Route.get;61@AutoService(CliCommand.class)62public class NodeServer extends TemplateGridCommand {63 private static final Logger LOG = Logger.getLogger(NodeServer.class.getName());64 @Override65 public String getName() {66 return "node";67 }68 @Override69 public String getDescription() {70 return "Adds this server as a node in the selenium grid.";71 }72 @Override73 public Set<Role> getConfigurableRoles() {74 return ImmutableSet.of(EVENT_BUS_ROLE, HTTPD_ROLE, NODE_ROLE);75 }76 @Override77 public Set<Object> getFlagObjects() {78 return Collections.emptySet();79 }80 @Override81 protected String getSystemPropertiesConfigPrefix() {82 return "node";83 }84 @Override85 protected Config getDefaultConfig() {86 return new DefaultNodeConfig();87 }88 @Override89 protected void execute(Config config) {90 LoggingOptions loggingOptions = new LoggingOptions(config);91 Tracer tracer = loggingOptions.getTracer();92 EventBusOptions events = new EventBusOptions(config);93 EventBus bus = events.getEventBus();94 NetworkOptions networkOptions = new NetworkOptions(config);95 HttpClient.Factory clientFactory = networkOptions.getHttpClientFactory(tracer);96 BaseServerOptions serverOptions = new BaseServerOptions(config);97 LOG.info("Reporting self as: " + serverOptions.getExternalUri());98 NodeOptions nodeOptions = new NodeOptions(config);99 Node node = nodeOptions.getNode();100 HttpHandler readinessCheck = req -> {101 if (node.getStatus().hasCapacity()) {102 return new HttpResponse()103 .setStatus(HTTP_NO_CONTENT);104 }105 return new HttpResponse()106 .setStatus(HTTP_INTERNAL_ERROR)107 .setHeader("Content-Type", MediaType.PLAIN_TEXT_UTF_8.toString())108 .setContent(Contents.utf8String("No capacity available"));109 };110 bus.addListener(NodeAddedEvent.listener(nodeId -> {111 if (node.getId().equals(nodeId)) {112 LOG.info("Node has been added");113 }114 }));115 bus.addListener(NodeDrainComplete.listener(nodeId -> {116 if (!node.getId().equals(nodeId)) {117 return;118 }119 // Wait a beat before shutting down so the final response from the120 // node can escape.121 new Thread(122 () -> {123 try {124 Thread.sleep(1000);125 } catch (InterruptedException e) {126 // Swallow, the next thing we're doing is shutting down127 }128 LOG.info("Shutting down");129 System.exit(0);...

Full Screen

Full Screen

Source:Drain.java Github

copy

Full Screen

...22import org.openqa.selenium.remote.http.HttpRequest;23import org.openqa.selenium.remote.http.HttpResponse;24import java.io.UncheckedIOException;25import java.util.Objects;26public class Drain implements HttpHandler {27 private final Node node;28 private final Json json;29 public Drain(Node node, Json json) {30 this.node = Objects.requireNonNull(node);31 this.json = Objects.requireNonNull(json);32 }33 @Override34 public HttpResponse execute(HttpRequest req) throws UncheckedIOException {35 this.node.drain();36 HttpResponse response = new HttpResponse();37 if (this.node.isDraining()) {38 response.setStatus(HTTP_OK);39 } else {40 response.setStatus(HTTP_INTERNAL_ERROR);41 }42 return response;43 }44}...

Full Screen

Full Screen

Drain

Using AI Code Generation

copy

Full Screen

1import org.openqa.selenium.grid.node.Drain;2import org.openqa.selenium.grid.node.config.NodeOptions;3import org.openqa.selenium.grid.node.remote.RemoteNode;4import org.openqa.selenium.grid.web.Routable;5import org.openqa.selenium.grid.web.Routes;6import org.openqa.selenium.remote.http.HttpClient;7import org.openqa.selenium.remote.http.HttpMethod;8import org.openqa.selenium.remote.http.HttpRequest;9import org.openqa.selenium.remote.http.HttpResponse;10import java.io.IOException;11import java.net.URI;12import java.net.URISyntaxException;13import java.util.Map;14import java.util.concurrent.TimeUnit;15import java.util.concurrent.atomic.AtomicBoolean;16public class MyDrain extends Drain {17 public MyDrain(HttpClient client, URI externalUri, Map<String, String> capabilities) {18 super(client, externalUri, capabilities);19 }20 public void start() {21 super.start();22 System.out.println("Drain has started");23 }24 public void stop() {25 super.stop();26 System.out.println("Drain has stopped");27 }28 public void drain() {29 super.drain();30 System.out.println("Drain has drained");31 }32 public void close() {33 super.close();34 System.out.println("Drain has closed");35 }36}37public class MyRemoteNode extends RemoteNode {38 private final AtomicBoolean isDraining = new AtomicBoolean(false);39 private final Drain drain;40 public MyRemoteNode(HttpClient client, URI externalUri, Map<String, String> capabilities) {41 super(client, externalUri, capabilities);42 drain = new MyDrain(client, externalUri, capabilities);43 }44 public void start() {45 super.start();46 System.out.println("Remote node has started");47 }48 public void stop() {49 super.stop();50 System.out.println("Remote node has stopped");51 }52 public void close() {53 super.close();54 System.out.println("Remote node has closed");55 }56 public void drain() {57 if (isDraining.compareAndSet(false, true)) {58 drain.drain();59 }60 }61 public void undrain() {62 if (isDraining.compareAndSet(true, false)) {63 drain.undrain();64 }65 }66 public boolean isDraining() {

Full Screen

Full Screen

Drain

Using AI Code Generation

copy

Full Screen

1import org.openqa.selenium.grid.node.config.Drain;2import org.openqa.selenium.grid.node.config.DrainOptions;3import org.openqa.selenium.grid.node.config.NodeOptions;4import org.openqa.selenium.grid.node.config.NodeRole;5import org.openqa.selenium.grid.node.local.LocalNode;6import org.openqa.selenium.grid.node.local.LocalNodeOptions;7import org.openqa.selenium.grid.web.Routable;8import org.openqa.selenium.net.PortProber;9import org.openqa.selenium.remote.http.HttpClient;10import org.openqa.selenium.remote.http.HttpMethod;11import org.openqa.selenium.remote.http.HttpRequest;12import org.openqa.selenium.remote.http.HttpResponse;13import org.openqa.selenium.remote.http.Route;14import org.openqa.selenium.remote.tracing.Tracer;15import org.openqa.selenium.remote.tracing.noop.NoOpTracer;16import java.io.IOException;17import java.net.URI;18import java.time.Duration;19import java.util.concurrent.TimeUnit;20import java.util.logging.Logger;21public class DrainTest {22 private static final Logger LOG = Logger.getLogger(DrainTest.class.getName());23 public static void main(String[] args) throws IOException {24 LocalNode node = createLocalNode();25 node.start();26 Drain drain = new Drain(node, Duration.ofSeconds(10));27 drain.start();28 boolean isNodeDown = node.isDown();29 System.out.println("Is the node down? " + isNodeDown);30 drain.stop();31 node.stop();32 }33 private static LocalNode createLocalNode() {34 Tracer tracer = NoOpTracer.getInstance();35 NodeOptions nodeOptions = new NodeOptions();36 nodeOptions.setRole(NodeRole.NODE);37 DrainOptions drainOptions = new DrainOptions();38 drainOptions.setDrainDuration(Duration.ofSeconds(10));39 LocalNodeOptions localNodeOptions = new LocalNodeOptions();40 localNodeOptions.setPort(PortProber.findFreePort());41 localNodeOptions.setNodeOptions(nodeOptions);42 localNodeOptions.setDrainOptions(drainOptions);

Full Screen

Full Screen

Drain

Using AI Code Generation

copy

Full Screen

1package org.openqa.selenium.grid.node;2import org.openqa.selenium.grid.node.config.NodeOptions;3import org.openqa.selenium.grid.node.local.LocalNode;4import org.openqa.selenium.grid.web.Routable;5import org.openqa.selenium.remote.http.HttpClient;6import org.openqa.selenium.remote.http.HttpMethod;7import org.openqa.selenium.remote.http.HttpRequest;8import org.openqa.selenium.remote.http.HttpResponse;9import org.openqa.selenium.remote.tracing.Tracer;10import java.net.URI;11import java.util.Collections;12import java.util.Set;13import java.util.logging.Logger;14public class Drain implements Routable {15 private static final Logger LOG = Logger.getLogger(Drain.class.getName());16 private final LocalNode node;17 public Drain(LocalNode node) {18 this.node = node;19 }20 public Set<HttpMethod> getSupportedMethod() {21 return Collections.singleton(HttpMethod.POST);22 }23 public URI getUri() {24 return URI.create("/drain");25 }26 public HttpResponse execute(HttpRequest req) {27 LOG.info("Draining node");28 node.drain();29 return new HttpResponse().setContent("Node drained");30 }31 public static void main(String[] args) {32 Tracer tracer = Tracer.getDefault();33 HttpClient.Factory clientFactory = HttpClient.Factory.createDefault();34 NodeOptions nodeOptions = NodeOptions.create(args);35 LocalNode node = LocalNode.create(tracer, clientFactory, nodeOptions);36 node.add(new Drain(node));37 node.start();38 }39}

Full Screen

Full Screen

Drain

Using AI Code Generation

copy

Full Screen

1package com.example.grid;2import java.io.IOException;3import java.net.URL;4import java.time.Duration;5import java.util.ArrayList;6import java.util.List;7import java.util.Map;8import java.util.Set;9import java.util.concurrent.TimeUnit;10import org.openqa.selenium.Capabilities;11import org.openqa.selenium.ImmutableCapabilities;12import org.openqa.selenium.SessionNotCreatedException;13import org.openqa.selenium.WebDriver;14import org.openqa.selenium.WebElement;15import org.openqa.selenium.chrome.ChromeDriver;16import org.openqa.selenium.chrome.ChromeOptions;17import org.openqa.selenium.grid.config.Config;18import org.openqa.selenium.grid.config.MapConfig;19import org.openqa.selenium.grid.data.DistributorStatus;20import org.openqa.selenium.grid.data.NodeStatus;21import org.openqa.selenium.grid.data.Session;22import org.openqa.selenium.grid.data.SessionRequest;23import org.openqa.selenium.grid.data.SessionRequestEvent;24import org.openqa.selenium.grid.data.SessionRequestEvent.State;25import org.openqa.selenium.grid.distributor.local.LocalDistributor;26import org.openqa.selenium.grid.graphql.GraphqlHandler;27import org.openqa.selenium.grid.node.ActiveSession;28import org.openqa.selenium.grid.node.Drain;29import org.openqa.selenium.grid.node.Node;30import org.openqa.selenium.grid.node.local.LocalNode;31import org.openqa.selenium.grid.security.Secret;32import org.openqa.selenium.grid.server.Server;33import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;34import org.openqa.selenium.grid.web.Values;35import org.openqa.selenium.internal.Require;36import org.openqa.selenium.json.Json;37import org.openqa.selenium.remote.http.HttpClient;38import org.openqa.selenium.remote.http.HttpMethod;39import org.openqa.selenium.remote.http.HttpRequest;40import org.openqa.selenium.remote.http.HttpResponse;41import org.openqa.selenium.remote.http.Route;42import org.openqa.selenium.remote.tracing.DefaultTestTracer;43import org.openqa.selenium.remote.tracing.Tracer;44public class GridDrainExample {45 private static final Json JSON = new Json();46 public static void main(String[] args) throws Exception {47 Tracer tracer = DefaultTestTracer.createTracer();48 LocalSessionMap sessions = new LocalSessionMap(tracer);49 LocalDistributor distributor = new LocalDistributor(tracer, sessions);50 LocalNode node = createLocalNode(tracer, distributor);51 Server<?> server = createServer(tracer, distributor, node);52 server.start();53 WebDriver driver = createDriver(tracer, server.getUrl());

Full Screen

Full Screen

Drain

Using AI Code Generation

copy

Full Screen

1Drain drain = new Drain(0);2NodeStatus status = new NodeStatus(false);3NodeStatusEvent event = new NodeStatusEvent(status, drain);4EventBus bus = new EventBus();5NodeStatusListener listener = new NodeStatusListener(bus);6RemoteNode node = new RemoteNode(uri, listener, bus);7bus.addListener(NodeStatusEvent.class, event -> {8 Drain drain = event.getDrain();9 NodeStatus status = event.getStatus();10 URI uri = node.getUri();11 NodeId nodeId = node.getId();12});13NodeId nodeId = node.getId();14URI uri = node.getUri();15bus.addListener(NodeStatusEvent.class, event -> {16 Drain drain = event.getDrain();17 NodeStatus status = event.getStatus();18 URI uri = node.getUri();19 NodeId nodeId = node.getId();20});21NodeId nodeId = node.getId();22URI uri = node.getUri();

Full Screen

Full Screen
copy
1String translation(String cat_language) {2 return switch (cat_language) {3 case "miau miau" -> "I am to run";4 case "miauuuh" -> "I am to sleep";5 case "mi...au?" -> "leave me alone";6 default -> "eat";7 };8} 9
Full Screen
copy
1 public static void main(String[] args) {2 switch (args[0]) {3 case "Monday", "Tuesday", "Wednesday" -> System.out.println("boring");4 case "Thursday" -> System.out.println("getting better");5 case "Friday", "Saturday", "Sunday" -> System.out.println("much better");6 }7
Full Screen

Selenium 4 Tutorial:

LambdaTest’s Selenium 4 tutorial is covering every aspects of Selenium 4 testing with examples and best practices. Here you will learn basics, such as how to upgrade from Selenium 3 to Selenium 4, to some advanced concepts, such as Relative locators and Selenium Grid 4 for Distributed testing. Also will learn new features of Selenium 4, such as capturing screenshots of specific elements, opening a new tab or window on the browser, and new protocol adoptions.

Chapters:

  1. Upgrading From Selenium 3 To Selenium 4?: In this chapter, learn in detail how to update Selenium 3 to Selenium 4 for Java binding. Also, learn how to upgrade while using different build tools such as Maven or Gradle and get comprehensive guidance for upgrading Selenium.

  2. What’s New In Selenium 4 & What’s Being Deprecated? : Get all information about new implementations in Selenium 4, such as W3S protocol adaption, Optimized Selenium Grid, and Enhanced Selenium IDE. Also, learn what is deprecated for Selenium 4, such as DesiredCapabilites and FindsBy methods, etc.

  3. Selenium 4 With Python: Selenium supports all major languages, such as Python, C#, Ruby, and JavaScript. In this chapter, learn how to install Selenium 4 for Python and the features of Python in Selenium 4, such as Relative locators, Browser manipulation, and Chrom DevTool protocol.

  4. Selenium 4 Is Now W3C Compliant: JSON Wireframe protocol is retiring from Selenium 4, and they are adopting W3C protocol to learn in detail about the advantages and impact of these changes.

  5. How To Use Selenium 4 Relative Locator? : Selenium 4 came with new features such as Relative Locators that allow constructing locators with reference and easily located constructors nearby. Get to know its different use cases with examples.

  6. Selenium Grid 4 Tutorial For Distributed Testing: Selenium Grid 4 allows you to perform tests over different browsers, OS, and device combinations. It also enables parallel execution browser testing, reads up on various features of Selenium Grid 4 and how to download it, and runs a test on Selenium Grid 4 with best practices.

  7. Selenium Video Tutorials: Binge on video tutorials on Selenium by industry experts to get step-by-step direction from automating basic to complex test scenarios with Selenium.

Selenium 101 certifications:

LambdaTest also provides certification for Selenium testing to accelerate your career in Selenium automation testing.

Run Selenium automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Most used methods in Drain

Test Your Web Or Mobile Apps On 3000+ Browsers

Signup for free

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful