Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental cloud operations client #2146

Merged
merged 3 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,45 @@ jobs:
with:
report_paths: '**/build/test-results/test/TEST-*.xml'

unit_test_cloud:
cretz marked this conversation as resolved.
Show resolved Hide resolved
name: Unit test with cloud
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
ref: ${{ github.event.pull_request.head.sha }}

- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: "11"
distribution: "temurin"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

- name: Run cloud test
# Only supported in non-fork runs, since secrets are not available in forks. We intentionally
# are only doing this check on the step instead of the job so we require job passing in CI
# even for those that can't run this step.
if: ${{ github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-java' }}
env:
USER: unittest
TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6
TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }}
TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00
run: ./gradlew --no-daemon :temporal-sdk:test --tests '*CloudOperationsClientTest'

- name: Publish Test Report
uses: mikepenz/action-junit-report@v4
if: success() || failure() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/test/TEST-*.xml'

copyright:
name: Copyright and code format
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "temporal-serviceclient/src/main/proto"]
path = temporal-serviceclient/src/main/proto
url = https://github.com/temporalio/api.git
[submodule "temporal-serviceclient/src/main/protocloud"]
path = temporal-serviceclient/src/main/protocloud
url = https://github.com/temporalio/api-cloud.git
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
*
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this material except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.temporal.client;

import io.temporal.common.Experimental;
import io.temporal.serviceclient.CloudServiceStubs;

/** Client to the Temporal Cloud operations service for performing cloud operations. */
@Experimental
public interface CloudOperationsClient {
@Experimental
static CloudOperationsClient newInstance(CloudServiceStubs service) {
return new CloudOperationsClientImpl(service);
}

/** Get the raw cloud service stubs. */
@Experimental
CloudServiceStubs getCloudServiceStubs();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
*
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this material except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.temporal.client;

import io.temporal.serviceclient.CloudServiceStubs;

class CloudOperationsClientImpl implements CloudOperationsClient {
private final CloudServiceStubs cloudServiceStubs;

CloudOperationsClientImpl(CloudServiceStubs cloudServiceStubs) {
this.cloudServiceStubs = cloudServiceStubs;
}

@Override
public CloudServiceStubs getCloudServiceStubs() {
return cloudServiceStubs;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
*
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this material except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.temporal.client;

import io.temporal.api.cloud.cloudservice.v1.GetNamespaceRequest;
import io.temporal.api.cloud.cloudservice.v1.GetNamespaceResponse;
import io.temporal.serviceclient.CloudServiceStubs;
import io.temporal.serviceclient.CloudServiceStubsOptions;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;

public class CloudOperationsClientTest {
private String namespace;
private String apiKey;
private String apiVersion;

@Before
public void checkCloudEnvVars() {
namespace = System.getenv("TEMPORAL_CLIENT_CLOUD_NAMESPACE");
apiKey = System.getenv("TEMPORAL_CLIENT_CLOUD_API_KEY");
apiVersion = System.getenv("TEMPORAL_CLIENT_CLOUD_API_VERSION");
Assume.assumeTrue(
"Cloud environment variables not present", namespace != null && apiKey != null);
}

@Test
public void simpleCall() {
CloudOperationsClient client =
CloudOperationsClient.newInstance(
CloudServiceStubs.newServiceStubs(
CloudServiceStubsOptions.newBuilder()
.addApiKey(() -> apiKey)
.setVersion(apiVersion)
.build()));
// Do simple get namespace call
GetNamespaceResponse resp =
client
.getCloudServiceStubs()
.blockingStub()
.getNamespace(GetNamespaceRequest.newBuilder().setNamespace(namespace).build());
Assert.assertEquals(namespace, resp.getNamespace().getNamespace());
}
}
9 changes: 9 additions & 0 deletions temporal-serviceclient/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ sourcesJar {
.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE)
}

// Putting protocloud as an additional proto source set
sourceSets {
main {
proto {
srcDir 'src/main/protocloud'
}
}
}

protobuf {
// version/variables substitution is not supported in protobuf section.
// protoc and protoc-gen-grpc-java versions are selected to be compatible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ final class ChannelManager {
private static final Metadata.Key<String> CLIENT_NAME_HEADER_KEY =
Metadata.Key.of("client-name", Metadata.ASCII_STRING_MARSHALLER);

/** refers to the name of the gRPC header that contains the cloud service version */
private static final Metadata.Key<String> CLOUD_VERSION_HEADER_KEY =
Metadata.Key.of("temporal-cloud-api-version", Metadata.ASCII_STRING_MARSHALLER);

private static final String CLIENT_NAME_HEADER_VALUE = "temporal-java";

private final ServiceStubsOptions options;
Expand All @@ -93,6 +97,18 @@ final class ChannelManager {

public ChannelManager(
ServiceStubsOptions options, List<ClientInterceptor> additionalHeadInterceptors) {
this(options, additionalHeadInterceptors, null);
}

public ChannelManager(
ServiceStubsOptions options,
List<ClientInterceptor> additionalHeadInterceptors,
@Nullable Capabilities fixedServerCapabilities) {
// If fixed capabilities are present, set them on the future
if (fixedServerCapabilities != null) {
serverCapabilitiesFuture.complete(fixedServerCapabilities);
}

// Do not shutdown a channel passed to the constructor from outside
this.channelNeedsShutdown = options.getChannel() == null;

Expand Down Expand Up @@ -154,6 +170,12 @@ private Channel applyHeadStandardInterceptors(Channel channel) {
headers.put(LIBRARY_VERSION_HEADER_KEY, Version.LIBRARY_VERSION);
headers.put(SUPPORTED_SERVER_VERSIONS_HEADER_KEY, Version.SUPPORTED_SERVER_VERSIONS);
headers.put(CLIENT_NAME_HEADER_KEY, CLIENT_NAME_HEADER_VALUE);
if (options instanceof CloudServiceStubsOptions) {
String version = ((CloudServiceStubsOptions) options).getVersion();
if (version != null) {
headers.put(CLOUD_VERSION_HEADER_KEY, version);
}
}

return ClientInterceptors.intercept(
channel,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 Temporal Technologies, Inc. All Rights Reserved.
*
* Copyright (C) 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this material except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.temporal.serviceclient;

import static io.temporal.internal.WorkflowThreadMarker.enforceNonWorkflowThread;

import io.temporal.api.cloud.cloudservice.v1.CloudServiceGrpc;
import io.temporal.internal.WorkflowThreadMarker;

/**
* Initializes and holds gRPC blocking and future stubs.
*
* <p>WARNING: The cloud service is currently experimental.
*/
public interface CloudServiceStubs
extends ServiceStubs<
CloudServiceGrpc.CloudServiceBlockingStub, CloudServiceGrpc.CloudServiceFutureStub> {
String HEALTH_CHECK_SERVICE_NAME = "temporal.api.cloud.cloudservice.v1.CloudService";

/** Creates CloudService gRPC stubs pointed on to Temporal Cloud. */
static CloudServiceStubs newCloudServiceStubs() {
return newServiceStubs(CloudServiceStubsOptions.getDefaultInstance());
}

/**
* Creates CloudService gRPC stubs<br>
* This method creates stubs with lazy connectivity, connection is not performed during the
* creation time and happens on the first request.
*
* @param options stub options to use
*/
static CloudServiceStubs newServiceStubs(CloudServiceStubsOptions options) {
enforceNonWorkflowThread();
return WorkflowThreadMarker.protectFromWorkflowThread(
new CloudServiceStubsImpl(options), CloudServiceStubs.class);
}
}
Loading
Loading