Skip to content

Commit

Permalink
feat: Allow the SDK to pass instance tags to replicated-sdk (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
FourSigma authored Apr 5, 2024
1 parent f3bc9c0 commit 5267347
Show file tree
Hide file tree
Showing 18 changed files with 327 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .github/actions/validate-endpoints/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,5 @@ runs:
echo "Expected 5 events, but found $numOfEvents"
exit 1
fi
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ vet:

.PHONY: build-ttl.sh
build-ttl.sh:
docker build -t ttl.sh/${USER}/replicated-sdk:24h deploy/Dockerfile
docker buildx build . -t ttl.sh/${USER}/replicated-sdk:24h -f deploy/Dockerfile
docker push ttl.sh/${USER}/replicated-sdk:24h

make -C chart build-ttl.sh
Expand Down
1 change: 1 addition & 0 deletions chart/templates/replicated-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ rules:
- {{ include "replicated.secretName" . }}
- replicated-instance-report
- replicated-custom-app-metrics-report
- replicated-meta-data
{{ end }}
5 changes: 5 additions & 0 deletions chart/templates/replicated-supportbundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ stringData:
name: replicated-custom-app-metrics-report
includeValue: true
key: report
- secret:
namespace: {{ include "replicated.namespace" . }}
name: replicated-meta-data
includeValue: true
key: instance-tag-data
analyzers:
- jsonCompare:
checkName: Replicated SDK App Status
Expand Down
8 changes: 7 additions & 1 deletion deploy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@ ENTRYPOINT /usr/bin/go

FROM local_go as builder

ENV GOMODCACHE=/.cache/gomod-cache
ENV GOCACHE=/.cache/go-cache

ENV PROJECTPATH=/go/src/github.com/replicatedhq/replicated-sdk
WORKDIR $PROJECTPATH

COPY Makefile.build.mk ./
COPY Makefile ./
COPY go.mod go.sum ./
RUN --mount=type=cache,target=${GOMODCACHE} go mod download
COPY cmd ./cmd
COPY pkg ./pkg

ARG git_tag
ENV GIT_TAG=${git_tag}

RUN make build && mv ./bin/replicated /replicated
RUN --mount=type=cache,target=${GOMODCACHE} \
--mount=type=cache,target=${GOCACHE} \
make build && mv ./bin/replicated /replicated

FROM cgr.dev/chainguard/static:latest

Expand Down
8 changes: 4 additions & 4 deletions pact/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestSendInstanceData(t *testing.T) {
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
},
})
mockStore.EXPECT().GetNamespace().Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
mockStore.EXPECT().GetChannelID().Return("replicated-sdk-instance-app-nightly")
Expand Down Expand Up @@ -96,7 +96,7 @@ func TestSendInstanceData(t *testing.T) {
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
},
})
mockStore.EXPECT().GetNamespace().Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
mockStore.EXPECT().GetChannelID().Return("replicated-sdk-instance-app-beta")
Expand Down Expand Up @@ -147,7 +147,7 @@ func TestSendInstanceData(t *testing.T) {
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
},
})
mockStore.EXPECT().GetNamespace().Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
mockStore.EXPECT().GetChannelID().Return("replicated-sdk-instance-app-beta")
Expand Down Expand Up @@ -198,7 +198,7 @@ func TestSendInstanceData(t *testing.T) {
Endpoint: fmt.Sprintf("http://%s:%d", pact.Host, pact.Server.Port),
},
})
mockStore.EXPECT().GetNamespace().Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetNamespace().Times(2).Return("replicated-sdk-instance-namespace")
mockStore.EXPECT().GetReplicatedID().Return("replicated-sdk-instance-cluster-id")
mockStore.EXPECT().GetAppID().Return("replicated-sdk-instance-app")
mockStore.EXPECT().GetChannelID().Return("replicated-sdk-instance-app-nightly")
Expand Down
1 change: 1 addition & 0 deletions pkg/apiserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func Start(params APIServerParams) {
r.HandleFunc("/api/v1/app/updates", handlers.GetAppUpdates).Methods("GET")
r.HandleFunc("/api/v1/app/history", handlers.GetAppHistory).Methods("GET")
r.HandleFunc("/api/v1/app/custom-metrics", handlers.SendCustomAppMetrics).Methods("POST")
r.HandleFunc("/api/v1/app/instance-tags", handlers.SendAppInstanceTags).Methods("POST")

// integration
r.HandleFunc("/api/v1/integration/mock-data", handlers.EnforceMockAccess(handlers.PostIntegrationMockData)).Methods("POST")
Expand Down
45 changes: 45 additions & 0 deletions pkg/handlers/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handlers

import (
"encoding/json"
"fmt"
"net/http"
"reflect"
"sort"
Expand All @@ -19,6 +20,8 @@ import (
"github.com/replicatedhq/replicated-sdk/pkg/logger"
"github.com/replicatedhq/replicated-sdk/pkg/report"
"github.com/replicatedhq/replicated-sdk/pkg/store"
"github.com/replicatedhq/replicated-sdk/pkg/tags"
"github.com/replicatedhq/replicated-sdk/pkg/tags/types"
"github.com/replicatedhq/replicated-sdk/pkg/upstream"
upstreamtypes "github.com/replicatedhq/replicated-sdk/pkg/upstream/types"
"github.com/replicatedhq/replicated-sdk/pkg/util"
Expand Down Expand Up @@ -55,6 +58,10 @@ type SendCustomAppMetricsRequest struct {

type CustomAppMetricsData map[string]interface{}

type SendAppInstanceTagsRequest struct {
Data types.InstanceTagData `json:"data"`
}

func GetCurrentAppInfo(w http.ResponseWriter, r *http.Request) {
clientset, err := k8sutil.GetClientset()
if err != nil {
Expand Down Expand Up @@ -383,3 +390,41 @@ func validateCustomAppMetricsData(data CustomAppMetricsData) error {

return nil
}

func SendAppInstanceTags(w http.ResponseWriter, r *http.Request) {
request := SendAppInstanceTagsRequest{}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
t, ok := err.(*json.UnmarshalTypeError)
if ok {
logger.Errorf("failed to decode instance-tag request: %s value is not a string", t.Field)
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "%v not supported, only string values are allowed on instance-tags", t.Value)
return
}

logger.Error(errors.Wrap(err, "decode request"))
w.WriteHeader(http.StatusInternalServerError)
return
}

clientset, err := k8sutil.GetClientset()
if err != nil {
logger.Error(errors.Wrap(err, "failed to get clientset"))
w.WriteHeader(http.StatusInternalServerError)
return
}

if err := tags.Save(r.Context(), clientset, store.GetStore().GetNamespace(), request.Data); err != nil {
logger.Errorf("failed to save instance tags: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

if err := report.SendInstanceData(clientset, store.GetStore()); err != nil {
logger.Errorf("failed to send instance data: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}

JSON(w, http.StatusOK, "")
}
14 changes: 14 additions & 0 deletions pkg/report/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package report

import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/replicatedhq/replicated-sdk/pkg/logger"
"github.com/replicatedhq/replicated-sdk/pkg/report/types"
"github.com/replicatedhq/replicated-sdk/pkg/store"
"github.com/replicatedhq/replicated-sdk/pkg/tags"
"github.com/replicatedhq/replicated-sdk/pkg/util"
"k8s.io/client-go/kubernetes"
)
Expand Down Expand Up @@ -72,6 +74,12 @@ func SendAirgapInstanceData(clientset kubernetes.Interface, namespace string, li
event.ResourceStates = string(marshalledRS)
}

marshalledTags, err := json.Marshal(instanceData.Tags)
if err != nil {
return errors.Wrap(err, "failed to marshal tags")
}
event.Tags = string(marshalledTags)

report := &InstanceReport{
Events: []InstanceReportEvent{event},
}
Expand Down Expand Up @@ -141,6 +149,12 @@ func GetInstanceData(sdkStore store.Store) *types.InstanceData {
if distribution := GetDistribution(clientset); distribution != types.UnknownDistribution {
r.K8sDistribution = distribution.String()
}

if tdata, err := tags.Get(context.TODO(), clientset, sdkStore.GetNamespace()); err != nil {
logger.Debugf("failed to get instance tag data: %v", err.Error())
} else {
r.Tags = *tdata
}
}

return &r
Expand Down
1 change: 1 addition & 0 deletions pkg/report/instance_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type InstanceReportEvent struct {
DownstreamChannelID string `json:"downstream_channel_id,omitempty"`
DownstreamChannelSequence int64 `json:"downstream_channel_sequence"`
DownstreamChannelName string `json:"downstream_channel_name,omitempty"`
Tags string `json:"tags"`
}

func (r *InstanceReport) GetType() ReportType {
Expand Down
4 changes: 2 additions & 2 deletions pkg/report/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func Test_SendInstanceData(t *testing.T) {
Endpoint: mockServer.URL,
},
})
mockStore.EXPECT().GetNamespace().Return("test-namespace")
mockStore.EXPECT().GetNamespace().Times(2).Return("test-namespace")
mockStore.EXPECT().GetReplicatedID().Return("test-cluster-id")
mockStore.EXPECT().GetAppID().Return("test-app")
mockStore.EXPECT().GetChannelID().Return("test-app-nightly")
Expand Down Expand Up @@ -101,7 +101,7 @@ func Test_SendInstanceData(t *testing.T) {
Endpoint: mockServer.URL,
},
})
mockStore.EXPECT().GetNamespace().Times(2).Return("test-namespace")
mockStore.EXPECT().GetNamespace().Times(3).Return("test-namespace")
mockStore.EXPECT().GetReplicatedID().Return("test-cluster-id")
mockStore.EXPECT().GetAppID().Return("test-app")
mockStore.EXPECT().GetChannelID().Return("test-app-nightly")
Expand Down
1 change: 1 addition & 0 deletions pkg/report/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ func createTestInstanceEvent(reportedAt int64) InstanceReportEvent {
DownstreamChannelID: "test-channel-id",
DownstreamChannelName: "test-channel-name",
DownstreamChannelSequence: 1,
Tags: `{"force": false, "tags": {}}`,
}
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/report/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types"
tagstypes "github.com/replicatedhq/replicated-sdk/pkg/tags/types"
)

type Distribution int64
Expand Down Expand Up @@ -34,6 +35,7 @@ type InstanceData struct {
ResourceStates appstatetypes.ResourceStates `json:"resource_states"`
K8sVersion string `json:"k8s_version"`
K8sDistribution string `json:"k8s_distribution"`
Tags tagstypes.InstanceTagData `json:"tags"`
}

func (d Distribution) String() string {
Expand Down
9 changes: 9 additions & 0 deletions pkg/report/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ func GetInstanceDataHeaders(instanceData *types.InstanceData) map[string]string
headers["X-Replicated-K8sDistribution"] = instanceData.K8sDistribution
}

if !instanceData.Tags.IsEmpty() {
b64, err := instanceData.Tags.MarshalBase64()
if err != nil {
logger.Errorf("Failed to base64 encode instance tags into headers: %v: %v", instanceData.Tags, err)
} else {
headers["X-Replicated-InstanceTagData"] = string(b64)
}
}

return headers
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/report/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
appstatetypes "github.com/replicatedhq/replicated-sdk/pkg/appstate/types"
"github.com/replicatedhq/replicated-sdk/pkg/k8sutil"
"github.com/replicatedhq/replicated-sdk/pkg/report/types"
tagstypes "github.com/replicatedhq/replicated-sdk/pkg/tags/types"
"github.com/replicatedhq/replicated-sdk/pkg/util"
"github.com/stretchr/testify/assert"
"k8s.io/client-go/kubernetes/fake"
Expand Down Expand Up @@ -70,6 +71,7 @@ func TestGetInstanceDataHeaders(t *testing.T) {
ChannelSequence: 42,
K8sVersion: "v1.20.2+k3s1",
K8sDistribution: "k3s",
Tags: tagstypes.InstanceTagData{Force: true, Tags: map[string]string{"key": "value"}},
}

headers := GetInstanceDataHeaders(instanceData)
Expand All @@ -82,6 +84,7 @@ func TestGetInstanceDataHeaders(t *testing.T) {
"X-Replicated-DownstreamChannelID": "channel-789",
"X-Replicated-DownstreamChannelSequence": "42",
"X-Replicated-K8sDistribution": "k3s",
"X-Replicated-InstanceTagData": "eyJmb3JjZSI6dHJ1ZSwidGFncyI6eyJrZXkiOiJ2YWx1ZSJ9fQ==",
}
assert.Equal(t, expectedHeaders, headers)

Expand Down
Loading

0 comments on commit 5267347

Please sign in to comment.