From 81ded4acf503250c16570ab4e543880924d47196 Mon Sep 17 00:00:00 2001 From: lbc97 <82015813+lbc97@users.noreply.github.com> Date: Fri, 22 Apr 2022 18:45:35 +0800 Subject: [PATCH] servicecomb 2.0 plugin (#127) --- .github/workflows/plugins-test.2.yaml | 1 + CHANGES.md | 1 + .../apm-sdk-plugin/servicecomb-plugin/pom.xml | 1 + .../pom.xml | 46 ++++++ .../ProducerOperationHandlerInterceptor.java | 76 +++++++++ .../v2/TransportClientHandlerInterceptor.java | 94 +++++++++++ ...oducerOperationHandlerInstrumentation.java | 74 +++++++++ ...TransportClientHandlerInstrumentation.java | 73 +++++++++ .../src/main/resources/skywalking-plugin.def | 18 +++ .../service-agent/java-agent/Plugin-list.md | 1 + .../java-agent/Supported-list.md | 2 +- .../servicecomb-2.x-scenario/bin/startup.sh | 21 +++ .../config/expectedData.yaml | 67 ++++++++ .../configuration.yml | 22 +++ .../servicecomb-2.x-scenario/pom.xml | 147 ++++++++++++++++++ .../src/main/assembly/assembly.xml | 41 +++++ .../servicecomb/ConsumerApplication.java | 36 +++++ .../CodeFirstPojoConsumerHelloImpl.java | 70 +++++++++ .../CodeFirstPojoHelloProviderImpl.java | 43 +++++ .../testcase/servicecomb/schema/Hello.java | 30 ++++ .../servicecomb/schema/models/Person.java | 35 +++++ .../resources/META-INF/spring/pojo.bean.xml | 28 ++++ .../src/main/resources/log4j.properties | 21 +++ .../src/main/resources/microservice.yaml | 47 ++++++ .../support-version.list | 18 +++ 25 files changed, 1012 insertions(+), 1 deletion(-) create mode 100644 apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml create mode 100644 apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/ProducerOperationHandlerInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/TransportClientHandlerInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/ProducerOperationHandlerInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/TransportClientHandlerInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/resources/skywalking-plugin.def create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/bin/startup.sh create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/config/expectedData.yaml create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/configuration.yml create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/pom.xml create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/assembly/assembly.xml create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/ConsumerApplication.java create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/consumer/CodeFirstPojoConsumerHelloImpl.java create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/provider/CodeFirstPojoHelloProviderImpl.java create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/Hello.java create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/models/Person.java create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/META-INF/spring/pojo.bean.xml create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/log4j.properties create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/microservice.yaml create mode 100644 test/plugin/scenarios/servicecomb-2.x-scenario/support-version.list diff --git a/.github/workflows/plugins-test.2.yaml b/.github/workflows/plugins-test.2.yaml index 08e48d687..07138379d 100644 --- a/.github/workflows/plugins-test.2.yaml +++ b/.github/workflows/plugins-test.2.yaml @@ -63,6 +63,7 @@ jobs: - redisson-scenario - resttemplate-4.x-scenario - servicecomb-1.x-scenario + - servicecomb-2.x-scenario - shardingsphere-3.x-scenario - shardingsphere-4.0.x-scenario - shardingsphere-4.x-scenario diff --git a/CHANGES.md b/CHANGES.md index b0117af43..bbb594179 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Release Notes. * Fix `cluster` and `namespace` value duplicated(`namespace` value) in properties report. * Add layer field to event when reporting. * Remove redundant `shade.package` property. +* Add servicecomb-2.x plugin and Testcase. #### Documentation diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml index 5b456c6ab..f00e39166 100644 --- a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/pom.xml @@ -29,6 +29,7 @@ servicecomb-java-chassis-1.x-plugin + servicecomb-java-chassis-2.x-plugin pom diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml new file mode 100644 index 000000000..b90ac297f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/pom.xml @@ -0,0 +1,46 @@ + + + + + + servicecomb-plugin + org.apache.skywalking + 8.11.0-SNAPSHOT + + 4.0.0 + + apm-servicecomb-java-chassis-2.x-plugin + jar + + servicecomb-java-chassis-2.x-plugin + http://maven.apache.org + + + 2.6.0 + + + + + org.apache.servicecomb + java-chassis-core + ${java-chassis-core.version} + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/ProducerOperationHandlerInterceptor.java b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/ProducerOperationHandlerInterceptor.java new file mode 100644 index 000000000..3befb8846 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/ProducerOperationHandlerInterceptor.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.plugin.servicecomb.v2; + +import java.lang.reflect.Method; + +import org.apache.servicecomb.core.Invocation; +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +public class ProducerOperationHandlerInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Invocation invocation = (Invocation) allArguments[0]; + ContextCarrier contextCarrier = new ContextCarrier(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(invocation.getContext().get(next.getHeadKey())); + } + String operationName = invocation.getMicroserviceQualifiedName(); + AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); + String url = invocation.getOperationMeta().getOperationPath(); + Tags.URL.set(span, url); + span.setComponent(ComponentsDefine.SERVICECOMB); + SpanLayer.asRPCFramework(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Invocation invocation = (Invocation) allArguments[0]; + AbstractSpan span = ContextManager.activeSpan(); + int statusCode = invocation.getStatus().getStatusCode(); + if (statusCode >= 400) { + span.errorOccurred(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, statusCode); + } + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + AbstractSpan span = ContextManager.activeSpan(); + span.log(t); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/TransportClientHandlerInterceptor.java b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/TransportClientHandlerInterceptor.java new file mode 100644 index 000000000..c7fc23c9c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/TransportClientHandlerInterceptor.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.plugin.servicecomb.v2; + +import java.lang.reflect.Method; +import java.net.URI; + +import org.apache.servicecomb.core.Invocation; +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +public class TransportClientHandlerInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Invocation invocation = (Invocation) allArguments[0]; + if (!checkRegisterStatus(invocation)) { + return; + } + URI uri = new URI(invocation.getEndpoint().toString()); + String peer = uri.getHost() + ":" + uri.getPort(); + String operationName = invocation.getMicroserviceQualifiedName(); + final ContextCarrier contextCarrier = new ContextCarrier(); + AbstractSpan span = ContextManager.createExitSpan(operationName, contextCarrier, peer); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + invocation.getContext().put(next.getHeadKey(), next.getHeadValue()); + } + String url = invocation.getOperationMeta().getOperationPath(); + Tags.URL.set(span, url); + span.setComponent(ComponentsDefine.SERVICECOMB); + SpanLayer.asRPCFramework(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Invocation invocation = (Invocation) allArguments[0]; + if (!checkRegisterStatus(invocation)) { + return ret; + } + AbstractSpan span = ContextManager.activeSpan(); + int statusCode = invocation.getStatus().getStatusCode(); + if (statusCode >= 400) { + span.errorOccurred(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, statusCode); + } + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + Invocation invocation = (Invocation) allArguments[0]; + AbstractSpan span = ContextManager.activeSpan(); + span.log(t); + } + + /** + * Serviecomb chassis Consumers and providers need to register at the service center. If the consumer is not + * registered then return false. + */ + private Boolean checkRegisterStatus(Invocation invocation) { + return null != invocation.getOperationMeta() && null != invocation.getEndpoint(); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/ProducerOperationHandlerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/ProducerOperationHandlerInstrumentation.java new file mode 100644 index 000000000..8daf9626b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/ProducerOperationHandlerInstrumentation.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.plugin.servicecomb.v2.define; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +/** + * {@link ProducerOperationHandlerInstrumentation} represents that skywalking intercept {@link + * org.apache.servicecomb.core.handler.impl.ProducerOperationHandler#handle(org.apache.servicecomb.core.Invocation, + * org.apache.servicecomb.swagger.invocation.AsyncResponse)} by using ProducerOperationHandlerInterceptor + */ +public class ProducerOperationHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.apache.servicecomb.core.handler.impl.ProducerOperationHandler"; + + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.servicecomb.v2.ProducerOperationHandlerInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("handle"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/TransportClientHandlerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/TransportClientHandlerInstrumentation.java new file mode 100644 index 000000000..600069963 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/servicecomb/v2/define/TransportClientHandlerInstrumentation.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.plugin.servicecomb.v2.define; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; + +/** + * {@link TransportClientHandlerInstrumentation} represents that skywalking intercept {@link + * org.apache.servicecomb.core.handler.impl.TransportClientHandler} by using linkTransportClientHandlerInterceptor + */ +public class TransportClientHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.apache.servicecomb.core.handler.impl.TransportClientHandler"; + + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.servicecomb.v2.TransportClientHandlerInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("handle"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..1b34bbb58 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/servicecomb-plugin/servicecomb-java-chassis-2.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file 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. + +servicecomb-2.x=org.apache.skywalking.apm.plugin.servicecomb.v2.define.ProducerOperationHandlerInstrumentation +servicecomb-2.x=org.apache.skywalking.apm.plugin.servicecomb.v2.define.TransportClientHandlerInstrumentation diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md index 442073ee6..770b0fe72 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -78,6 +78,7 @@ - rocketMQ-4.x - sentinel-1.x - servicecomb-1.x +- servicecomb-2.x - sharding-sphere-3.x - sharding-sphere-4.0.0 - sharding-sphere-4.1.0 diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 4385197f3..730b5fd38 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -50,7 +50,7 @@ metrics based on the tracing data. * [Apache Dubbo](https://github.com/apache/dubbo) 2.7.x -> 3.x * [Motan](https://github.com/weibocom/motan) 0.2.x -> 1.1.0 * [gRPC](https://github.com/grpc/grpc-java) 1.x - * [Apache ServiceComb Java Chassis](https://github.com/apache/servicecomb-java-chassis) 1.x + * [Apache ServiceComb Java Chassis](https://github.com/apache/servicecomb-java-chassis) 1.x, 2.x * [SOFARPC](https://github.com/alipay/sofa-rpc) 5.4.0 * [Armeria](https://github.com/line/armeria) 0.63.0 -> 0.98.0 * [Apache Avro](http://avro.apache.org) 1.7.0 - 1.8.x diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/bin/startup.sh b/test/plugin/scenarios/servicecomb-2.x-scenario/bin/startup.sh new file mode 100644 index 000000000..54300d80d --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file 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. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/servicecomb-2.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/servicecomb-2.x-scenario/config/expectedData.yaml new file mode 100644 index 000000000..5264ffea8 --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/config/expectedData.yaml @@ -0,0 +1,67 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file 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. +segmentItems: + - serviceName: servicecomb-2.x-scenario + segmentSize: gt 2 + segments: + - segmentId: not null + spans: + - operationName: codefirsttest.codeFirstHello.sayHi + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 28 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: /sayHi} + refs: + - {parentEndpoint: codefirsttest.codeFirstSpringmvcHelloClient.say, networkAddress: not + null, refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not + null, parentServiceInstance: not null, parentService: not null, traceId: not + null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: codefirsttest.codeFirstHello.sayHi + parentSpanId: 0 + spanId: 1 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 28 + isError: false + spanType: Exit + peer: not null + tags: + - {key: url, value: /sayHi} + skipAnalysis: 'false' + - operationName: codefirsttest.codeFirstSpringmvcHelloClient.say + parentSpanId: -1 + spanId: 0 + spanLayer: RPCFramework + startTime: nq 0 + endTime: nq 0 + componentId: 28 + isError: false + spanType: Entry + peer: '' + tags: + - {key: url, value: /say} + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/configuration.yml b/test/plugin/scenarios/servicecomb-2.x-scenario/configuration.yml new file mode 100644 index 000000000..01b28be88 --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/configuration.yml @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file 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. + +type: jvm +entryService: http://localhost:8080/servicecomb/say +healthCheck: http://localhost:8080/servicecomb/healthCheck +startScript: ./bin/startup.sh +environment: +dependencies: diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/pom.xml b/test/plugin/scenarios/servicecomb-2.x-scenario/pom.xml new file mode 100644 index 000000000..c583273dd --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/pom.xml @@ -0,0 +1,147 @@ + + + + + org.apache.skywalking.apm.testcase + servicecomb-2.x-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + 2.6.0 + 5.2.9.RELEASE + 2.11.2 + + + skywalking-servicecomb-2.x-scenario + + + + org.slf4j + slf4j-log4j12 + 1.7.30 + + + org.apache.servicecomb + transport-rest-vertx + ${test.framework.version} + + + org.apache.servicecomb + registry-local + ${test.framework.version} + + + jackson-core + com.fasterxml.jackson.core + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + ${test.framework.version} + + + logback-classic + ch.qos.logback + + + + + org.apache.servicecomb + foundation-test-scaffolding + ${test.framework.version} + compile + + + jackson-annotations + com.fasterxml.jackson.core + + + jackson-databind + com.fasterxml.jackson.core + + + jackson-core + com.fasterxml.jackson.core + + + + + javax.validation + validation-api + 2.0.1.Final + + + + + + servicecomb-2.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.6.RELEASE + + + + repackage + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + -parameters + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/assembly/assembly.xml new file mode 100644 index 000000000..1eb88bffd --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/servicecomb-2.x-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/ConsumerApplication.java b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/ConsumerApplication.java new file mode 100644 index 000000000..90764d795 --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/ConsumerApplication.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.testcase.servicecomb; + +import org.apache.servicecomb.springboot2.starter.EnableServiceComb; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +@EnableServiceComb +public class ConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/consumer/CodeFirstPojoConsumerHelloImpl.java b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/consumer/CodeFirstPojoConsumerHelloImpl.java new file mode 100644 index 000000000..f3149192c --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/consumer/CodeFirstPojoConsumerHelloImpl.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.testcase.servicecomb.consumer; + +import java.util.concurrent.ExecutionException; + +import org.apache.log4j.Logger; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.apache.skywalking.apm.testcase.servicecomb.schema.Hello; +import org.apache.skywalking.apm.testcase.servicecomb.schema.models.Person; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +@RpcSchema(schemaId = "codeFirstSpringmvcHelloClient") +@RequestMapping(path = "/servicecomb") +public class CodeFirstPojoConsumerHelloImpl { + private static Logger LOGGER = Logger.getLogger(CodeFirstPojoConsumerHelloImpl.class); + + @RpcReference(microserviceName = "codefirsttest", schemaId = "codeFirstHello") + private Hello hello; + + @RequestMapping(path = "/say", method = RequestMethod.GET) + public String say() { + String repo = "sayHi invoke filed"; + try { + repo = hello.sayHi("Java Chassis"); + } catch (Exception e) { + LOGGER.error("sayHi invoke filed"); + } + return repo; + } + + @RequestMapping(path = "/say1", method = RequestMethod.GET) + public String say1(@RequestParam String name) { + Person person = new Person(); + person.setName(name); + String repo = "hello"; + try { + repo = hello.sayHello(person).get(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + return repo; + } + + @RequestMapping(path = "/healthCheck", method = {RequestMethod.HEAD}) + public String healthCheck() { + return "Success"; + } +} diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/provider/CodeFirstPojoHelloProviderImpl.java b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/provider/CodeFirstPojoHelloProviderImpl.java new file mode 100644 index 000000000..d2ae42bc3 --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/provider/CodeFirstPojoHelloProviderImpl.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.testcase.servicecomb.provider; + +import java.util.concurrent.CompletableFuture; + +import org.apache.log4j.Logger; +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.apache.skywalking.apm.testcase.servicecomb.schema.Hello; +import org.apache.skywalking.apm.testcase.servicecomb.schema.models.Person; + +@RpcSchema(schemaId = "codeFirstHello") +public class CodeFirstPojoHelloProviderImpl implements Hello { + private static Logger LOGGER = Logger.getLogger(CodeFirstPojoHelloProviderImpl.class); + + @Override + public String sayHi(String name) { + return "Pojo Hello " + name; + } + + @Override + public CompletableFuture sayHello(Person person) { + CompletableFuture future = new CompletableFuture<>(); + future.complete("Pojo Hello person " + person.getName()); + return future; + } +} diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/Hello.java b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/Hello.java new file mode 100644 index 000000000..a4288a9b3 --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/Hello.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.testcase.servicecomb.schema; + +import java.util.concurrent.CompletableFuture; + +import org.apache.skywalking.apm.testcase.servicecomb.schema.models.Person; + +public interface Hello { + + String sayHi(String name); + + CompletableFuture sayHello(Person person); +} diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/models/Person.java b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/models/Person.java new file mode 100644 index 000000000..d3f5c13fd --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/servicecomb/schema/models/Person.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file 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 org.apache.skywalking.apm.testcase.servicecomb.schema.models; + +import org.springframework.stereotype.Component; + +@Component +public class Person { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/META-INF/spring/pojo.bean.xml b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/META-INF/spring/pojo.bean.xml new file mode 100644 index 000000000..bdff7b99f --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/META-INF/spring/pojo.bean.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/log4j.properties b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/log4j.properties new file mode 100644 index 000000000..f64a9f7c5 --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/log4j.properties @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file 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. +# +# +log4j.rootLogger=INFO,stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n \ No newline at end of file diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/microservice.yaml b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/microservice.yaml new file mode 100644 index 000000000..afe575f3b --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/src/main/resources/microservice.yaml @@ -0,0 +1,47 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file 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. + +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file 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. +## --------------------------------------------------------------------------- + +server: + port: 8080 + +# java-chassis configurations +servicecomb: + service: + application: codefirst + name: codefirsttest + version: 0.0.1 + rest: + address: 0.0.0.0:8080 + handler: + chain: + Consumer: + default: loadbalance diff --git a/test/plugin/scenarios/servicecomb-2.x-scenario/support-version.list b/test/plugin/scenarios/servicecomb-2.x-scenario/support-version.list new file mode 100644 index 000000000..e27c3c27c --- /dev/null +++ b/test/plugin/scenarios/servicecomb-2.x-scenario/support-version.list @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file 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. + +2.6.0 +2.7.0