Search code examples
goazure-keyvaultazure-sdkautorest

Handling a disabled azure key vault secret using go azure sdk?


I am trying an extend Kubernetes operator written in Go to handle Azure Key Vault Secrets that have been disabled. The operator is using the Azure SDK for Go as well as the Azure autorest library.

I am very new to Go and I'm struggling to populate the ServiceError struct when there is an error

The snippet of code I'm looking at is this: (see in context)

    response, err := sm.client.GetSecret(ctx, fmt.Sprintf(azureVaultURLFmt, sm.azureVaultName), secretID, "")
    if err != nil {
        if e, ok := err.(autorest.DetailedError); ok && e.StatusCode.(int) == 404 {
            return []byte{}, nil
        }
        return []byte{}, err
    }

At present it handles missing secrets as noted by the 404 http status code. However I want to handle the error situation when you read a disabled secret which surfaces as a 403, and with extra error information in the ServiceError. However, no matter what I try I haven't managed to figure out how to get at the ServiceError struct.

I tried doing:

        if e, ok := err.(azure.ServiceError); ok {
            log.V(0).Info("Go an azure service error")
            return []byte{}, nil
        }

But the code is never called.

Using the original code, I attached the delve debugger with vscode, and I can see that the e variable had an extra hidden data variable that appeared to contain both the ServiceError and DetailedError struct, but I just can't get at the ServiceError.

The DetailedError struct has a ServiceError byte array on it. Am I supposed to manually call UnmarshallJSON or something like that? I think it's supposed to magically do it for me somehow, but I'm just not sure how.

What am I missing?


Solution

  • So I managed to find a workaround by using a debugger and inspecting the err object. It isn't particularly pretty, but it does work.

        response, err := sm.client.GetSecret(ctx, fmt.Sprintf(azureVaultURLFmt, sm.azureVaultName), secretID, "")
        if err != nil {
            // We can ignore some errors
            if de, ok := err.(autorest.DetailedError); ok {
                if re, ok := de.Original.(*azure.RequestError); ok {
                    if re.ServiceError.Code == "SecretNotFound" {
                        // Secret not existing is fine, as that means we will create a new secret
                        return []byte{}, nil
                    } else if code, ok := re.ServiceError.InnerError["code"].(string); ok && code == "SecretDisabled" {
                        // Disabled secret also fine, as it means we will create a new version of the secret
                        return []byte{}, nil
                    }
                }
            }
            return []byte{}, err
        }