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

fix: Handling all cases for 5xx #148

Merged
merged 8 commits into from
May 28, 2024
Merged
42 changes: 25 additions & 17 deletions error/constants.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
package error

import "google.golang.org/grpc/codes"
import (
"google.golang.org/grpc/codes"
)

// list of errors from helm
// list of error strings from Helm. These are part of the errors we check for presence in Helm's error messages.
const (
YAMLToJSONConversionError = "error converting YAML to JSON"
ClusterUnreachableErrorMsg = "cluster unreachable"
CrdPreconditionErrorMsg = "ensure CRDs are installed first"
CrdPreconditionErrorMsg = "ensure crds are installed first"
ArrayStringMismatchErrorMsg = "got array expected string"
//NamespaceNotFoundErrorMsg = "not found" // todo - add more acurate error message, this is very generic
InvalidValueErrorMsg = "Invalid value"
NotFoundErrorMsg = "not found" //this is a generic type constant, an error could be namespace "ns1" not found or service "ser1" not found.
InvalidValueErrorMsg = "invalid value"
OperationInProgressErrorMsg = "another operation (install/upgrade/rollback) is in progress"
ForbiddenErrorMsg = "forbidden"
)

// list of internal errors
// list of internal errors, these errors are easy for the users to understand
const (
InternalClusterUnreachableErrorMsg = "cluster unreachable"
InternalCrdPreconditionErrorMsg = "ensure CRDs are installed first"
InternalArrayStringMismatchErrorMsg = "got array expected string"
InternalNamespaceNotFoundErrorMsg = "namespace not found"
InternalInvalidValueErrorMsg = "invalid value in manifest"
InternalOperationInProgressErrorMsg = "another operation (install/upgrade/rollback) is in progress"
)

var helmErrorInternalErrorMap = map[string]map[string]codes.Code{
ClusterUnreachableErrorMsg: {InternalClusterUnreachableErrorMsg: codes.DeadlineExceeded},
CrdPreconditionErrorMsg: {InternalCrdPreconditionErrorMsg: codes.FailedPrecondition},
//NamespaceNotFoundErrorMsg: {InternalNamespaceNotFoundErrorMsg: codes.Unknown},
ArrayStringMismatchErrorMsg: {InternalArrayStringMismatchErrorMsg: codes.Unknown},
InvalidValueErrorMsg: {InternalInvalidValueErrorMsg: codes.Unknown},
OperationInProgressErrorMsg: {InternalOperationInProgressErrorMsg: codes.FailedPrecondition},
type errorGrpcCodeTuple struct {
errorMsg string
grpcCode codes.Code
}

var helmErrorInternalErrorMap = map[string]errorGrpcCodeTuple{
ClusterUnreachableErrorMsg: {errorMsg: InternalClusterUnreachableErrorMsg, grpcCode: codes.DeadlineExceeded},
OperationInProgressErrorMsg: {errorMsg: InternalOperationInProgressErrorMsg, grpcCode: codes.FailedPrecondition},
}

var DynamicErrorMapping = map[string]codes.Code{
NotFoundErrorMsg: codes.NotFound,
ForbiddenErrorMsg: codes.PermissionDenied,
InvalidValueErrorMsg: codes.InvalidArgument,
ArrayStringMismatchErrorMsg: codes.InvalidArgument,
CrdPreconditionErrorMsg: codes.FailedPrecondition,
}
28 changes: 25 additions & 3 deletions error/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,35 @@ import (
"strings"
)

// ConvertHelmErrorToInternalError converts known error message from helm to internal error and also maps it with proper grpc code
func ConvertHelmErrorToInternalError(err error) error {
genericError := getInternalErrorForGenericErrorTypes(err)
if genericError != nil {
return genericError
}
var internalError error
for helmErrMsg, internalErr := range helmErrorInternalErrorMap {
if strings.Contains(err.Error(), helmErrMsg) {
for internalErrMsg, internalErrCode := range internalErr {
return status.New(internalErrCode, internalErrMsg).Err()
}
internalError = status.New(internalErr.grpcCode, internalErr.errorMsg).Err()
}
}
return internalError
}

// getInternalErrorForGenericErrorTypes returns all those kinds of errors which are generic in nature and also dynamic, make sure to return all generic and dynamic errors from this func. instead of putting them in helmErrorInternalErrorMap
func getInternalErrorForGenericErrorTypes(err error) error {
/*
for example:-
1. if namespace is not found err is:- namespace "ns1" not found,
2. in case ingress class not found error is of type ingress class: IngressClass.networking.k8s.io "ingress1" not found,
3. when some resource is forbidden then err can be of many formats one of which is:- Unable to continue with install: could not get information about the resource Ingress "prakash-1-prakash-env3-ingress" in namespace "prakash-ns3": ingresses.networking.k8s.io "prakash-1-prakash-env3-ingress" is forbidden...
etc..
*/
for errorMsg, code := range DynamicErrorMapping {
if strings.Contains(strings.ToLower(err.Error()), errorMsg) {
return status.New(code, err.Error()).Err()
}
}

return nil
}
57 changes: 48 additions & 9 deletions pkg/service/HelmAppService.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import (
const (
hibernateReplicaAnnotation = "hibernator.devtron.ai/replicas"
hibernatePatch = `[{"op": "replace", "path": "/spec/replicas", "value":%d}, {"op": "add", "path": "/metadata/annotations", "value": {"%s":"%s"}}]`
chartWorkingDirectory = "/home/devtron/devtroncd/charts/"
ReadmeFileName = "README.md"
REGISTRY_TYPE_ECR = "ecr"
REGISTRYTYPE_GCR = "gcr"
Expand Down Expand Up @@ -138,7 +137,7 @@ func NewHelmAppServiceImpl(logger *zap.SugaredLogger, k8sService K8sService,
clusterRepository: clusterRepository,
converter: converter,
}
err = os.MkdirAll(chartWorkingDirectory, os.ModePerm)
err = os.MkdirAll(helmReleaseConfig.ChartWorkingDirectory, os.ModePerm)
if err != nil {
helmAppServiceImpl.logger.Errorw("err in creating dir", "err", err)
return nil, err
Expand Down Expand Up @@ -285,10 +284,14 @@ func (impl HelmAppServiceImpl) getManifestsForExternalResources(restConfig *rest
func (impl HelmAppServiceImpl) BuildAppDetail(req *client.AppDetailRequest) (*bean.AppDetail, error) {
helmRelease, err := impl.getHelmRelease(req.ClusterConfig, req.Namespace, req.ReleaseName)
if err != nil {
impl.logger.Errorw("Error in getting helm release ", "err", err)
if errors.Is(err, driver.ErrReleaseNotFound) {
return &bean.AppDetail{ReleaseExists: false}, err
}
impl.logger.Errorw("Error in getting helm release ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}
resourceTreeResponse, err := impl.buildResourceTree(req, helmRelease)
Expand Down Expand Up @@ -390,6 +393,10 @@ func (impl *HelmAppServiceImpl) FetchApplicationStatus(req *client.AppDetailRequ
helmRelease, err := impl.getHelmRelease(req.ClusterConfig, req.Namespace, req.ReleaseName)
if err != nil {
impl.logger.Errorw("Error in getting helm release ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return helmAppStatus, err
}
if helmRelease.Info != nil {
Expand All @@ -413,6 +420,10 @@ func (impl HelmAppServiceImpl) GetHelmAppValues(req *client.AppDetailRequest) (*
helmRelease, err := impl.getHelmRelease(req.ClusterConfig, req.Namespace, req.ReleaseName)
if err != nil {
impl.logger.Errorw("Error in getting helm release ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}

Expand Down Expand Up @@ -540,6 +551,10 @@ func (impl HelmAppServiceImpl) GetDeploymentHistory(req *client.AppDetailRequest
helmReleases, err := impl.getHelmReleaseHistory(req.ClusterConfig, req.Namespace, req.ReleaseName, impl.helmReleaseConfig.MaxCountForHelmRelease)
if err != nil {
impl.logger.Errorw("Error in getting helm release history ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}
helmAppDeployments := make([]*client.HelmAppDeploymentDetail, 0, len(helmReleases))
Expand Down Expand Up @@ -579,6 +594,10 @@ func (impl HelmAppServiceImpl) GetDesiredManifest(req *client.ObjectRequest) (*c
helmRelease, err := impl.getHelmRelease(req.ClusterConfig, req.ReleaseNamespace, req.ReleaseName)
if err != nil {
impl.logger.Errorw("Error in getting helm release ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}

Expand Down Expand Up @@ -616,6 +635,10 @@ func (impl HelmAppServiceImpl) UninstallRelease(releaseIdentifier *client.Releas
err = helmClient.UninstallReleaseByName(releaseIdentifier.ReleaseName)
if err != nil {
impl.logger.Errorw("Error in uninstall release ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}

Expand Down Expand Up @@ -699,6 +722,10 @@ func (impl HelmAppServiceImpl) GetDeploymentDetail(request *client.DeploymentDet
helmReleases, err := impl.getHelmReleaseHistory(releaseIdentifier.ClusterConfig, releaseIdentifier.ReleaseNamespace, releaseIdentifier.ReleaseName, impl.helmReleaseConfig.MaxCountForHelmRelease)
if err != nil {
impl.logger.Errorw("Error in getting helm release history ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}

Expand Down Expand Up @@ -781,6 +808,10 @@ func (impl HelmAppServiceImpl) installRelease(ctx context.Context, request *clie
err = helmClientObj.AddOrUpdateChartRepo(chartRepo)
if err != nil {
impl.logger.Errorw("Error in add/update chart repo ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}
chartName = fmt.Sprintf("%s/%s", chartRepoName, request.ChartName)
Expand Down Expand Up @@ -808,6 +839,10 @@ func (impl HelmAppServiceImpl) installRelease(ctx context.Context, request *clie
rel, err := helmClientObj.InstallChart(context.Background(), chartSpec)
if err != nil {
impl.logger.Errorw("Error in install release ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}

Expand Down Expand Up @@ -879,6 +914,10 @@ func (impl HelmAppServiceImpl) GetNotes(ctx context.Context, request *client.Ins
release, err := helmClientObj.GetNotes(chartSpec, HelmTemplateOptions)
if err != nil {
impl.logger.Errorw("Error in fetching Notes ", "err", err)
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return "", err
}
if release == nil {
Expand Down Expand Up @@ -1788,15 +1827,15 @@ func (impl HelmAppServiceImpl) InstallReleaseWithCustomChart(ctx context.Context
return false, err
}

if _, err := os.Stat(chartWorkingDirectory); os.IsNotExist(err) {
err := os.MkdirAll(chartWorkingDirectory, os.ModePerm)
if _, err := os.Stat(impl.helmReleaseConfig.ChartWorkingDirectory); os.IsNotExist(err) {
err := os.MkdirAll(impl.helmReleaseConfig.ChartWorkingDirectory, os.ModePerm)
if err != nil {
impl.logger.Errorw("err in creating dir", "err", err)
return false, err
}
}
dir := impl.GetRandomString()
referenceChartDir := filepath.Join(chartWorkingDirectory, dir)
referenceChartDir := filepath.Join(impl.helmReleaseConfig.ChartWorkingDirectory, dir)
referenceChartDir = fmt.Sprintf("%s.tgz", referenceChartDir)
defer impl.CleanDir(referenceChartDir)
err = ioutil.WriteFile(referenceChartDir, b.Bytes(), os.ModePerm)
Expand Down Expand Up @@ -1850,15 +1889,15 @@ func (impl HelmAppServiceImpl) UpgradeReleaseWithCustomChart(ctx context.Context
return false, err
}

if _, err := os.Stat(chartWorkingDirectory); os.IsNotExist(err) {
err := os.MkdirAll(chartWorkingDirectory, os.ModePerm)
if _, err := os.Stat(impl.helmReleaseConfig.ChartWorkingDirectory); os.IsNotExist(err) {
err := os.MkdirAll(impl.helmReleaseConfig.ChartWorkingDirectory, os.ModePerm)
if err != nil {
impl.logger.Errorw("err in creating dir", "err", err)
return false, err
}
}
dir := impl.GetRandomString()
referenceChartDir := filepath.Join(chartWorkingDirectory, dir)
referenceChartDir := filepath.Join(impl.helmReleaseConfig.ChartWorkingDirectory, dir)
referenceChartDir = fmt.Sprintf("%s.tgz", referenceChartDir)
defer impl.CleanDir(referenceChartDir)
err = ioutil.WriteFile(referenceChartDir, b.Bytes(), os.ModePerm)
Expand Down
6 changes: 6 additions & 0 deletions pkg/service/K8sService.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
k8sUtils "github.com/devtron-labs/common-lib/utils/k8s"
k8sCommonBean "github.com/devtron-labs/common-lib/utils/k8s/commonBean"
"github.com/devtron-labs/kubelink/bean"
error2 "github.com/devtron-labs/kubelink/error"
"go.uber.org/zap"
coreV1 "k8s.io/api/core/v1"
errors2 "k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -42,6 +43,7 @@ type HelmReleaseConfig struct {
ManifestFetchBatchSize int `env:"MANIFEST_FETCH_BATCH_SIZE" envDefault:"2"`
RunHelmInstallInAsyncMode bool `env:"RUN_HELM_INSTALL_IN_ASYNC_MODE" envDefault:"false"`
ParentChildGvkMapping string `env:"PARENT_CHILD_GVK_MAPPING" envDefault:""`
ChartWorkingDirectory string `env:"CHART_WORKING_DIRECTORY" envDefault:"/home/devtron/devtroncd/charts/"`
}

func GetHelmReleaseConfig() (*HelmReleaseConfig, error) {
Expand Down Expand Up @@ -159,6 +161,10 @@ func (impl K8sServiceImpl) GetChildObjects(restConfig *rest.Config, namespace st
if err != nil {
statusError, ok := err.(*errors2.StatusError)
if !ok || statusError.ErrStatus.Reason != metav1.StatusReasonNotFound {
internalErr := error2.ConvertHelmErrorToInternalError(err)
if internalErr != nil {
err = internalErr
}
return nil, err
}
}
Expand Down