Skip to content
Snippets Groups Projects
Verified Commit 0117ce5d authored by Mikhail Mazurskiy's avatar Mikhail Mazurskiy
Browse files

JsonBox helper for correct JSON marshaling

parent ef11253d
No related branches found
No related tags found
1 merge request!477JsonBox helper for correct JSON marshaling
......@@ -25,8 +25,8 @@ go_library(
deps = [
"//internal/api",
"//internal/gitlab",
"//internal/tool/prototool",
"//pkg/agentcfg",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_google_protobuf//reflect/protoreflect",
"@org_golang_google_protobuf//runtime/protoimpl",
],
......@@ -36,7 +36,6 @@ go_custom_test(
name = "api_test",
srcs = [
"get_agent_info_test.go",
"get_allowed_agents_test.go",
"get_project_info_test.go",
],
embed = [":api"],
......@@ -44,9 +43,7 @@ go_custom_test(
"//internal/gitlab",
"//internal/tool/testing/mock_gitlab",
"//internal/tool/testing/testhelpers",
"@com_github_google_go_cmp//cmp",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_golang_google_protobuf//testing/protocmp",
],
)
......@@ -4,36 +4,24 @@ import (
"context"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/gitlab"
"google.golang.org/protobuf/encoding/protojson"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/prototool"
)
const (
AllowedAgentsApiPath = "/api/v4/job/allowed_agents"
)
// AllowedAgentsForJobAlias ensures the protojson package is used for to/from JSON marshaling.
// See https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson.
type AllowedAgentsForJobAlias AllowedAgentsForJob
func (a *AllowedAgentsForJobAlias) MarshalJSON() ([]byte, error) {
typedA := (*AllowedAgentsForJob)(a)
return protojson.Marshal(typedA)
}
func (a *AllowedAgentsForJobAlias) UnmarshalJSON(data []byte) error {
typedA := (*AllowedAgentsForJob)(a)
return protojson.Unmarshal(data, typedA)
}
func GetAllowedAgentsForJob(ctx context.Context, client gitlab.ClientInterface, jobToken string) (*AllowedAgentsForJob, error) {
ji := &AllowedAgentsForJobAlias{}
resp := &prototool.JsonBox{
Message: &AllowedAgentsForJob{},
}
err := client.Do(ctx,
gitlab.WithPath(AllowedAgentsApiPath),
gitlab.WithJobToken(jobToken),
gitlab.WithResponseHandler(gitlab.JsonResponseHandler(ji)),
gitlab.WithResponseHandler(gitlab.JsonResponseHandler(resp)),
)
if err != nil {
return nil, err
}
return (*AllowedAgentsForJob)(ji), nil
return resp.Message.(*AllowedAgentsForJob), nil
}
package api
import (
"encoding/json"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"
)
var (
_ json.Marshaler = (*AllowedAgentsForJobAlias)(nil)
_ json.Unmarshaler = (*AllowedAgentsForJobAlias)(nil)
)
func TestGetAllowedAgents_JSON(t *testing.T) {
expected := &AllowedAgentsForJob{
AllowedAgents: []*AllowedAgent{
{
Id: 123,
ConfigProject: &ConfigProject{
Id: 234,
},
Configuration: &Configuration{
DefaultNamespace: "abc",
},
},
{
Id: 1,
ConfigProject: &ConfigProject{
Id: 2,
},
Configuration: &Configuration{
DefaultNamespace: "", // empty
},
},
},
Job: &Job{
Id: 32,
},
Pipeline: &Pipeline{
Id: 3232,
},
Project: &Project{
Id: 44,
},
User: &User{
Id: 2323,
Username: "abc",
},
}
data, err := json.Marshal(expected)
require.NoError(t, err)
actual := &AllowedAgentsForJob{}
err = json.Unmarshal(data, actual)
require.NoError(t, err)
assert.Empty(t, cmp.Diff(expected, actual, cmp.Transformer("AllowedAgentsForJob", transformAlias), protocmp.Transform()))
}
func transformAlias(val *AllowedAgentsForJobAlias) *AllowedAgentsForJob {
return (*AllowedAgentsForJob)(val)
}
......@@ -18,6 +18,7 @@ go_library(
"//internal/tool/errz",
"//internal/tool/grpctool",
"//internal/tool/logz",
"//internal/tool/prototool",
"//internal/tool/retry",
"//pkg/agentcfg",
"@com_github_cilium_cilium//api/v1/flow",
......@@ -31,7 +32,6 @@ go_library(
"@io_k8s_apimachinery//pkg/util/wait",
"@io_k8s_client_go//tools/cache",
"@org_golang_google_grpc//:grpc",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/timestamppb",
"@org_uber_go_zap//:zap",
......@@ -69,7 +69,6 @@ go_custom_test(
"@io_k8s_apimachinery//pkg/util/wait",
"@org_golang_google_grpc//:grpc",
"@org_golang_google_grpc//metadata",
"@org_golang_google_protobuf//testing/protocmp",
"@org_uber_go_zap//:zap",
"@org_uber_go_zap//zapcore",
"@org_uber_go_zap//zaptest",
......
......@@ -21,9 +21,9 @@ import (
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/errz"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/grpctool"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/logz"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/prototool"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/retry"
"go.uber.org/zap"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/timestamppb"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
......@@ -157,7 +157,7 @@ func (w *worker) processFlow(ctx context.Context, flw *flow.Flow, informer cache
func (w *worker) sendAlert(ctx context.Context, fl *flow.Flow, cnp *v2.CiliumNetworkPolicy) (retErr error) {
mbdy, err := json.Marshal(payload{
Alert: alert{
Flow: (*flowAlias)(fl),
Flow: &prototool.JsonBox{Message: fl},
CiliumNetworkPolicy: cnp,
},
})
......@@ -182,25 +182,11 @@ func (w *worker) sendAlert(ctx context.Context, fl *flow.Flow, cnp *v2.CiliumNet
return nil
}
// flowAlias ensures the protojson package is used for to/from JSON marshaling.
// See https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson.
type flowAlias flow.Flow
func (f *flowAlias) MarshalJSON() ([]byte, error) {
typedF := (*flow.Flow)(f)
return protojson.Marshal(typedF)
}
func (f *flowAlias) UnmarshalJSON(data []byte) error {
typedF := (*flow.Flow)(f)
return protojson.Unmarshal(data, typedF)
}
type payload struct {
Alert alert `json:"alert"`
}
type alert struct {
Flow *flowAlias `json:"flow"`
Flow *prototool.JsonBox `json:"flow"`
CiliumNetworkPolicy *v2.CiliumNetworkPolicy `json:"ciliumNetworkPolicy"`
}
......@@ -2,7 +2,6 @@ package agent
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
......@@ -16,23 +15,14 @@ import (
"github.com/cilium/cilium/pkg/labels"
"github.com/cilium/cilium/pkg/policy/api"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/module/modagent"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/testing/mock_modagent"
"gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v14/internal/tool/testing/testhelpers"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest"
"google.golang.org/protobuf/testing/protocmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
_ json.Marshaler = (*flowAlias)(nil)
_ json.Unmarshaler = (*flowAlias)(nil)
)
func TestSuccessfulMapping(t *testing.T) {
for caseNum, matchingData := range matchingData() {
t.Run(fmt.Sprintf("case %d", caseNum), func(t *testing.T) {
......@@ -88,34 +78,6 @@ func TestNoMatch(t *testing.T) {
}
}
func TestJSON(t *testing.T) {
expected := payload{
Alert: alert{
Flow: (*flowAlias)(&flow.Flow{
DropReasonDesc: flow.DropReason_POLICY_DENIED,
}),
CiliumNetworkPolicy: &v2.CiliumNetworkPolicy{
TypeMeta: metav1.TypeMeta{
Kind: "bla",
APIVersion: "bla",
},
},
},
}
data, err := json.Marshal(expected)
require.NoError(t, err)
actual := payload{}
err = json.Unmarshal(data, &actual)
require.NoError(t, err)
assert.Empty(t, cmp.Diff(expected, actual, cmp.Transformer("flow", flowAlias2Flow), protocmp.Transform()))
}
func flowAlias2Flow(val *flowAlias) *flow.Flow {
return (*flow.Flow)(val)
}
func setupTest(t *testing.T, cv2 versioned.Interface) (*worker, *MockObserverClient, *MockObserver_GetFlowsClient, *mock_modagent.MockApi) {
ctrl := gomock.NewController(t)
flwClient := NewMockObserver_GetFlowsClient(ctrl)
......
......@@ -19,7 +19,7 @@ go_library(
"//internal/module/modserver",
"//internal/module/usage_metrics",
"//internal/tool/cache",
"//internal/tool/errz",
"//internal/tool/errz",
"//internal/tool/grpctool",
"//internal/tool/httpz",
"//internal/tool/logz",
......
......@@ -456,7 +456,7 @@ func configGitLabHandler(t *testing.T, config *gapi.Configuration) func(w http.R
w.WriteHeader(http.StatusUnauthorized)
return
}
testhelpers.RespondWithJSON(t, w, &gapi.AllowedAgentsForJobAlias{ // use alias to ensure proper JSON marshaling
testhelpers.RespondWithJSON(t, w, &prototool.JsonBox{Message: &gapi.AllowedAgentsForJob{
AllowedAgents: []*gapi.AllowedAgent{
{
Id: testhelpers.AgentId,
......@@ -479,7 +479,7 @@ func configGitLabHandler(t *testing.T, config *gapi.Configuration) func(w http.R
Id: 3,
Username: "testuser",
},
})
}})
}
}
......
......@@ -14,6 +14,7 @@ go_library(
name = "prototool",
srcs = [
"defaulting.go",
"jsonbox.go",
"prototool.extra_methods.go",
"prototool.pb.go",
"prototool.pb.validate.go",
......@@ -22,6 +23,8 @@ go_library(
visibility = ["//:__subpackages__"],
deps = [
"@com_github_envoyproxy_protoc_gen_validate//validate:go_custom_library",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//reflect/protoreflect",
"@org_golang_google_protobuf//runtime/protoimpl",
"@org_golang_google_protobuf//types/known/anypb",
......@@ -31,10 +34,16 @@ go_library(
go_custom_test(
name = "prototool_test",
srcs = ["defaulting_test.go"],
srcs = [
"defaulting_test.go",
"jsonbox_test.go",
],
embed = [":prototool"],
deps = [
"@com_github_google_go_cmp//cmp",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
"@org_golang_google_protobuf//testing/protocmp",
"@org_golang_google_protobuf//types/known/durationpb",
],
)
package prototool
import (
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
// JsonBox ensures the protojson package is used for to/from JSON marshaling.
// See https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson.
type JsonBox struct {
Message proto.Message
}
func (b *JsonBox) MarshalJSON() ([]byte, error) {
return protojson.Marshal(b.Message)
}
func (b *JsonBox) UnmarshalJSON(data []byte) error {
return protojson.Unmarshal(data, b.Message)
}
package prototool
import (
"encoding/json"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"
)
var (
_ json.Marshaler = (*JsonBox)(nil)
_ json.Unmarshaler = (*JsonBox)(nil)
)
// Test round trip using stdlib json package.
func TestJsonBox_RoundTrip(t *testing.T) {
source := &JsonBox{
Message: &HttpRequest{
Method: "POST",
Header: map[string]*Values{
"Req-Header": {
Value: []string{"x1", "x2"},
},
},
UrlPath: "adsad",
Query: map[string]*Values{
"asdsad": {
Value: []string{"asdasds"},
},
},
},
}
data, err := json.Marshal(source)
require.NoError(t, err)
actual := &JsonBox{
Message: &HttpRequest{},
}
err = json.Unmarshal(data, actual)
require.NoError(t, err)
assert.Empty(t, cmp.Diff(source.Message, actual.Message, protocmp.Transform()))
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment