Skip to content

Commit

Permalink
Wrap AppLens resp in required envelope (#2923)
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudfit-nick authored Jun 5, 2023
1 parent 5f9e0bd commit 151eca2
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 11 deletions.
17 changes: 15 additions & 2 deletions pkg/frontend/adminactions/azureactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package adminactions

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -131,9 +132,21 @@ func (a *azureActions) VMResize(ctx context.Context, vmName string, size string)
}

func (a *azureActions) AppLensGetDetector(ctx context.Context, detectorId string) ([]byte, error) {
return a.appLens.GetDetector(ctx, &applens.GetDetectorOptions{ResourceID: a.oc.ID, DetectorID: detectorId})
detector, err := a.appLens.GetDetector(ctx, &applens.GetDetectorOptions{ResourceID: a.oc.ID, DetectorID: detectorId, Location: a.oc.Location})

if err != nil {
return nil, err
}

return json.Marshal(detector)
}

func (a *azureActions) AppLensListDetectors(ctx context.Context) ([]byte, error) {
return a.appLens.ListDetectors(ctx, &applens.ListDetectorsOptions{ResourceID: a.oc.ID})
detectors, err := a.appLens.ListDetectors(ctx, &applens.ListDetectorsOptions{ResourceID: a.oc.ID, Location: a.oc.Location})

if err != nil {
return nil, err
}

return json.Marshal(detectors)
}
4 changes: 2 additions & 2 deletions pkg/util/azureclient/applens/applens.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (

// AppLensClient is a minimal interface for azure AppLensClient
type AppLensClient interface {
GetDetector(ctx context.Context, o *GetDetectorOptions) ([]byte, error)
ListDetectors(ctx context.Context, o *ListDetectorsOptions) ([]byte, error)
GetDetector(ctx context.Context, o *GetDetectorOptions) (*ResponseMessageEnvelope, error)
ListDetectors(ctx context.Context, o *ListDetectorsOptions) (*ResponseMessageCollectionEnvelope, error)
}

type appLensClient struct {
Expand Down
89 changes: 85 additions & 4 deletions pkg/util/azureclient/applens/applens_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ package applens
import (
"context"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"net/http"
"path"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
Expand All @@ -25,6 +27,18 @@ type Client struct {
pipeline runtime.Pipeline
}

type ResponseMessageEnvelope struct {
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Location string `json:"location,omitempty"`
Properties interface{} `json:"properties,omitempty"`
}

type ResponseMessageCollectionEnvelope struct {
Value []ResponseMessageEnvelope `json:"value,omitempty"`
}

// Endpoint used to create the client.
func (c *Client) Endpoint() string {
return c.endpoint
Expand Down Expand Up @@ -78,7 +92,7 @@ func newPipeline(authPolicy []policy.Policy, options *ClientOptions, issuerUrlTe
// o - Options for Read operation.
func (c *Client) ListDetectors(
ctx context.Context,
o *ListDetectorsOptions) ([]byte, error) {
o *ListDetectorsOptions) (*ResponseMessageCollectionEnvelope, error) {
if o == nil {
o = &ListDetectorsOptions{}
}
Expand All @@ -93,15 +107,21 @@ func (c *Client) ListDetectors(

defer azResponse.Body.Close()

return io.ReadAll(azResponse.Body)
bodyJson, err := io.ReadAll(azResponse.Body)

if err != nil {
return nil, err
}

return newResponseMessageCollectionEnvelope(bodyJson, o.ResourceID, o.Location)
}

// GetDetector obtains detector information from AppLens.
// ctx - The context for the request.
// o - Options for Read operation.
func (c *Client) GetDetector(
ctx context.Context,
o *GetDetectorOptions) ([]byte, error) {
o *GetDetectorOptions) (*ResponseMessageEnvelope, error) {
if o == nil {
o = &GetDetectorOptions{}
}
Expand All @@ -116,7 +136,13 @@ func (c *Client) GetDetector(

defer azResponse.Body.Close()

return io.ReadAll(azResponse.Body)
detectorJson, err := io.ReadAll(azResponse.Body)

if err != nil {
return nil, err
}

return newResponseMessageEnvelope(o.ResourceID, o.DetectorID, o.Location, detectorJson)
}

func (c *Client) sendPostRequest(
Expand Down Expand Up @@ -166,3 +192,58 @@ func (c *Client) executeAndEnsureSuccessResponse(request *policy.Request) (*http

return nil, newAppLensError(response)
}

func newResponseMessageCollectionEnvelope(valueJson []byte, resourceID, location string) (*ResponseMessageCollectionEnvelope, error) {
var results []interface{}
err := json.Unmarshal(valueJson, &results)

if err != nil {
return nil, err
}

listResult := ResponseMessageCollectionEnvelope{}
for _, v := range results {
if id := getDetectorID(v); len(id) > 0 {
detector := ResponseMessageEnvelope{
Id: path.Join(resourceID, "detectors", id),
Name: id,
Location: location,
Type: "Microsoft.RedHatOpenShift/openShiftClusters/detectors",
Properties: v,
}
listResult.Value = append(listResult.Value, detector)
}
}

return &listResult, nil
}

func getDetectorID(detector interface{}) string {
if propertyMap, ok := detector.(map[string]interface{}); ok {
if metadataMap, ok := propertyMap["metadata"].(map[string]interface{}); ok {
if idObj, ok := metadataMap["id"]; ok {
if id, ok := idObj.(string); ok {
return id
}
}
}
}
return ""
}

func newResponseMessageEnvelope(resourceID, name, location string, propertiesJson []byte) (*ResponseMessageEnvelope, error) {
var converted interface{}
err := json.Unmarshal(propertiesJson, &converted)

if err != nil {
return nil, err
}

return &ResponseMessageEnvelope{
Id: path.Join(resourceID, "detectors", name),
Name: name,
Type: "Microsoft.RedHatOpenShift/openShiftClusters/detectors",
Location: location,
Properties: converted,
}, nil
}
168 changes: 165 additions & 3 deletions pkg/util/azureclient/applens/applens_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,20 +184,22 @@ func TestGetDetector(t *testing.T) {
}
}

func TestListDetectors(t *testing.T) {
func TestListDetectorsDirect(t *testing.T) {
srv, close := testhttp.NewTLSServer()
defer close()
srv.SetResponse(
testhttp.WithStatusCode(200))
srv.SetResponse(testhttp.WithStatusCode(200))
verifier := pipelineVerifier{}
pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}

testResourceId := "testResourceId"
testLocation := "eastus"
_, err := client.sendPostRequest(context.Background(),
&ListDetectorsOptions{
ResourceID: testResourceId,
Location: testLocation,
}, nil)

if err != nil {
t.Fatal(err)
}
Expand All @@ -223,6 +225,166 @@ func TestListDetectors(t *testing.T) {
}
}

func TestListDetectors(t *testing.T) {
srv, close := testhttp.NewTLSServer()
defer close()
testResourceId := "testResourceId"
testLocation := "eastus"
testDetectorName := "aroauthhealth"
testBody := `[{
"appFilter": null,
"dataProvidersMetadata": null,
"dataset": [],
"metadata": {
"analysisType": "arooperatorinsights,aroclusterinsights",
"analysisTypes": [
"arooperatorinsights",
"aroclusterinsights"
],
"author": "",
"category": "Operator Health",
"description": "Reports if the ARO Auth Operator becomes unhealthy.",
"id": "aroauthhealth",
"name": "Authentication Operator",
"score": 0,
"supportTopicList": [],
"type": "Detector",
"typeId": "6820fea2-a74f-4059-b7ba-688cc943d2d8"
},
"status": {
"message": null,
"statusId": 4
},
"suggestedUtterances": null
}]`

srv.SetResponse(testhttp.WithBody([]byte(testBody)))
verifier := pipelineVerifier{}
pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}

detectors, err := client.ListDetectors(context.Background(),
&ListDetectorsOptions{
ResourceID: testResourceId,
Location: testLocation,
})

if err != nil {
t.Fatal(err)
}

if verifier.requests[0].method != http.MethodPost {
t.Errorf("Expected %v, but got %v", http.MethodPost, verifier.requests[0].method)
}

if verifier.requests[0].headers.Get(headerXmsDate) == "" {
t.Errorf("Expected %v, but got %v", "", verifier.requests[0].headers.Get(headerXmsDate))
}

if verifier.requests[0].headers.Get(headerXmsClientRequestId) == "" {
t.Errorf("Expected uuid in %v header field, but got empty string", headerXmsClientRequestId)
}

if verifier.requests[0].headers.Get(headerXmsRequestId) == "" {
t.Errorf("Expected uuid in %v header field, but got empty string", headerXmsRequestId)
}

if verifier.requests[0].headers.Get(headerXmsPathQuery) != fmt.Sprintf("%s/detectors", testResourceId) {
t.Errorf("Expected %v in %v header field, but got %v", fmt.Sprintf("%s/detectors", testResourceId), headerXmsPathQuery, verifier.requests[0].headers.Get(headerXmsPathQuery))
}

if len(detectors.Value) != 1 {
t.Error("Expected count of detectors equal 1")
}

if detectors.Value[0].Id != fmt.Sprintf("%s/detectors/aroauthhealth", testResourceId) {
t.Error("Expected detector Id does not match")
}

if detectors.Value[0].Name != testDetectorName {
t.Error("Expected detector Name does not match")
}

if detectors.Value[0].Location != testLocation {
t.Error("Expected detector Name does not match")
}

if detectors.Value[0].Type != "Microsoft.RedHatOpenShift/openShiftClusters/detectors" {
t.Error("Expected detector Name does not match")
}

if detectors.Value[0].Properties.(map[string]interface{})["metadata"].(map[string]interface{})["id"].(string) != testDetectorName {
t.Error("Expected detector properties does not match")
}
}

func TestListDetectorAroAuthHealth(t *testing.T) {
srv, close := testhttp.NewTLSServer()
defer close()
testResourceId := "testResourceId"
testLocation := "eastus"
testDetectorName := "aroclusterinsights"
testBody := `{
"appFilter": null,
"dataProvidersMetadata": null,
"dataset": [],
"metadata": {
"analysisType": "",
"analysisTypes": null,
"author": "",
"category": "Insights",
"description": "Identifies scenarios that may cause a cluster to no longer be manageable.",
"id": "aroclusterinsights",
"name": "Cluster Insights",
"score": 0,
"supportTopicList": [],
"type": "Analysis",
"typeId": "a881d7f8-6385-4f33-9f43-063744b61452"
},
"status": {
"message": null,
"statusId": 4
},
"suggestedUtterances": null
}`

srv.SetResponse(testhttp.WithBody([]byte(testBody)))
verifier := pipelineVerifier{}
pl := runtime.NewPipeline("applenstest", "v1.0.0", runtime.PipelineOptions{PerCall: []policy.Policy{&verifier}}, &policy.ClientOptions{Transport: srv})
client := &Client{endpoint: srv.URL(), pipeline: pl}

detector, err := client.GetDetector(context.Background(),
&GetDetectorOptions{
ResourceID: testResourceId,
Location: testLocation,
DetectorID: testDetectorName,
})

if err != nil {
t.Fatal(err)
}

if detector.Id != fmt.Sprintf("%s/detectors/%s", testResourceId, testDetectorName) {
t.Error("Expected detector Id does not match")
}

if detector.Name != testDetectorName {
t.Error("Expected name of detector does not match")
}

if detector.Type != "Microsoft.RedHatOpenShift/openShiftClusters/detectors" {
t.Error("Expected type of detector does not match")
}

if detector.Location != testLocation {
t.Error("Expected type of detector does not match")
}

if detector.Properties.(map[string]interface{})["metadata"].(map[string]interface{})["id"].(string) != testDetectorName {
t.Error("Expected detector properties does not match")
}
}

type pipelineVerifier struct {
requests []pipelineVerifierRequest
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
type GetDetectorOptions struct {
ResourceID string
DetectorID string
Location string
}

func (options *GetDetectorOptions) toHeader() http.Header {
Expand All @@ -37,6 +38,7 @@ func (options *GetDetectorOptions) toHeader() http.Header {
// ListDetectorOptions includes options for ListDetector operation.
type ListDetectorsOptions struct {
ResourceID string
Location string
}

func (options *ListDetectorsOptions) toHeader() http.Header {
Expand Down

0 comments on commit 151eca2

Please sign in to comment.