|
4 | 4 |
|
5 | 5 | package io.airbyte.container_orchestrator;
|
6 | 6 |
|
| 7 | +import com.google.common.annotations.VisibleForTesting; |
7 | 8 | import io.airbyte.workers.process.AsyncKubePodStatus;
|
8 | 9 | import io.airbyte.workers.process.KubePodInfo;
|
| 10 | +import io.airbyte.workers.storage.DocumentStoreClient; |
| 11 | +import jakarta.inject.Singleton; |
| 12 | +import java.lang.invoke.MethodHandles; |
| 13 | +import java.util.List; |
| 14 | +import org.slf4j.Logger; |
| 15 | +import org.slf4j.LoggerFactory; |
9 | 16 |
|
10 | 17 | /**
|
11 | 18 | * The state manager writes the "truth" for states of the async pod process. If the store isn't
|
12 | 19 | * updated by the underlying pod, it will appear as failed.
|
13 |
| - * |
| 20 | + * <p> |
14 | 21 | * It doesn't have a single value for a state. Instead, in a location on cloud storage or disk, it
|
15 | 22 | * writes every state it's encountered.
|
16 | 23 | */
|
17 |
| -public interface AsyncStateManager { |
| 24 | +@Singleton |
| 25 | +public class AsyncStateManager { |
| 26 | + |
| 27 | + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
| 28 | + private static final List<AsyncKubePodStatus> STATUS_CHECK_ORDER = List.of( |
| 29 | + // terminal states first |
| 30 | + AsyncKubePodStatus.FAILED, |
| 31 | + AsyncKubePodStatus.SUCCEEDED, |
| 32 | + // then check in progress state |
| 33 | + AsyncKubePodStatus.RUNNING, |
| 34 | + // then check for initialization state |
| 35 | + AsyncKubePodStatus.INITIALIZING); |
| 36 | + |
| 37 | + private final DocumentStoreClient documentStoreClient; |
| 38 | + private final KubePodInfo kubePodInfo; |
| 39 | + |
| 40 | + public AsyncStateManager(final DocumentStoreClient documentStoreClient, final KubePodInfo kubePodInfo) { |
| 41 | + this.documentStoreClient = documentStoreClient; |
| 42 | + this.kubePodInfo = kubePodInfo; |
| 43 | + } |
18 | 44 |
|
19 | 45 | /**
|
20 |
| - * Writes a file containing a string value to a location designated by the input status. |
| 46 | + * Writes an empty file to a location designated by the input status. |
21 | 47 | */
|
22 |
| - void write(final KubePodInfo kubePodInfo, final AsyncKubePodStatus status, final String value); |
| 48 | + public void write(final AsyncKubePodStatus status, final String value) { |
| 49 | + final var key = getDocumentStoreKey(status); |
| 50 | + log.info("Writing async status {} for {}...", status, kubePodInfo); |
| 51 | + documentStoreClient.write(key, value); |
| 52 | + } |
23 | 53 |
|
24 | 54 | /**
|
25 |
| - * Writes an empty file to a location designated by the input status. |
| 55 | + * Writes a file containing a string value to a location designated by the input status. |
26 | 56 | */
|
27 |
| - void write(final KubePodInfo kubePodInfo, final AsyncKubePodStatus status); |
| 57 | + public void write(final AsyncKubePodStatus status) { |
| 58 | + write(status, ""); |
| 59 | + } |
28 | 60 |
|
29 | 61 | /**
|
30 | 62 | * Interprets the state given all written state messages for the pod.
|
| 63 | + * <p> |
| 64 | + * Checks terminal states first, then running, then initialized. Defaults to not started. |
| 65 | + * <p> |
| 66 | + * The order matters here! |
31 | 67 | */
|
32 |
| - AsyncKubePodStatus getStatus(final KubePodInfo kubePodInfo); |
| 68 | + public AsyncKubePodStatus getStatus() { |
| 69 | + return STATUS_CHECK_ORDER.stream() |
| 70 | + .filter(this::statusFileExists) |
| 71 | + .findFirst() |
| 72 | + .orElse(AsyncKubePodStatus.NOT_STARTED); |
| 73 | + } |
33 | 74 |
|
34 | 75 | /**
|
35 | 76 | * @return the output stored in the success file. This can be an empty string.
|
36 | 77 | * @throws IllegalArgumentException if no success file exists
|
37 | 78 | */
|
38 |
| - String getOutput(final KubePodInfo kubePodInfo) throws IllegalArgumentException; |
| 79 | + public String getOutput() throws IllegalArgumentException { |
| 80 | + final var key = getDocumentStoreKey(AsyncKubePodStatus.SUCCEEDED); |
| 81 | + final var output = documentStoreClient.read(key); |
| 82 | + |
| 83 | + return output.orElseThrow(() -> new IllegalArgumentException("Expected to retrieve output from a successfully completed pod!")); |
| 84 | + |
| 85 | + } |
| 86 | + |
| 87 | + /** |
| 88 | + * IMPORTANT: Changing the storage location will orphan already existing kube pods when the new |
| 89 | + * version is deployed! |
| 90 | + */ |
| 91 | + @VisibleForTesting |
| 92 | + String getDocumentStoreKey(final AsyncKubePodStatus status) { |
| 93 | + return kubePodInfo.namespace() + "/" + kubePodInfo.name() + "/" + status.name(); |
| 94 | + } |
| 95 | + |
| 96 | + private boolean statusFileExists(final AsyncKubePodStatus status) { |
| 97 | + final var key = getDocumentStoreKey(status); |
| 98 | + return documentStoreClient.read(key).isPresent(); |
| 99 | + } |
39 | 100 |
|
40 | 101 | }
|
0 commit comments