Reload ZFSSA certificate and reset https client config when failed to verify certificate

This commit is contained in:
Helen Zhang 2024-05-20 14:20:42 -07:00
parent 0c8642ed58
commit d4631059de
2 changed files with 154 additions and 122 deletions

View File

@ -6,11 +6,11 @@
package service package service
import ( import (
"github.com/oracle/zfssa-csi-driver/pkg/utils"
"github.com/oracle/zfssa-csi-driver/pkg/zfssarest"
"errors" "errors"
"fmt" "fmt"
"github.com/container-storage-interface/spec/lib/go/csi" "github.com/container-storage-interface/spec/lib/go/csi"
"github.com/oracle/zfssa-csi-driver/pkg/utils"
"github.com/oracle/zfssa-csi-driver/pkg/zfssarest"
"golang.org/x/net/context" "golang.org/x/net/context"
"google.golang.org/grpc" "google.golang.org/grpc"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -28,9 +28,9 @@ import (
const ( const (
// Default Log Level // Default Log Level
DefaultLogLevel = "3" DefaultLogLevel = "3"
DefaultCertPath = "/mnt/certs/zfssa.crt" DefaultCertPath = "/mnt/certs/zfssa.crt"
DefaultCredPath = "/mnt/zfssa/zfssa.yaml" DefaultCredPath = "/mnt/zfssa/zfssa.yaml"
DefaultConfigPath = "/mnt/config/config.yaml" DefaultConfigPath = "/mnt/config/config.yaml"
) )
@ -49,15 +49,15 @@ type ZFSSADriver struct {
} }
type config struct { type config struct {
Appliance string Appliance string
User string User string
endpoint string endpoint string
HostIp string HostIp string
NodeName string NodeName string
PodIp string PodIp string
Secure bool Secure bool
logLevel string logLevel string
Certificate []byte Certificate []byte
CertLocation string CertLocation string
CredLocation string CredLocation string
} }
@ -73,8 +73,8 @@ type accessType int
// NonBlocking server // NonBlocking server
type nonBlockingGRPCServer struct { type nonBlockingGRPCServer struct {
wg sync.WaitGroup wg sync.WaitGroup
server *grpc.Server server *grpc.Server
} }
const ( const (
@ -86,7 +86,7 @@ const (
Tib int64 = Gib * 1024 Tib int64 = Gib * 1024
Tib100 int64 = Tib * 100 Tib100 int64 = Tib * 100
DefaultVolumeSizeBytes int64 = 50 * Gib DefaultVolumeSizeBytes int64 = 50 * Gib
mountAccess accessType = iota mountAccess accessType = iota
blockAccess blockAccess
@ -94,7 +94,7 @@ const (
const ( const (
UsernamePattern string = `^[a-zA-Z][a-zA-Z0-9_\-\.]*$` UsernamePattern string = `^[a-zA-Z][a-zA-Z0-9_\-\.]*$`
UsernameLength int = 255 UsernameLength int = 255
) )
type ZfssaBlockVolume struct { type ZfssaBlockVolume struct {
@ -122,7 +122,7 @@ func NewZFSSADriver(driverName, version string) (*ZFSSADriver, error) {
utils.InitLogs(zd.config.logLevel, zd.name, version, zd.config.NodeName) utils.InitLogs(zd.config.logLevel, zd.name, version, zd.config.NodeName)
err = zfssarest.InitREST(zd.config.Appliance, zd.config.Certificate, zd.config.Secure) err = zfssarest.InitREST(zd.config.Appliance, zd.config.CertLocation, zd.config.Secure)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -175,8 +175,8 @@ func getConfig(zd *ZFSSADriver) error {
return errors.New(fmt.Sprintf("Cannot get ZFSSA username: %s", err)) return errors.New(fmt.Sprintf("Cannot get ZFSSA username: %s", err))
} }
// Get ZFSSA_TARGET from the mounted config file if available // Get ZFSSA_TARGET from the mounted config file if available
zfssaHost, _ := utils.GetValueFromYAML(DefaultConfigPath,"ZFSSA_TARGET") zfssaHost, _ := utils.GetValueFromYAML(DefaultConfigPath, "ZFSSA_TARGET")
appliance := getEnvFallback("ZFSSA_TARGET", zfssaHost) appliance := getEnvFallback("ZFSSA_TARGET", zfssaHost)
zd.config.Appliance = strings.TrimSpace(appliance) zd.config.Appliance = strings.TrimSpace(appliance)
if zd.config.Appliance == "not-set" { if zd.config.Appliance == "not-set" {
@ -189,7 +189,7 @@ func getConfig(zd *ZFSSADriver) error {
} }
zd.config.endpoint = getEnvFallback("CSI_ENDPOINT", "") zd.config.endpoint = getEnvFallback("CSI_ENDPOINT", "")
if zd.config.endpoint == "" { if zd.config.endpoint == "" {
return errors.New("endpoint is required") return errors.New("endpoint is required")
} else { } else {
if !strings.HasPrefix(zd.config.endpoint, "unix://") { if !strings.HasPrefix(zd.config.endpoint, "unix://") {
@ -204,8 +204,10 @@ func getConfig(zd *ZFSSADriver) error {
} }
switch strings.ToLower(strings.TrimSpace(getEnvFallback("ZFSSA_INSECURE", "False"))) { switch strings.ToLower(strings.TrimSpace(getEnvFallback("ZFSSA_INSECURE", "False"))) {
case "true": zd.config.Secure = false case "true":
case "false": zd.config.Secure = true zd.config.Secure = false
case "false":
zd.config.Secure = true
default: default:
return errors.New("ZFSSA_INSECURE value is invalid") return errors.New("ZFSSA_INSECURE value is invalid")
} }
@ -219,6 +221,7 @@ func getConfig(zd *ZFSSADriver) error {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return errors.New("certificate does not exits") return errors.New("certificate does not exits")
} }
zd.config.CertLocation = certfile
zd.config.Certificate, err = ioutil.ReadFile(certfile) zd.config.Certificate, err = ioutil.ReadFile(certfile)
if err != nil { if err != nil {
return errors.New("failed to read certificate") return errors.New("failed to read certificate")
@ -238,7 +241,7 @@ func getConfig(zd *ZFSSADriver) error {
// Starts the CSI driver. This includes registering the different servers (Identity, Controller and Node) with // Starts the CSI driver. This includes registering the different servers (Identity, Controller and Node) with
// the CSI framework and starting listening on the UNIX socket. // the CSI framework and starting listening on the UNIX socket.
var sigList = []os.Signal { var sigList = []os.Signal{
syscall.SIGTERM, syscall.SIGTERM,
syscall.SIGHUP, syscall.SIGHUP,
syscall.SIGINT, syscall.SIGINT,
@ -344,7 +347,7 @@ func (s *nonBlockingGRPCServer) serve(endpoint string,
return return
} }
opts := []grpc.ServerOption{ grpc.UnaryInterceptor(interceptorGRPC) } opts := []grpc.ServerOption{grpc.UnaryInterceptor(interceptorGRPC)}
server := grpc.NewServer(opts...) server := grpc.NewServer(opts...)
s.server = server s.server = server

View File

@ -15,6 +15,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings"
"sync" "sync"
"time" "time"
@ -25,35 +26,35 @@ import (
// Use RESTapi v2 as it returns scriptable and consistent values // Use RESTapi v2 as it returns scriptable and consistent values
const ( const (
zAppliance string = "https://%s:215" zAppliance string = "https://%s:215"
zServices = zAppliance + "/api/access/v2" zServices = zAppliance + "/api/access/v2"
zStorage = zAppliance + "/api/storage/v2" zStorage = zAppliance + "/api/storage/v2"
zSan = zAppliance + "/api/san/v2" zSan = zAppliance + "/api/san/v2"
zPools = zStorage + "/pools" zPools = zStorage + "/pools"
zPool = zPools + "/%s" zPool = zPools + "/%s"
zAllProjects = zStorage + "/projects" zAllProjects = zStorage + "/projects"
zProjects = zPool + "/projects" zProjects = zPool + "/projects"
zProject = zProjects + "/%s" zProject = zProjects + "/%s"
zAllFilesystems = zStorage + "/filesystems" zAllFilesystems = zStorage + "/filesystems"
zFilesystems = zProject + "/filesystems" zFilesystems = zProject + "/filesystems"
zFilesystem = zFilesystems + "/%s" zFilesystem = zFilesystems + "/%s"
zAllLUNs = zStorage + "/luns" zAllLUNs = zStorage + "/luns"
zLUNs = zProject + "/luns" zLUNs = zProject + "/luns"
zLUN = zLUNs + "/%s" zLUN = zLUNs + "/%s"
zAllSnapshots = zStorage + "/snapshots" zAllSnapshots = zStorage + "/snapshots"
zSnapshots = zProject + "/snapshots" zSnapshots = zProject + "/snapshots"
zSnapshot = zSnapshots + "/%s" zSnapshot = zSnapshots + "/%s"
zFilesystemSnapshots = zFilesystem + "/snapshots" zFilesystemSnapshots = zFilesystem + "/snapshots"
zFilesystemSnapshot = zFilesystemSnapshots + "/%s" zFilesystemSnapshot = zFilesystemSnapshots + "/%s"
zCloneFilesystemSnapshot = zFilesystemSnapshot + "/clone" zCloneFilesystemSnapshot = zFilesystemSnapshot + "/clone"
zLUNSnapshots = zLUN + "/snapshots" zLUNSnapshots = zLUN + "/snapshots"
zLUNSnapshot = zLUNSnapshots + "/%s" zLUNSnapshot = zLUNSnapshots + "/%s"
zFilesystemDependents = zFilesystemSnapshot + "/dependents" zFilesystemDependents = zFilesystemSnapshot + "/dependents"
zLUNDependents = zLUNSnapshot + "/dependents" zLUNDependents = zLUNSnapshot + "/dependents"
zTargetGroups = zSan + "/%s/target-groups" zTargetGroups = zSan + "/%s/target-groups"
zTargetGroup = zTargetGroups + "/%s" zTargetGroup = zTargetGroups + "/%s"
zProperties = zAppliance + "/api/storage/v2/schema" zProperties = zAppliance + "/api/storage/v2/schema"
zProperty = zProperties + "/%s" zProperty = zProperties + "/%s"
) )
const ( const (
@ -63,68 +64,88 @@ const (
) )
type Token struct { type Token struct {
Name string Name string
cv *sync.Cond cv *sync.Cond
mtx sync.Mutex mtx sync.Mutex
user string user string
password string password string
state int state int
xAuthSession string xAuthSession string
xAuthName string xAuthName string
} }
type tokenList struct { type tokenList struct {
mtx sync.Mutex mtx sync.Mutex
list map[string]*Token list map[string]*Token
} }
type faultInfo struct { type faultInfo struct {
Message string `json:"message"` Message string `json:"message"`
Code int `json:"code"` Code int `json:"code"`
Name string `json:"Name"` Name string `json:"Name"`
} }
type faultResponse struct { type faultResponse struct {
Fault faultInfo `json:"fault"` Fault faultInfo `json:"fault"`
} }
var httpTransport = http.Transport{TLSClientConfig: &tls.Config{}} var httpTransport = http.Transport{TLSClientConfig: &tls.Config{}}
var httpClient = &http.Client{Transport: &httpTransport} var httpClient = &http.Client{Transport: &httpTransport}
var zServicesURL string var zServicesURL string
var zName string var zName string
var tokens tokenList var tokens tokenList
var zfssaCertLocation string
// Initializes the ZFSSA REST API interface // Initializes the ZFSSA REST API interface
// func InitREST(name string, certLocation string, secure bool) error {
func InitREST(name string, certs []byte, secure bool) error {
if secure {
// set TLSv1.2 for the minimum version of supporting TLS
httpTransport.TLSClientConfig.MinVersion = tls.VersionTLS12
// Get the SystemCertPool, continue with an empty pool on error
httpTransport.TLSClientConfig.RootCAs, _ = x509.SystemCertPool()
if httpTransport.TLSClientConfig.RootCAs == nil {
httpTransport.TLSClientConfig.RootCAs = x509.NewCertPool()
}
if ok := httpTransport.TLSClientConfig.RootCAs.AppendCertsFromPEM(certs); !ok {
return errors.New("failed to append the certificate")
}
}
httpTransport.TLSClientConfig.InsecureSkipVerify = !secure httpTransport.TLSClientConfig.InsecureSkipVerify = !secure
httpTransport.MaxConnsPerHost = 16 httpTransport.MaxConnsPerHost = 16
httpTransport.MaxIdleConnsPerHost = 16 httpTransport.MaxIdleConnsPerHost = 16
httpTransport.IdleConnTimeout = 30 * time.Second httpTransport.IdleConnTimeout = 30 * time.Second
tokens.list = make(map[string]*Token) zfssaCertLocation = certLocation
err := resetHttpTlsClient(nil)
if err != nil {
return err
}
zServicesURL = fmt.Sprintf(zServices, name) zServicesURL = fmt.Sprintf(zServices, name)
zName = name zName = name
return nil return nil
} }
func resetHttpTlsClient(ctx context.Context) error {
if httpTransport.TLSClientConfig.InsecureSkipVerify {
utils.GetLogREST(ctx, 2).Println("resetHttpTransport skipped")
return nil
}
// set TLSv1.2 for the minimum version of supporting TLS
httpTransport.TLSClientConfig.MinVersion = tls.VersionTLS12
// Get the SystemCertPool, continue with an empty pool on error
utils.GetLogREST(ctx, 2).Println("loading RootCAs")
httpTransport.TLSClientConfig.RootCAs, _ = x509.SystemCertPool()
if httpTransport.TLSClientConfig.RootCAs == nil {
httpTransport.TLSClientConfig.RootCAs = x509.NewCertPool()
}
certs, err := ioutil.ReadFile(zfssaCertLocation)
if err != nil {
return errors.New("failed to read ZFSSA certificate")
}
if ok := httpTransport.TLSClientConfig.RootCAs.AppendCertsFromPEM(certs); !ok {
return errors.New("failed to append the certificate")
}
tokens.list = make(map[string]*Token)
utils.GetLogREST(ctx, 5).Println("resetHttpTransport done")
return nil
}
// Looks up a token context based on the user name passed in. If one doesn't exist // Looks up a token context based on the user name passed in. If one doesn't exist
// yet, it is created. // yet, it is created.
func LookUpToken(ctx context.Context, user, password string) *Token { func LookUpToken(ctx context.Context, user, password string) *Token {
@ -160,14 +181,14 @@ func LookUpToken(ctx context.Context, user, password string) *Token {
// //
// The possible return values are: // The possible return values are:
// //
// Code Message X-Auth-Session // Code Message X-Auth-Session
// //
// nil Valid // nil Valid
// codes.Internal "Failure getting token" "" // codes.Internal "Failure getting token" ""
// codes.Internal "Failure creating token" "" // codes.Internal "Failure creating token" ""
// //
// In case of failure, the message logged will provide more information // In case of failure, the message logged will provide more information
// as to where the problem occurred. // as to where the problem occurred.
func getToken(ctx context.Context, token *Token, previous *string) (string, error) { func getToken(ctx context.Context, token *Token, previous *string) (string, error) {
token.mtx.Lock() token.mtx.Lock()
@ -223,7 +244,7 @@ func createZfssaSession(ctx context.Context, token *Token) (string, string, erro
httpReq, err := http.NewRequest("POST", zServicesURL, bytes.NewBuffer(nil)) httpReq, err := http.NewRequest("POST", zServicesURL, bytes.NewBuffer(nil))
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("Could not build a request to create a token", utils.GetLogREST(ctx, 2).Println("Could not build a request to create a token",
"method", "POST", "url", zServicesURL, "error", err.Error()) "method", "POST", "url", zServicesURL, "error", err.Error())
return "", "", grpcStatus.Error(codes.Internal, "Failure creating token") return "", "", grpcStatus.Error(codes.Internal, "Failure creating token")
} }
@ -233,15 +254,18 @@ func createZfssaSession(ctx context.Context, token *Token) (string, string, erro
httpRsp, err := httpClient.Do(httpReq) httpRsp, err := httpClient.Do(httpReq)
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("Token creation failed in Do", utils.GetLogREST(ctx, 2).Println("Token creation failed in Do",
"url", zServicesURL, "error", err.Error()) "url", zServicesURL, "error", err.Error())
if strings.Contains(err.Error(), "failed to verify certificate") {
resetHttpTlsClient(ctx)
}
return "", "", grpcStatus.Error(codes.Internal, "Failure creating token") return "", "", grpcStatus.Error(codes.Internal, "Failure creating token")
} }
defer httpRsp.Body.Close() defer httpRsp.Body.Close()
if httpRsp.StatusCode != http.StatusCreated { if httpRsp.StatusCode != http.StatusCreated {
utils.GetLogREST(ctx,2).Println("Token creation failed in ZFSSA", utils.GetLogREST(ctx, 2).Println("Token creation failed in ZFSSA",
"url", zServicesURL, "StatusCode", httpRsp.StatusCode) "url", zServicesURL, "StatusCode", httpRsp.StatusCode)
return "", "", grpcStatus.Error(codes.Internal, "Failure creating token") return "", "", grpcStatus.Error(codes.Internal, "Failure creating token")
} }
@ -264,7 +288,7 @@ func MakeRequest(ctx context.Context, token *Token, method, url string, reqbody
func makeRequest(ctx context.Context, token *Token, method, url string, reqbody interface{}, status int, func makeRequest(ctx context.Context, token *Token, method, url string, reqbody interface{}, status int,
rspbody interface{}) (interface{}, int, error) { rspbody interface{}) (interface{}, int, error) {
utils.GetLogREST(ctx,5).Println("MakeRequest to ZFSSA", utils.GetLogREST(ctx, 5).Println("MakeRequest to ZFSSA",
"method", method, "url", url, "body", reqbody) "method", method, "url", url, "body", reqbody)
xAuthSession, err := getToken(ctx, token, nil) xAuthSession, err := getToken(ctx, token, nil)
@ -274,14 +298,14 @@ func makeRequest(ctx context.Context, token *Token, method, url string, reqbody
reqjson, err := json.Marshal(reqbody) reqjson, err := json.Marshal(reqbody)
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("json.Marshal call failed", utils.GetLogREST(ctx, 2).Println("json.Marshal call failed",
"method", method, "url", url, "body", reqbody, "error", err.Error()) "method", method, "url", url, "body", reqbody, "error", err.Error())
return nil, 0, grpcStatus.Error(codes.Unknown, "json.Marshal call failed") return nil, 0, grpcStatus.Error(codes.Unknown, "json.Marshal call failed")
} }
reqhttp, err := http.NewRequest(method, url, bytes.NewBuffer(reqjson)) reqhttp, err := http.NewRequest(method, url, bytes.NewBuffer(reqjson))
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("http.NewRequest call failed", utils.GetLogREST(ctx, 2).Println("http.NewRequest call failed",
"method", method, "url", url, "body", reqbody, "error", err.Error()) "method", method, "url", url, "body", reqbody, "error", err.Error())
return nil, 0, grpcStatus.Error(codes.Unknown, "http.NewRequest call failed") return nil, 0, grpcStatus.Error(codes.Unknown, "http.NewRequest call failed")
} }
@ -292,8 +316,15 @@ func makeRequest(ctx context.Context, token *Token, method, url string, reqbody
rsphttp, err := httpClient.Do(reqhttp) rsphttp, err := httpClient.Do(reqhttp)
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("client.do call failed", utils.GetLogREST(ctx, 2).Println("client.do call failed",
"method", method, "url", url, "error", err.Error()) "method", method, "url", url, "error", err.Error())
if strings.Contains(err.Error(), "failed to verify certificate") {
utils.GetLogREST(ctx, 2).Println("mark token as invalid")
token.state = zfssaTokenInvalid
resetHttpTlsClient(ctx)
return nil, http.StatusUnauthorized, err
}
return nil, 0, grpcStatus.Error(codes.Unknown, "client.do call failed") return nil, 0, grpcStatus.Error(codes.Unknown, "client.do call failed")
} }
@ -306,23 +337,23 @@ func makeRequest(ctx context.Context, token *Token, method, url string, reqbody
// read json http response // read json http response
rspjson, err := ioutil.ReadAll(rsphttp.Body) rspjson, err := ioutil.ReadAll(rsphttp.Body)
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("ioutil.ReadAll call failed", utils.GetLogREST(ctx, 2).Println("ioutil.ReadAll call failed",
"method", method, "url", url, "code", rsphttp.StatusCode, "method", method, "url", url, "code", rsphttp.StatusCode,
"status", rsphttp.Status, "error", err.Error()) "status", rsphttp.Status, "error", err.Error())
return nil, rsphttp.StatusCode, grpcStatus.Error(codes.Unknown,"ioutil.ReadAll call failed") return nil, rsphttp.StatusCode, grpcStatus.Error(codes.Unknown, "ioutil.ReadAll call failed")
} }
if rsphttp.StatusCode == status { if rsphttp.StatusCode == status {
if rspbody != nil { if rspbody != nil {
err = json.Unmarshal(rspjson, rspbody) err = json.Unmarshal(rspjson, rspbody)
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("json.Unmarshal call failed", utils.GetLogREST(ctx, 2).Println("json.Unmarshal call failed",
"\nmethod", method, "\nurl", url, "\ncode", rsphttp.StatusCode, "\nmethod", method, "\nurl", url, "\ncode", rsphttp.StatusCode,
"\nstatus", rsphttp.Status, "\nbody", rspjson, "\nerror", err) "\nstatus", rsphttp.Status, "\nbody", rspjson, "\nerror", err)
return nil, rsphttp.StatusCode, grpcStatus.Error(codes.Unknown, "json.Unmarshal call failed") return nil, rsphttp.StatusCode, grpcStatus.Error(codes.Unknown, "json.Unmarshal call failed")
} }
} }
utils.GetLogREST(ctx,5).Println("Successful response from ZFSSA", utils.GetLogREST(ctx, 5).Println("Successful response from ZFSSA",
"method", method, "url", url, "result", rsphttp.StatusCode) "method", method, "url", url, "result", rsphttp.StatusCode)
return rspbody, rsphttp.StatusCode, nil return rspbody, rsphttp.StatusCode, nil
} }
@ -335,7 +366,7 @@ func makeRequest(ctx context.Context, token *Token, method, url string, reqbody
} }
// status code was not what the user expected, attempt to unpack // status code was not what the user expected, attempt to unpack
utils.GetLogREST(ctx,2).Println("MakeRequest to ZFSSA resulted in an unexpected status", utils.GetLogREST(ctx, 2).Println("MakeRequest to ZFSSA resulted in an unexpected status",
"method", method, "url", url, "expected", status, "code", rsphttp.StatusCode, "method", method, "url", url, "expected", status, "code", rsphttp.StatusCode,
"status", rsphttp.Status) "status", rsphttp.Status)
@ -343,7 +374,7 @@ func makeRequest(ctx context.Context, token *Token, method, url string, reqbody
err = json.Unmarshal(rspjson, failure) err = json.Unmarshal(rspjson, failure)
var responseString string var responseString string
if err != nil { if err != nil {
utils.GetLogREST(ctx,2).Println("Failure from ZFSSA could not be un-marshalled", utils.GetLogREST(ctx, 2).Println("Failure from ZFSSA could not be un-marshalled",
"method", method, "url", url, "code", rsphttp.StatusCode, "method", method, "url", url, "code", rsphttp.StatusCode,
"status", rsphttp.Status, "body", rspjson) "status", rsphttp.Status, "body", rspjson)
responseString = string(rspjson) responseString = string(rspjson)
@ -353,7 +384,7 @@ func makeRequest(ctx context.Context, token *Token, method, url string, reqbody
switch rsphttp.StatusCode { switch rsphttp.StatusCode {
case http.StatusNotFound: case http.StatusNotFound:
err = grpcStatus.Errorf(codes.NotFound, "Resource not found on target appliance: %s", responseString) err = grpcStatus.Errorf(codes.NotFound, "Resource not found on target appliance: %s", responseString)
default: default:
err = grpcStatus.Errorf(codes.Unknown, "Unknown Error Occurred on target appliance: %s", responseString) err = grpcStatus.Errorf(codes.Unknown, "Unknown Error Occurred on target appliance: %s", responseString)
} }
@ -366,9 +397,9 @@ type services struct {
} }
type Service struct { type Service struct {
Version string `json:"version"` Version string `json:"version"`
Name string `json:"name"` Name string `json:"name"`
URI string `json:"uri"` URI string `json:"uri"`
} }
func GetServices(ctx context.Context, token *Token) (*[]Service, error) { func GetServices(ctx context.Context, token *Token) (*[]Service, error) {
@ -386,22 +417,20 @@ func GetServices(ctx context.Context, token *Token) (*[]Service, error) {
// Unmarshalling of a "List" structure. This structure is the ZFSSA response to // Unmarshalling of a "List" structure. This structure is the ZFSSA response to
// the http request: // the http request:
// //
// GET /api/access/v1 HTTP/1.1 // GET /api/access/v1 HTTP/1.1
// Host: zfs-storage.example.com // Host: zfs-storage.example.com
// X-Auth-User: admin // X-Auth-User: admin
// X-Auth-Key: password // X-Auth-Key: password
//
func (l *services) UnmarshalJSON(b []byte) error { func (l *services) UnmarshalJSON(b []byte) error {
return zfssaUnmarshalList(b, &l.List) return zfssaUnmarshalList(b, &l.List)
} }
// Unmarshalling of a List sent by the ZFSSA // Unmarshalling of a List sent by the ZFSSA
//
func zfssaUnmarshalList(b []byte, l interface{}) error { func zfssaUnmarshalList(b []byte, l interface{}) error {
// 'b' starts and ends like this: // 'b' starts and ends like this:
// {List:[{...},...,{...}]} // {List:[{...},...,{...}]}
b = b[0:len(b) - 1] b = b[0 : len(b)-1]
for i := 1; i < len(b); i++ { for i := 1; i < len(b); i++ {
if b[i] == '[' { if b[i] == '[' {
b = b[i:] b = b[i:]