Skip to content

Commit cd8b55b

Browse files
CyberARk(dataupload): replace PostDataReadingsWithOptions with PutSnapshot
- Introduced a new `Snapshot` struct to represent the payload for the CyberArk Discovery and Context API. - Renamed `PostDataReadingsWithOptions` to `PutSnapshot` to better reflect its purpose. - Updated `retrievePresignedUploadURL` to accept `clusterID` directly instead of using `Options`. - Refactored tests to use `PutSnapshot` and removed references to the old `PostDataReadingsWithOptions` method. Signed-off-by: Richard Wall <[email protected]>
1 parent 7490f89 commit cd8b55b

File tree

2 files changed

+73
-64
lines changed

2 files changed

+73
-64
lines changed

pkg/internal/cyberark/dataupload/dataupload.go

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212
"net/http"
1313
"net/url"
1414

15-
"github.com/jetstack/preflight/api"
15+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
16+
1617
"github.com/jetstack/preflight/pkg/version"
1718
)
1819

@@ -46,7 +47,43 @@ func New(httpClient *http.Client, baseURL string, authenticateRequest func(req *
4647
}
4748
}
4849

49-
// PostDataReadingsWithOptions PUTs the supplied payload to an [AWS presigned URL] which it obtains via the CyberArk inventory API.
50+
// Snapshot is the JSON that the CyberArk Discovery and Context API expects to
51+
// be uploaded to the AWS presigned URL.
52+
type Snapshot struct {
53+
// AgentVersion is the version of the Venafi Kubernetes Agent which is uploading this snapshot.
54+
AgentVersion string `json:"agent_version"`
55+
// ClusterID is the unique ID of the Kubernetes cluster which this snapshot was taken from.
56+
ClusterID string `json:"cluster_id"`
57+
// K8SVersion is the version of Kubernetes which the cluster is running.
58+
K8SVersion string `json:"k8s_version"`
59+
// Secrets is a list of Secret resources in the cluster. Not all Secret
60+
// types are included and only a subset of the Secret data is included.
61+
Secrets []*unstructured.Unstructured `json:"secrets"`
62+
// ServiceAccounts is a list of ServiceAccount resources in the cluster.
63+
ServiceAccounts []*unstructured.Unstructured `json:"serviceaccounts"`
64+
// Roles is a list of Role resources in the cluster.
65+
Roles []*unstructured.Unstructured `json:"roles"`
66+
// ClusterRoles is a list of ClusterRole resources in the cluster.
67+
ClusterRoles []*unstructured.Unstructured `json:"clusterroles"`
68+
// RoleBindings is a list of RoleBinding resources in the cluster.
69+
RoleBindings []*unstructured.Unstructured `json:"rolebindings"`
70+
// ClusterRoleBindings is a list of ClusterRoleBinding resources in the cluster.
71+
ClusterRoleBindings []*unstructured.Unstructured `json:"clusterrolebindings"`
72+
// Jobs is a list of Job resources in the cluster.
73+
Jobs []*unstructured.Unstructured `json:"jobs"`
74+
// CronJobs is a list of CronJob resources in the cluster.
75+
CronJobs []*unstructured.Unstructured `json:"cronjobs"`
76+
// Deployments is a list of Deployment resources in the cluster.
77+
Deployments []*unstructured.Unstructured `json:"deployments"`
78+
// Statefulsets is a list of StatefulSet resources in the cluster.
79+
Statefulsets []*unstructured.Unstructured `json:"statefulsets"`
80+
// Daemonsets is a list of DaemonSet resources in the cluster.
81+
Daemonsets []*unstructured.Unstructured `json:"daemonsets"`
82+
// Pods is a list of Pod resources in the cluster.
83+
Pods []*unstructured.Unstructured `json:"pods"`
84+
}
85+
86+
// PutSnapshot PUTs the supplied snapshot to an [AWS presigned URL] which it obtains via the CyberArk inventory API.
5087
// [AWS presigned URL]: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
5188
//
5289
// A SHA256 checksum header is included in the request, to verify that the payload
@@ -60,20 +97,16 @@ func New(httpClient *http.Client, baseURL string, authenticateRequest func(req *
6097
// If you omit that header, it is possible to PUT any data.
6198
// There is a work around listed in that issue which we have shared with the
6299
// CyberArk API team.
63-
func (c *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, payload api.DataReadingsPost, opts Options) error {
64-
if opts.ClusterName == "" {
65-
return fmt.Errorf("programmer mistake: the cluster name (aka `cluster_id` in the config file) cannot be left empty")
66-
}
67-
100+
func (c *CyberArkClient) PutSnapshot(ctx context.Context, snapshot Snapshot) error {
68101
encodedBody := &bytes.Buffer{}
69102
hash := sha256.New()
70-
if err := json.NewEncoder(io.MultiWriter(encodedBody, hash)).Encode(payload); err != nil {
103+
if err := json.NewEncoder(io.MultiWriter(encodedBody, hash)).Encode(snapshot); err != nil {
71104
return err
72105
}
73106
checksum := hash.Sum(nil)
74107
checksumHex := hex.EncodeToString(checksum)
75108
checksumBase64 := base64.StdEncoding.EncodeToString(checksum)
76-
presignedUploadURL, err := c.retrievePresignedUploadURL(ctx, checksumHex, opts)
109+
presignedUploadURL, err := c.retrievePresignedUploadURL(ctx, checksumHex, snapshot.ClusterID)
77110
if err != nil {
78111
return fmt.Errorf("while retrieving snapshot upload URL: %s", err)
79112
}
@@ -103,7 +136,7 @@ func (c *CyberArkClient) PostDataReadingsWithOptions(ctx context.Context, payloa
103136
return nil
104137
}
105138

106-
func (c *CyberArkClient) retrievePresignedUploadURL(ctx context.Context, checksum string, opts Options) (string, error) {
139+
func (c *CyberArkClient) retrievePresignedUploadURL(ctx context.Context, checksum string, clusterID string) (string, error) {
107140
uploadURL, err := url.JoinPath(c.baseURL, apiPathSnapshotLinks)
108141
if err != nil {
109142
return "", err
@@ -114,7 +147,7 @@ func (c *CyberArkClient) retrievePresignedUploadURL(ctx context.Context, checksu
114147
Checksum string `json:"checksum_sha256"`
115148
AgentVersion string `json:"agent_version"`
116149
}{
117-
ClusterID: opts.ClusterName,
150+
ClusterID: clusterID,
118151
Checksum: checksum,
119152
AgentVersion: version.PreflightVersion,
120153
}

pkg/internal/cyberark/dataupload/dataupload_test.go

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ import (
66
"net/http"
77
"os"
88
"testing"
9-
"time"
109

1110
"github.com/jetstack/venafi-connection-lib/http_client"
1211
"github.com/stretchr/testify/require"
1312
"k8s.io/client-go/transport"
1413
"k8s.io/klog/v2"
1514
"k8s.io/klog/v2/ktesting"
1615

17-
"github.com/jetstack/preflight/api"
1816
"github.com/jetstack/preflight/pkg/internal/cyberark/dataupload"
1917
"github.com/jetstack/preflight/pkg/internal/cyberark/identity"
2018
"github.com/jetstack/preflight/pkg/internal/cyberark/servicediscovery"
@@ -23,28 +21,10 @@ import (
2321
_ "k8s.io/klog/v2/ktesting/init"
2422
)
2523

26-
func TestCyberArkClient_PostDataReadingsWithOptions(t *testing.T) {
27-
fakeTime := time.Unix(123, 0)
28-
defaultPayload := api.DataReadingsPost{
29-
AgentMetadata: &api.AgentMetadata{
30-
Version: "test-version",
31-
ClusterID: "test",
32-
},
33-
DataGatherTime: fakeTime,
34-
DataReadings: []*api.DataReading{
35-
{
36-
ClusterID: "success-cluster-id",
37-
DataGatherer: "test-gatherer",
38-
Timestamp: api.Time{Time: fakeTime},
39-
Data: map[string]interface{}{"test": "data"},
40-
SchemaVersion: "v1",
41-
},
42-
},
43-
}
44-
defaultOpts := dataupload.Options{
45-
ClusterName: "success-cluster-id",
46-
}
47-
24+
// TestCyberArkClient_PutSnapshot_MockAPI tests the dataupload code against a
25+
// mock API server. The mock server is configured to return different responses
26+
// based on the cluster ID and bearer token used in the request.
27+
func TestCyberArkClient_PutSnapshot_MockAPI(t *testing.T) {
4828
setToken := func(token string) func(*http.Request) error {
4929
return func(req *http.Request) error {
5030
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
@@ -54,51 +34,49 @@ func TestCyberArkClient_PostDataReadingsWithOptions(t *testing.T) {
5434

5535
tests := []struct {
5636
name string
57-
payload api.DataReadingsPost
37+
snapshot dataupload.Snapshot
5838
authenticate func(req *http.Request) error
59-
opts dataupload.Options
6039
requireFn func(t *testing.T, err error)
6140
}{
6241
{
63-
name: "successful upload",
64-
payload: defaultPayload,
65-
opts: defaultOpts,
42+
name: "successful upload",
43+
snapshot: dataupload.Snapshot{
44+
ClusterID: "success-cluster-id",
45+
AgentVersion: "test-version",
46+
},
6647
authenticate: setToken("success-token"),
6748
requireFn: func(t *testing.T, err error) {
6849
require.NoError(t, err)
6950
},
7051
},
7152
{
72-
name: "error when cluster name is empty",
73-
payload: defaultPayload,
74-
opts: dataupload.Options{ClusterName: ""},
75-
authenticate: setToken("success-token"),
76-
requireFn: func(t *testing.T, err error) {
77-
require.ErrorContains(t, err, "programmer mistake: the cluster name")
53+
name: "error when bearer token is incorrect",
54+
snapshot: dataupload.Snapshot{
55+
ClusterID: "test",
56+
AgentVersion: "test-version",
7857
},
79-
},
80-
{
81-
name: "error when bearer token is incorrect",
82-
payload: defaultPayload,
83-
opts: defaultOpts,
8458
authenticate: setToken("fail-token"),
8559
requireFn: func(t *testing.T, err error) {
8660
require.ErrorContains(t, err, "while retrieving snapshot upload URL: received response with status code 500: should authenticate using the correct bearer token")
8761
},
8862
},
8963
{
90-
name: "invalid JSON from server (RetrievePresignedUploadURL step)",
91-
payload: defaultPayload,
92-
opts: dataupload.Options{ClusterName: "invalid-json-retrieve-presigned"},
64+
name: "invalid JSON from server (RetrievePresignedUploadURL step)",
65+
snapshot: dataupload.Snapshot{
66+
ClusterID: "invalid-json-retrieve-presigned",
67+
AgentVersion: "test-version",
68+
},
9369
authenticate: setToken("success-token"),
9470
requireFn: func(t *testing.T, err error) {
9571
require.ErrorContains(t, err, "while retrieving snapshot upload URL: rejecting JSON response from server as it was too large or was truncated")
9672
},
9773
},
9874
{
99-
name: "500 from server (RetrievePresignedUploadURL step)",
100-
payload: defaultPayload,
101-
opts: dataupload.Options{ClusterName: "invalid-response-post-data"},
75+
name: "500 from server (RetrievePresignedUploadURL step)",
76+
snapshot: dataupload.Snapshot{
77+
ClusterID: "invalid-response-post-data",
78+
AgentVersion: "test-version",
79+
},
10280
authenticate: setToken("success-token"),
10381
requireFn: func(t *testing.T, err error) {
10482
require.ErrorContains(t, err, "while retrieving snapshot upload URL: received response with status code 500: mock error")
@@ -115,13 +93,13 @@ func TestCyberArkClient_PostDataReadingsWithOptions(t *testing.T) {
11593

11694
cyberArkClient := dataupload.New(httpClient, datauploadAPIBaseURL, tc.authenticate)
11795

118-
err := cyberArkClient.PostDataReadingsWithOptions(ctx, tc.payload, tc.opts)
96+
err := cyberArkClient.PutSnapshot(ctx, tc.snapshot)
11997
tc.requireFn(t, err)
12098
})
12199
}
122100
}
123101

124-
// TestPostDataReadingsWithOptionsWithRealAPI demonstrates that the dataupload code works with the real inventory API.
102+
// TestCyberArkClient_PutSnapshot_RealAPI demonstrates that the dataupload code works with the real inventory API.
125103
// An API token is obtained by authenticating with the ARK_USERNAME and ARK_SECRET from the environment.
126104
// ARK_SUBDOMAIN should be your tenant subdomain.
127105
//
@@ -131,8 +109,8 @@ func TestCyberArkClient_PostDataReadingsWithOptions(t *testing.T) {
131109
// To enable verbose request logging:
132110
//
133111
// go test ./pkg/internal/cyberark/dataupload/... \
134-
// -v -count 1 -run TestPostDataReadingsWithOptionsWithRealAPI -args -testing.v 6
135-
func TestPostDataReadingsWithOptionsWithRealAPI(t *testing.T) {
112+
// -v -count 1 -run TestCyberArkClient_PutSnapshot_RealAPI -args -testing.v 6
113+
func TestCyberArkClient_PutSnapshot_RealAPI(t *testing.T) {
136114
subdomain := os.Getenv("ARK_SUBDOMAIN")
137115
username := os.Getenv("ARK_USERNAME")
138116
secret := os.Getenv("ARK_SECRET")
@@ -159,8 +137,6 @@ func TestPostDataReadingsWithOptionsWithRealAPI(t *testing.T) {
159137
require.NoError(t, err)
160138

161139
cyberArkClient := dataupload.New(httpClient, services.DiscoveryContext.API, identityClient.AuthenticateRequest)
162-
err = cyberArkClient.PostDataReadingsWithOptions(ctx, api.DataReadingsPost{}, dataupload.Options{
163-
ClusterName: "bb068932-c80d-460d-88df-34bc7f3f3297",
164-
})
140+
err = cyberArkClient.PutSnapshot(ctx, dataupload.Snapshot{})
165141
require.NoError(t, err)
166142
}

0 commit comments

Comments
 (0)