diff options
6 files changed, 276 insertions, 38 deletions
diff --git a/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/application.yaml b/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/application.yaml index 98d3b6461..d6aa5d951 100644 --- a/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/application.yaml +++ b/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/application.yaml @@ -6,6 +6,8 @@ spring: server: port: ${PORT} + ssl: + enabled: false error: path: /error diff --git a/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/logback.xml b/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/logback.xml new file mode 100644 index 000000000..d013c0909 --- /dev/null +++ b/examples/src/main/resources/clamp/acm/acelement-helm/acelement/resources/config/logback.xml @@ -0,0 +1,206 @@ +<!-- + ============LICENSE_START======================================================= + policy-clamp + ================================================================================ + Copyright (C) 2022 Nordix Foundation. + ================================================================================ + Licensed 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. + ============LICENSE_END========================================================= + --> + +<configuration scan="true" scanPeriod="30 seconds" debug="false"> + + <property name="logDir" value="${POLICY_LOGS}" /> + + <property name="errorLog" value="error" /> + <property name="debugLog" value="debug" /> + <property name="networkLog" value="network" /> + + <property name="metricLog" value="metric" /> + <property name="transactionLog" value="audit" /> + + <property name="debugPattern" + value="[%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00, UTC}|%level|%logger{0}|%thread] %msg%n" /> + <property name="errorPattern" value="${debugPattern}" /> + <property name="networkPattern" + value="[%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00, UTC}|%t]%m%n" /> + + <property + name="mpPreamble" + value="%X{RequestID}|%X{InvocationID}|%X{ServiceName}|%X{PartnerName}" /> + + <property + name="mpTime" + value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{ElapsedTime}" /> + + <property + name="mpLine1" + value="%X{ServiceInstanceID}|%X{VirtualServerName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}" /> + + <property + name="mpLine2" + value="%X{InstanceUUID}|%X{Severity}|%X{TargetEntity}|%X{TargetServiceName}|%X{Server}|%X{ServerIPAddress}" /> + + <property + name="mpLine3" + value="%X{ServerFQDN}|%X{ClientIPAddress}|%X{ProcessKey}|%X{RemoteHost}|%X{AlertSeverity}" /> + + <property + name="mpLine4" + value="%X{TargetVirtualEntity}|%level|%thread| %msg%n" /> + + + <property name="metricPattern" + value="{$mpPreamble}|{$mpTime}|{$mpLine1}|{$mpLine2}|{$mpLine3}|$mpLine4" /> + + <property name="transactionPattern" value="${metricPattern}" /> + + <appender name="ErrorOut" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDir}/${errorLog}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logDir}/${errorLog}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>50MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>10GB</totalSizeCap> + </rollingPolicy> + <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> + <level>WARN</level> + </filter> + <encoder> + <pattern>${errorPattern}</pattern> + </encoder> + </appender> + + <appender name="AsyncErrorOut" + class="ch.qos.logback.classic.AsyncAppender"> + <appender-ref ref="ErrorOut" /> + </appender> + + <appender name="DebugOut" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDir}/${debugLog}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logDir}/${debugLog}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>50MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>10GB</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${debugPattern}</pattern> + </encoder> + </appender> + + <appender name="AsyncDebugOut" + class="ch.qos.logback.classic.AsyncAppender"> + <appender-ref ref="DebugOut" /> + </appender> + + <appender name="NetworkOut" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDir}/${networkLog}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logDir}/${networkLog}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>50MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>10GB</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${networkPattern}</pattern> + </encoder> + </appender> + + <appender name="AsyncNetworkOut" + class="ch.qos.logback.classic.AsyncAppender"> + <appender-ref ref="NetworkOut" /> + </appender> + + <appender name="MetricOut" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDir}/${metricLog}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logDir}/${metricLog}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>50MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>10GB</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${metricPattern}</pattern> + </encoder> + </appender> + + <appender name="AsyncMetricOut" + class="ch.qos.logback.classic.AsyncAppender"> + <appender-ref ref="MetricOut" /> + </appender> + + <appender name="TransactionOut" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${logDir}/${transactionLog}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logDir}/${transactionLog}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>50MB</maxFileSize> + <maxHistory>30</maxHistory> + <totalSizeCap>10GB</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${transactionPattern}</pattern> + </encoder> + </appender> + + <appender name="AsyncTransactionOut" + class="ch.qos.logback.classic.AsyncAppender"> + <appender-ref ref="TransactionOut" /> + </appender> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <Pattern>[%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00, UTC}|%level|%logger{0}|%thread] %msg%n</Pattern> + </encoder> + </appender> + + <appender name="AsyncStdOut" class="ch.qos.logback.classic.AsyncAppender"> + <appender-ref ref="STDOUT" /> + </appender> + + <logger name="network" level="INFO" additivity="false"> + <appender-ref ref="AsyncNetworkOut" /> + <appender-ref ref="AsyncStdOut" /> + </logger> + + <logger name="org.eclipse.jetty.server.RequestLog" level="info" additivity="false"> + <appender-ref ref="AsyncNetworkOut" /> + <appender-ref ref="AsyncStdOut" /> + </logger> + + <logger name="org.eclipse.jetty" level="ERROR" /> + + <root level="INFO"> + <appender-ref ref="AsyncDebugOut" /> + <appender-ref ref="AsyncErrorOut" /> + <appender-ref ref="AsyncMetricOut" /> + <appender-ref ref="AsyncTransactionOut" /> + <appender-ref ref="AsyncStdOut" /> + </root> + +</configuration> diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidator.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidator.java index 8267bda13..67bdc0b40 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidator.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidator.java @@ -1,6 +1,6 @@ /*- * ========================LICENSE_START================================= - * Copyright (C) 2021 Nordix Foundation. All rights reserved. + * Copyright (C) 2021-2022 Nordix Foundation. All rights reserved. * ====================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; import lombok.SneakyThrows; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException; import org.onap.policy.clamp.acm.participant.kubernetes.handler.AutomationCompositionElementHandler; import org.onap.policy.clamp.acm.participant.kubernetes.models.ChartInfo; @@ -62,58 +63,64 @@ public class PodStatusValidator implements Runnable { @Override public void run() { logger.info("Polling the status of deployed pods for the chart {}", chart.getChartId().getName()); - Map<String, String> podStatusMap; - String output = null; + + try { + verifyPodStatus(); + } catch (ServiceException | IOException e) { + throw new ServiceException("Error verifying the status of the pod. Exiting", e); + } + } + + private void verifyPodStatus() throws ServiceException, IOException, InterruptedException { var isVerified = false; long endTime = System.currentTimeMillis() + (timeout * 1000L); while (!isVerified && System.currentTimeMillis() < endTime) { - try { - output = HelmClient.executeCommand(verifyPodStatusCommand(chart)); - podStatusMap = mapPodStatus(output); - isVerified = podStatusMap.values() - .stream() - .allMatch("Running"::equals); - if (! isVerified) { - logger.info("Waiting for the pods to be active for the chart {}", chart.getChartId().getName()); - podStatusMap.forEach((key, value) -> logger.info("Pod: {} , state: {}", key, value)); - AutomationCompositionElementHandler.getPodStatusMap().put(chart.getReleaseName(), podStatusMap); - // Recheck status of pods in specific intervals. - Thread.sleep(statusCheckInterval * 1000L); - } else { - logger.info("All pods are in running state for the helm chart {}", chart.getChartId().getName()); - AutomationCompositionElementHandler.getPodStatusMap().put(chart.getReleaseName(), podStatusMap); - } - } catch (ServiceException | IOException e) { - throw new ServiceException("Error verifying the status of the pod. Exiting", e); + var output = HelmClient.executeCommand(verifyPodStatusCommand(chart)); + var podStatusMap = mapPodStatus(output); + isVerified = !podStatusMap.isEmpty() + && podStatusMap.values().stream().allMatch("Running"::equals); + if (!isVerified) { + logger.info("Waiting for the pods to be active for the chart {}", chart.getChartId().getName()); + podStatusMap.forEach((key, value) -> logger.info("Pod: {} , state: {}", key, value)); + // Recheck status of pods in specific intervals. + Thread.sleep(statusCheckInterval * 1000L); + } else { + logger.info("All pods are in running state for the helm chart {}", chart.getChartId().getName()); + AutomationCompositionElementHandler.getPodStatusMap().put(chart.getReleaseName(), podStatusMap); } } + if (!isVerified) { + throw new ServiceException("Time out Exception verifying the status of the pod"); + } } private ProcessBuilder verifyPodStatusCommand(ChartInfo chart) { - String cmd = "kubectl get pods --namespace " + chart.getNamespace() + " | grep " - + chart.getChartId().getName(); + String cmd = "kubectl get pods --namespace " + chart.getNamespace() + " | grep " + getPodName(); return new ProcessBuilder("sh", "-c", cmd); } + private String getPodName() { + return StringUtils.isNotEmpty(chart.getPodName()) ? chart.getPodName() : chart.getChartId().getName(); + } - private Map<String, String> mapPodStatus(String output) throws IOException, ServiceException { + private Map<String, String> mapPodStatus(String output) throws IOException { Map<String, String> podStatusMap = new HashMap<>(); + var podName = getPodName(); try (var reader = new BufferedReader(new InputStreamReader(IOUtils.toInputStream(output, StandardCharsets.UTF_8)))) { var line = reader.readLine(); while (line != null) { - if (line.contains(chart.getChartId().getName())) { + if (line.contains(podName)) { var result = line.split("\\s+"); podStatusMap.put(result[0], result[2]); } line = reader.readLine(); } } - if (!podStatusMap.isEmpty()) { - return podStatusMap; - } else { - throw new ServiceException("Status of Pod is empty"); + if (podStatusMap.isEmpty()) { + logger.warn("Status of Pod {} is empty", podName); } + return podStatusMap; } } diff --git a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/models/ChartInfo.java b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/models/ChartInfo.java index b925e782d..e2c4c2e11 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/models/ChartInfo.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/main/java/org/onap/policy/clamp/acm/participant/kubernetes/models/ChartInfo.java @@ -34,6 +34,8 @@ public class ChartInfo { @NonNull private ToscaConceptIdentifier chartId; + private String podName; + @NonNull private String namespace; diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidatorTest.java b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidatorTest.java index fbddf8b28..962744db7 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidatorTest.java +++ b/participant/participant-impl/participant-impl-kubernetes/src/test/java/org/onap/policy/clamp/acm/participant/kubernetes/helm/PodStatusValidatorTest.java @@ -35,7 +35,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; import org.mockito.MockedStatic; import org.onap.policy.clamp.acm.participant.kubernetes.exception.ServiceException; import org.onap.policy.clamp.acm.participant.kubernetes.handler.AutomationCompositionElementHandler; @@ -51,20 +50,16 @@ class PodStatusValidatorTest { private static final Coder CODER = new StandardCoder(); private static final String CHART_INFO_YAML = "src/test/resources/ChartList.json"; + private static int TIMEOUT = 2; + private static int STATUS_CHECK_INTERVAL = 1; private static List<ChartInfo> charts; - @InjectMocks - private static PodStatusValidator podStatusValidator; - private static MockedStatic<HelmClient> mockedClient; @BeforeAll static void init() throws CoderException { charts = CODER.decode(new File(CHART_INFO_YAML), ChartList.class).getCharts(); mockedClient = mockStatic(HelmClient.class); - int timeout = 60; - int statusCheckInterval = 30; - podStatusValidator = new PodStatusValidator(charts.get(0), timeout, statusCheckInterval); } @AfterEach @@ -77,12 +72,12 @@ class PodStatusValidatorTest { mockedClient.close(); } - @Test void test_RunningPodState() { String runningPod = "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\r\nHelloWorld-54777df9f8-qpzqr\t1/1\tRunning\t0\t9h"; mockedClient.when(() -> HelmClient.executeCommand(any())) .thenReturn(runningPod); + var podStatusValidator = new PodStatusValidator(charts.get(0), TIMEOUT, STATUS_CHECK_INTERVAL); assertDoesNotThrow(() -> podStatusValidator.run()); assertThat(AutomationCompositionElementHandler.getPodStatusMap()).hasSize(1); assertThat(AutomationCompositionElementHandler.getPodStatusMap()).containsKey(charts.get(0).getReleaseName()); @@ -90,15 +85,28 @@ class PodStatusValidatorTest { .containsValue(Map.of("HelloWorld-54777df9f8-qpzqr", "Running")); } - @Test void test_InvalidPodState() { String invalidPod = "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\nhellofromdocker-54777df9f8-qpzqr\t1/1\tInit\t0\t9h"; mockedClient.when(() -> HelmClient.executeCommand(any())) .thenReturn(invalidPod); + var podStatusValidator = new PodStatusValidator(charts.get(1), TIMEOUT, STATUS_CHECK_INTERVAL); assertThatThrownBy(() -> podStatusValidator.run()) .isInstanceOf(ServiceException.class).hasMessage("Error verifying the status of the pod. Exiting"); assertThat(AutomationCompositionElementHandler.getPodStatusMap()).isEmpty(); } + // Use case scenario: Hard coded pod name + @Test + void test_RunningPodStateWhitPodName() { + String runningPod = "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\r\nhelloallworld-54777df9f8-qpzqr\t1/1\tRunning\t0\t9h"; + mockedClient.when(() -> HelmClient.executeCommand(any())) + .thenReturn(runningPod); + var podStatusValidator = new PodStatusValidator(charts.get(2), TIMEOUT, STATUS_CHECK_INTERVAL); + assertDoesNotThrow(() -> podStatusValidator.run()); + assertThat(AutomationCompositionElementHandler.getPodStatusMap()).hasSize(1); + assertThat(AutomationCompositionElementHandler.getPodStatusMap()).containsKey(charts.get(2).getReleaseName()); + assertThat(AutomationCompositionElementHandler.getPodStatusMap()) + .containsValue(Map.of("helloallworld-54777df9f8-qpzqr", "Running")); + } } diff --git a/participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json b/participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json index d4cd5de47..f58049640 100644 --- a/participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json +++ b/participant/participant-impl/participant-impl-kubernetes/src/test/resources/ChartList.json @@ -19,6 +19,19 @@ }, "namespace" : "onap", "releaseName" : "nginxapp" + }, + { + "chartId" : { + "name" : "HelloWorld", + "version" : "1.2" + }, + "namespace" : "onap", + "repository" : { + "repoName": "chartMuseum", + "address" : "https://localhost:8080" + }, + "releaseName" : "helloworld", + "podName" : "helloallworld" } ] } |