From a46287c35949f828619cd08e195914c8d1afd12b Mon Sep 17 00:00:00 2001 From: Shekhar Saxena Date: Fri, 6 Dec 2024 02:15:28 +0530 Subject: [PATCH] adding basic abstraction for updaters Signed-off-by: Shekhar Saxena --- .../exceptions/ApplyRecommendationsError.java | 26 +++++ .../InvalidRecommendationUpdaterType.java | 26 +++++ .../updater/RecommendationUpdater.java | 66 +++++++++++ .../updater/RecommendationUpdaterImpl.java | 108 ++++++++++++++++++ .../updater/vpa/VpaUpdaterImpl.java | 67 +++++++++++ .../analyzer/utils/AnalyzerConstants.java | 33 ++++++ .../utils/AnalyzerErrorConstants.java | 10 ++ 7 files changed, 336 insertions(+) create mode 100644 src/main/java/com/autotune/analyzer/exceptions/ApplyRecommendationsError.java create mode 100644 src/main/java/com/autotune/analyzer/exceptions/InvalidRecommendationUpdaterType.java create mode 100644 src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdater.java create mode 100644 src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdaterImpl.java create mode 100644 src/main/java/com/autotune/analyzer/recommendations/updater/vpa/VpaUpdaterImpl.java diff --git a/src/main/java/com/autotune/analyzer/exceptions/ApplyRecommendationsError.java b/src/main/java/com/autotune/analyzer/exceptions/ApplyRecommendationsError.java new file mode 100644 index 000000000..45fa5df64 --- /dev/null +++ b/src/main/java/com/autotune/analyzer/exceptions/ApplyRecommendationsError.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, IBM Corporation and others. + * + * 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. + *******************************************************************************/ + +package com.autotune.analyzer.exceptions; + +public class ApplyRecommendationsError extends Exception { + public ApplyRecommendationsError() { + } + + public ApplyRecommendationsError(String message) { + super(message); + } +} diff --git a/src/main/java/com/autotune/analyzer/exceptions/InvalidRecommendationUpdaterType.java b/src/main/java/com/autotune/analyzer/exceptions/InvalidRecommendationUpdaterType.java new file mode 100644 index 000000000..67ed3ed78 --- /dev/null +++ b/src/main/java/com/autotune/analyzer/exceptions/InvalidRecommendationUpdaterType.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, IBM Corporation and others. + * + * 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. + *******************************************************************************/ + +package com.autotune.analyzer.exceptions; + +public class InvalidRecommendationUpdaterType extends Exception { + public InvalidRecommendationUpdaterType() { + } + + public InvalidRecommendationUpdaterType(String message) { + super(message); + } +} diff --git a/src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdater.java b/src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdater.java new file mode 100644 index 000000000..2ddd75cee --- /dev/null +++ b/src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdater.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, IBM Corporation and others. + * + * 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. + *******************************************************************************/ + +package com.autotune.analyzer.recommendations.updater; + +import com.autotune.analyzer.exceptions.ApplyRecommendationsError; +import com.autotune.analyzer.exceptions.InvalidRecommendationUpdaterType; +import com.autotune.analyzer.kruizeObject.KruizeObject; + +/** + * This interface defines the abstraction for updating resource recommendations in a system. + * Implementing classes will provide the logic to update resources with recommendations for a specific resources, + * such as CPU, memory, or any other resources that require periodic or dynamic adjustments. + * + * The RecommendationUpdater interface is designed to be extended by different updater classes. + * For example, vpaUpdaterImpl for updating resources with recommendations related to CPU and memory resources. + */ + +public interface RecommendationUpdater { + /** + * Retrieves an instance of a specific updater implementation based on the provided updater type + * + * @param updaterType String the type of updater to retrieve + * @return RecommendationUpdaterImpl An instance of provided updater type class + * @throws InvalidRecommendationUpdaterType If the provided updater type doesn't match any valid type of updater. + */ + RecommendationUpdaterImpl getUpdaterInstance(String updaterType) throws InvalidRecommendationUpdaterType; + + /** + * Checks whether the necessary updater dependencies are installed or available in the system. + * + * @return boolean true if the required updaters are installed, false otherwise. + */ + boolean isUpdaterInstalled(); + + /** + * Generates resource recommendations for a specific experiment based on the experiment's name. + * + * @param experimentName String The name of the experiment for which the resource recommendations should be generated. + * @return KruizeObject containing recommendations + */ + KruizeObject generateResourceRecommendationsForExperiment(String experimentName); + + /** + * Applies the resource recommendations contained within the provided KruizeObject + * This method will take the KruizeObject, which contains the resource recommendations, + * and apply them to the desired resources. + * + * @param kruizeObject KruizeObject containing the resource recommendations to be applied. + * @throws ApplyRecommendationsError in case of any error. + */ + void applyResourceRecommendationsForExperiment(KruizeObject kruizeObject) throws ApplyRecommendationsError; +} diff --git a/src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdaterImpl.java b/src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdaterImpl.java new file mode 100644 index 000000000..a3cfcc379 --- /dev/null +++ b/src/main/java/com/autotune/analyzer/recommendations/updater/RecommendationUpdaterImpl.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, IBM Corporation and others. + * + * 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. + *******************************************************************************/ + +package com.autotune.analyzer.recommendations.updater; + +import com.autotune.analyzer.exceptions.ApplyRecommendationsError; +import com.autotune.analyzer.exceptions.FetchMetricsError; +import com.autotune.analyzer.exceptions.InvalidRecommendationUpdaterType; +import com.autotune.analyzer.kruizeObject.KruizeObject; +import com.autotune.analyzer.recommendations.engine.RecommendationEngine; +import com.autotune.analyzer.recommendations.updater.vpa.VpaUpdaterImpl; +import com.autotune.analyzer.utils.AnalyzerConstants; +import com.autotune.analyzer.utils.AnalyzerErrorConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RecommendationUpdaterImpl implements RecommendationUpdater { + + private static final Logger LOGGER = LoggerFactory.getLogger(RecommendationUpdaterImpl.class); + + /** + * Retrieves an instance of a specific updater implementation based on the provided updater type + * + * @param updaterType String the type of updater to retrieve + * @return RecommendationUpdaterImpl An instance of provided updater type class + * @throws InvalidRecommendationUpdaterType If the provided updater type doesn't match any valid type of updater. + */ + @Override + public RecommendationUpdaterImpl getUpdaterInstance(String updaterType) throws InvalidRecommendationUpdaterType { + if (AnalyzerConstants.RecommendationUpdaterConstants.SupportedUpdaters.VPA.equalsIgnoreCase(updaterType)) { + return VpaUpdaterImpl.getInstance(); + } else { + throw new InvalidRecommendationUpdaterType(String.format(AnalyzerErrorConstants.RecommendationUpdaterErrors.UNSUPPORTED_UPDATER_TYPE, updaterType)); + } + } + + /** + * Checks whether the necessary updater dependencies are installed or available in the system. + * @return boolean true if the required updaters are installed, false otherwise. + */ + @Override + public boolean isUpdaterInstalled() { + /* + * This function will be implemented by specific updater type child classes + */ + return false; + } + + /** + * Generates resource recommendations for a specific experiment based on the experiment's name. + * + * @param experimentName String The name of the experiment for which the resource recommendations should be generated. + * @return KruizeObject containing recommendations + */ + @Override + public KruizeObject generateResourceRecommendationsForExperiment(String experimentName) { + try { + LOGGER.info(AnalyzerConstants.RecommendationUpdaterConstants.InfoMsgs.GENERATING_RECOMMENDATIONS, experimentName); + // generating latest recommendations for experiment + RecommendationEngine recommendationEngine = new RecommendationEngine(experimentName, null, null); + int calCount = 0; + String validationMessage = recommendationEngine.validate_local(); + if (validationMessage.isEmpty()) { + KruizeObject kruizeObject = recommendationEngine.prepareRecommendations(calCount); + if (kruizeObject.getValidation_data().isSuccess()) { + LOGGER.info(AnalyzerConstants.RecommendationUpdaterConstants.InfoMsgs.GENERATED_RECOMMENDATIONS, experimentName); + return kruizeObject; + } else { + throw new Exception(kruizeObject.getValidation_data().getMessage()); + } + } else { + throw new Exception(validationMessage); + } + } catch (Exception | FetchMetricsError e) { + LOGGER.error(AnalyzerErrorConstants.RecommendationUpdaterErrors.GENERATE_RECOMMNEDATION_FAILED, experimentName); + LOGGER.debug(e.getMessage()); + return null; + } + } + + /** + * Applies the resource recommendations contained within the provided KruizeObject + * This method will take the KruizeObject, which contains the resource recommendations, + * and apply them to the desired resources. + * + * @param kruizeObject KruizeObject containing the resource recommendations to be applied. + * @throws ApplyRecommendationsError in case of any error. + */ + @Override + public void applyResourceRecommendationsForExperiment(KruizeObject kruizeObject) throws ApplyRecommendationsError { + /* + * This function will be implemented by specific updater type child classes + */ + } +} diff --git a/src/main/java/com/autotune/analyzer/recommendations/updater/vpa/VpaUpdaterImpl.java b/src/main/java/com/autotune/analyzer/recommendations/updater/vpa/VpaUpdaterImpl.java new file mode 100644 index 000000000..66c2b80f9 --- /dev/null +++ b/src/main/java/com/autotune/analyzer/recommendations/updater/vpa/VpaUpdaterImpl.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, IBM Corporation and others. + * + * 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. + *******************************************************************************/ + +package com.autotune.analyzer.recommendations.updater.vpa; + +import com.autotune.analyzer.recommendations.updater.RecommendationUpdaterImpl; +import com.autotune.analyzer.utils.AnalyzerConstants; +import com.autotune.analyzer.utils.AnalyzerErrorConstants; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinitionList; +import io.fabric8.kubernetes.client.DefaultKubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.ApiextensionsAPIGroupDSL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VpaUpdaterImpl extends RecommendationUpdaterImpl { + private static final Logger LOGGER = LoggerFactory.getLogger(VpaUpdaterImpl.class); + private static VpaUpdaterImpl vpaUpdater = new VpaUpdaterImpl(); + + private KubernetesClient kubernetesClient; + private ApiextensionsAPIGroupDSL apiextensionsClient; + + + private VpaUpdaterImpl() { + this.kubernetesClient = new DefaultKubernetesClient(); + this.apiextensionsClient = kubernetesClient.apiextensions(); + } + + public static VpaUpdaterImpl getInstance() { + if (vpaUpdater == null) { + vpaUpdater = new VpaUpdaterImpl(); + } + return vpaUpdater; + } + + /** + * Checks whether the necessary updater dependencies are installed or available in the system. + * @return boolean true if the required updaters are installed, false otherwise. + */ + @Override + public boolean isUpdaterInstalled() { + LOGGER.info(AnalyzerConstants.RecommendationUpdaterConstants.InfoMsgs.CHECKING_IF_UPDATER_INSTALLED, + AnalyzerConstants.RecommendationUpdaterConstants.SupportedUpdaters.VPA); + // checking if VPA CRD is present or not + CustomResourceDefinitionList crdList = apiextensionsClient.v1().customResourceDefinitions().list(); + boolean isVpaInstalled = crdList.getItems().stream().anyMatch(crd -> AnalyzerConstants.RecommendationUpdaterConstants.VPA.VPA_PLURAL.equalsIgnoreCase(crd.getSpec().getNames().getKind())); + if (isVpaInstalled) { + LOGGER.info(AnalyzerConstants.RecommendationUpdaterConstants.InfoMsgs.FOUND_UPDATER_INSTALLED, AnalyzerConstants.RecommendationUpdaterConstants.SupportedUpdaters.VPA); + } else { + LOGGER.error(AnalyzerErrorConstants.RecommendationUpdaterErrors.UPDATER_NOT_INSTALLED, AnalyzerConstants.RecommendationUpdaterConstants.SupportedUpdaters.VPA); + } + return isVpaInstalled; + } +} diff --git a/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java b/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java index f7ae69c9f..c0dbbe783 100644 --- a/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java +++ b/src/main/java/com/autotune/analyzer/utils/AnalyzerConstants.java @@ -666,4 +666,37 @@ private APIVersionConstants() { } } } + + public static final class RecommendationUpdaterConstants { + private RecommendationUpdaterConstants() { + + } + + public static final class SupportedUpdaters { + public static final String VPA = "vpa"; + + private SupportedUpdaters() { + + } + } + + public static final class VPA { + public static final String VPA_PLURAL = "VerticalPodAutoscaler"; + + private VPA() { + + } + } + + public static final class InfoMsgs { + public static final String GENERATING_RECOMMENDATIONS = "Generating recommendations for experiment: {}"; + public static final String GENERATED_RECOMMENDATIONS = "Generated recommendations for experiment: {}"; + public static final String CHECKING_IF_UPDATER_INSTALLED = "Verifying if the updater is installed: {}"; + public static final String FOUND_UPDATER_INSTALLED = "Found updater is installed: {}"; + + private InfoMsgs() { + + } + } + } } diff --git a/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java b/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java index a279ea77a..148494d38 100644 --- a/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java +++ b/src/main/java/com/autotune/analyzer/utils/AnalyzerErrorConstants.java @@ -286,4 +286,14 @@ private KruizeRecommendationError() { } } } + + public static final class RecommendationUpdaterErrors { + private RecommendationUpdaterErrors() { + + } + + public static final String UNSUPPORTED_UPDATER_TYPE = "Updater type %s is not supported."; + public static final String GENERATE_RECOMMNEDATION_FAILED = "Failed to generate recommendations for experiment: {}"; + public static final String UPDATER_NOT_INSTALLED = "Updater is not installed: {}"; + } }