ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
1 changed files with 145336 additions and 0 deletions
177
services/short/controller/run_test.go
Normal file
177
services/short/controller/run_test.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v65/common"
|
||||
"github.com/oracle/oci-go-sdk/v65/core"
|
||||
kcore "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
dynfake "k8s.io/client-go/dynamic/fake"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"quorate.dev/services/short/cloud"
|
||||
"quorate.dev/services/short/config"
|
||||
"quorate.dev/services/short/vault"
|
||||
"quorate.dev/services/short/wan"
|
||||
)
|
||||
|
||||
type publishedCloud struct {
|
||||
cloud.Network
|
||||
phase atomic.Int32
|
||||
}
|
||||
|
||||
func (f *publishedCloud) generations() []int {
|
||||
switch f.phase.Load() {
|
||||
case 0:
|
||||
return []int{0}
|
||||
case 1:
|
||||
return []int{0, 1}
|
||||
default:
|
||||
return []int{1}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *publishedCloud) ListCpes(context.Context, core.ListCpesRequest) (core.ListCpesResponse, error) {
|
||||
var result core.ListCpesResponse
|
||||
for _, i := range f.generations() {
|
||||
result.Items = append(result.Items, core.Cpe{Id: common.String(fmt.Sprint(i)), IpAddress: common.String(fmt.Sprintf("192.0.2.%d", i+2)), FreeformTags: map[string]string{"owner": "short"}})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *publishedCloud) ListIPSecConnections(context.Context, core.ListIPSecConnectionsRequest) (core.ListIPSecConnectionsResponse, error) {
|
||||
var result core.ListIPSecConnectionsResponse
|
||||
for _, i := range f.generations() {
|
||||
result.Items = append(result.Items, core.IpSecConnection{Id: common.String(fmt.Sprint(i)), CpeId: common.String(fmt.Sprint(i)), LifecycleState: core.IpSecConnectionLifecycleStateAvailable, StaticRoutes: []string{"198.18.0.10/32"}, FreeformTags: map[string]string{"owner": "short"}})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *publishedCloud) ListIPSecConnectionTunnels(_ context.Context, r core.ListIPSecConnectionTunnelsRequest) (core.ListIPSecConnectionTunnelsResponse, error) {
|
||||
var result core.ListIPSecConnectionTunnelsResponse
|
||||
for i := 1; i <= 2; i++ {
|
||||
result.Items = append(result.Items, core.IpSecConnectionTunnel{
|
||||
Id: common.String(fmt.Sprintf("%s-%d", *r.IpscId, i)), VpnIp: common.String(fmt.Sprintf("192.0.2.%s%d", map[string]string{"0": "1", "1": "2"}[*r.IpscId], i)),
|
||||
LifecycleState: core.IpSecConnectionTunnelLifecycleStateAvailable, Status: core.IpSecConnectionTunnelStatusDown,
|
||||
OracleCanInitiate: core.IpSecConnectionTunnelOracleCanInitiateInitiatorOrResponder,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *publishedCloud) GetIPSecConnectionTunnelSharedSecret(_ context.Context, r core.GetIPSecConnectionTunnelSharedSecretRequest) (core.GetIPSecConnectionTunnelSharedSecretResponse, error) {
|
||||
return core.GetIPSecConnectionTunnelSharedSecretResponse{IpSecConnectionTunnelSharedSecret: core.IpSecConnectionTunnelSharedSecret{SharedSecret: common.String("synthetic-" + *r.TunnelId)}}, nil
|
||||
}
|
||||
|
||||
func TestWANPublication(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
api := &publishedCloud{}
|
||||
dyn := dynfake.NewSimpleDynamicClient(runtime.NewScheme(), &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "cilium.io/v2", "kind": "CiliumCIDRGroup", "metadata": map[string]any{"name": "short"}, "spec": map[string]any{"externalCIDRs": []any{}},
|
||||
}})
|
||||
kube := fake.NewClientset(&kcore.Service{ObjectMeta: metav1.ObjectMeta{Name: "cilium-gateway-gateway-short", Namespace: "kube-system"}, Spec: kcore.ServiceSpec{ClusterIP: "192.0.2.100", Ports: []kcore.ServicePort{{Port: 8080}}}})
|
||||
var mu sync.Mutex
|
||||
version := 1
|
||||
data := map[string]any{"config": "{}"}
|
||||
writes := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch req.URL.Path {
|
||||
case "/api/interfaces/overview/interfaces_info":
|
||||
if req.Method != http.MethodGet {
|
||||
t.Error("WAN reader attempted a mutation")
|
||||
}
|
||||
address := "192.0.2.2"
|
||||
if api.phase.Load() > 0 {
|
||||
address = "192.0.2.3"
|
||||
}
|
||||
fmt.Fprintf(w, `{"rows":[{"identifier":"WAN","ipv4":[{"ipaddr":%q}]}]}`, address)
|
||||
case "/v1/secret/data/short/peers":
|
||||
if req.Method == http.MethodGet {
|
||||
json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{"data": data, "metadata": map[string]int{"version": version}}})
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
Options struct{ CAS int } `json:"options"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(req.Body).Decode(&input); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if req.Method != http.MethodPost || input.Options.CAS != version {
|
||||
t.Error("configuration publication lost its CAS contract")
|
||||
http.Error(w, "conflict", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
var published config.Runtime
|
||||
if err := json.Unmarshal([]byte(input.Data["config"].(string)), &published); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
current, _ := dyn.Resource(group).Get(ctx, "short", metav1.GetOptions{})
|
||||
allowed, _, _ := unstructured.NestedStringSlice(current.Object, "spec", "externalCIDRs")
|
||||
for _, p := range published.Peers {
|
||||
found := false
|
||||
for _, cidr := range allowed {
|
||||
found = found || cidr == p.Address+"/32"
|
||||
}
|
||||
if !found {
|
||||
t.Error("published a peer before admitting its address")
|
||||
}
|
||||
}
|
||||
data = input.Data
|
||||
version++
|
||||
writes++
|
||||
default:
|
||||
t.Errorf("unexpected request: %s", req.URL.Path)
|
||||
http.NotFound(w, req)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
r := Runner{Cloud: &cloud.Client{API: api, Config: config.Cloud{Address: "198.18.0.10", Network: "198.18.1.0/24"}}, WAN: &wan.Client{URL: server.URL, HTTP: server.Client()}, Kube: kube, Dynamic: dyn}
|
||||
kv := &vault.Client{URL: server.URL, HTTP: server.Client(), Token: "synthetic-token"}
|
||||
for phase, wantCIDRs := range []int{2, 4, 2} {
|
||||
api.phase.Store(int32(phase))
|
||||
if err := r.step(ctx, kv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mu.Lock()
|
||||
value := data["config"].(string)
|
||||
count := writes
|
||||
mu.Unlock()
|
||||
var published config.Runtime
|
||||
if err := json.Unmarshal([]byte(value), &published); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantWAN, wantWrites := "192.0.2.2", 1
|
||||
if phase > 0 {
|
||||
wantWAN, wantWrites = "192.0.2.3", 2
|
||||
}
|
||||
if published.WAN != wantWAN || count != wantWrites || published.Path.Address != "198.18.0.10" || published.Path.Network != "198.18.1.0/24" || published.Path.Gateway != "192.0.2.100" || !reflect.DeepEqual(published.Path.Ports, []uint16{8080}) {
|
||||
t.Fatalf("wrong runtime publication: %s; writes=%d", value, count)
|
||||
}
|
||||
current, _ := dyn.Resource(group).Get(ctx, "short", metav1.GetOptions{})
|
||||
cidrs, _, _ := unstructured.NestedStringSlice(current.Object, "spec", "externalCIDRs")
|
||||
if len(cidrs) != wantCIDRs {
|
||||
t.Fatalf("wrong peer admission transition: %v", cidrs)
|
||||
}
|
||||
if directory := os.Getenv("SHORT_FIXTURE"); directory != "" && phase < 2 {
|
||||
if err := os.WriteFile(filepath.Join(directory, fmt.Sprintf("runtime%d.json", phase)), []byte(value), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue