From ae7c541a08766690c49f503defbd53e695ebfbd2 Mon Sep 17 00:00:00 2001 From: kchiranjewee63 Date: Wed, 26 Jun 2024 10:53:05 -0400 Subject: [PATCH] Dispatch verification rules to tratteria agents on trats resource creation --- kubernetes/crd/deploy.sh | 1 - kubernetes/crd/example-trat.yaml | 10 +- kubernetes/crd/trat-crd.yaml | 38 ++++- kubernetes/tconfigd/deploy.sh | 5 - kubernetes/tconfigd/deployment.yaml | 9 -- rules/deploy-rules.sh | 27 ---- .../order/verification-rules.ndjson | 43 ------ .../stocks/verification-rules.ndjson | 73 --------- rules/example-rules/trats.ndjson | 115 --------------- service/agentsmanager/agentsmanager.go | 51 +++++-- service/api/handler/handler.go | 52 +------ service/api/pkg/apierrors/apperrors.go | 5 - service/api/pkg/rules/parse.go | 101 ------------- service/api/pkg/rules/rules.go | 53 ------- service/api/pkg/rules/types.go | 62 -------- service/api/pkg/rules/validation.go | 89 ----------- service/api/pkg/service/service.go | 29 ++-- service/api/setup.go | 16 +- service/cmd/main.go | 12 +- service/configdispatcher/configdispatcher.go | 69 +++++++++ service/tconfigderrors/tconfigerrors.go | 7 + service/tratcontroller/controller.go | 139 +++++++++++++++--- .../pkg/apis/tratteria/v1alpha1/types.go | 75 +++++++++- .../v1alpha1/zz_generated.deepcopy.go | 97 +++++++++++- service/tratcontroller/setup.go | 8 +- 25 files changed, 461 insertions(+), 725 deletions(-) delete mode 100755 rules/deploy-rules.sh delete mode 100644 rules/example-rules/order/verification-rules.ndjson delete mode 100644 rules/example-rules/stocks/verification-rules.ndjson delete mode 100644 rules/example-rules/trats.ndjson delete mode 100644 service/api/pkg/apierrors/apperrors.go delete mode 100644 service/api/pkg/rules/parse.go delete mode 100644 service/api/pkg/rules/rules.go delete mode 100644 service/api/pkg/rules/types.go delete mode 100644 service/api/pkg/rules/validation.go create mode 100644 service/configdispatcher/configdispatcher.go create mode 100644 service/tconfigderrors/tconfigerrors.go diff --git a/kubernetes/crd/deploy.sh b/kubernetes/crd/deploy.sh index 404806f..b14e365 100755 --- a/kubernetes/crd/deploy.sh +++ b/kubernetes/crd/deploy.sh @@ -1,4 +1,3 @@ kubectl create namespace test-application kubectl apply -f trat-crd.yaml -kubectl apply -f example-trat.yaml diff --git a/kubernetes/crd/example-trat.yaml b/kubernetes/crd/example-trat.yaml index 006f6e3..5aae6b4 100644 --- a/kubernetes/crd/example-trat.yaml +++ b/kubernetes/crd/example-trat.yaml @@ -1,12 +1,13 @@ apiVersion: tratteria.io/v1alpha1 kind: TraT metadata: - name: trade + name: order-trade-trat namespace: test-application spec: endpoint: "order/trade/{#stock-id}?action={#action}" method: "POST" - azd-mapping: + purp: trade + azdMapping: quantity: required: true value: "${body.quantity}" @@ -17,7 +18,4 @@ spec: required: true value: "${stock-id}" services: - - order - - catalog - - stocks - - payment \ No newline at end of file + - name: order diff --git a/kubernetes/crd/trat-crd.yaml b/kubernetes/crd/trat-crd.yaml index c426295..9a87409 100644 --- a/kubernetes/crd/trat-crd.yaml +++ b/kubernetes/crd/trat-crd.yaml @@ -33,18 +33,50 @@ spec: type: string method: type: string - azd-mapping: + purp: + type: string + azdMapping: type: object x-kubernetes-preserve-unknown-fields: true services: type: array items: - type: string + type: object + properties: + name: + type: string + endpoint: + type: string + azdMapping: + type: object + x-kubernetes-preserve-unknown-fields: true required: ["endpoint", "method", "services"] status: type: object properties: - applied: + status: + type: string + default: "PENDING" + verificationApplied: type: boolean + generationApplied: + type: boolean + lastErrorMessage: + type: string + nullable: true + retries: + type: integer + default: 0 + additionalPrinterColumns: + - name: "Status" + type: "string" + jsonPath: ".status.status" + - name: "Age" + type: "date" + jsonPath: ".metadata.creationTimestamp" + description: "Time since creation" + - name: "Retries" + type: "integer" + jsonPath: ".status.retries" subresources: status: {} diff --git a/kubernetes/tconfigd/deploy.sh b/kubernetes/tconfigd/deploy.sh index 4130d19..5b69dc3 100755 --- a/kubernetes/tconfigd/deploy.sh +++ b/kubernetes/tconfigd/deploy.sh @@ -4,11 +4,6 @@ kubectl create namespace tratteria kubectl create configmap config --from-file=config.yaml=config.yaml -n tratteria -cd ../../rules -chmod +x deploy-rules.sh -./deploy-rules.sh example-rules -cd ../kubernetes/tconfigd - kubectl apply -f service-account.yaml kubectl apply -f role.yaml kubectl apply -f rolebinding.yaml diff --git a/kubernetes/tconfigd/deployment.yaml b/kubernetes/tconfigd/deployment.yaml index e293541..a1ccab2 100644 --- a/kubernetes/tconfigd/deployment.yaml +++ b/kubernetes/tconfigd/deployment.yaml @@ -32,9 +32,6 @@ spec: - containerPort: 443 protocol: TCP volumeMounts: - - name: trats-rules-volume - mountPath: "/etc/tconfigd/rules" - readOnly: true - name: tconfigd-config-volume mountPath: "/etc/tconfigd/config" readOnly: true @@ -43,12 +40,6 @@ spec: readOnly: true restartPolicy: Always volumes: - - name: trats-rules-volume - configMap: - name: trats-rules-config - items: - - key: "trats-rules.ndjson" - path: "trats-rules.ndjson" - name: tconfigd-config-volume configMap: name: config diff --git a/rules/deploy-rules.sh b/rules/deploy-rules.sh deleted file mode 100755 index ec6843b..0000000 --- a/rules/deploy-rules.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -if [ "$#" -ne 1 ]; then - echo "Usage: $0 " - exit 1 -fi - -INPUT_DIRECTORY="$1" -CONFIG_MAP_NAME="trats-rules-config" -NAMESPACE="tratteria" - -if [ ! -d "$INPUT_DIRECTORY" ]; then - echo "Error: Trats rules directory $INPUT_DIRECTORY does not exist." - exit 1 -fi - -if [ $(find "$INPUT_DIRECTORY" -type f -name '*.ndjson' | wc -l) -eq 0 ]; then - echo "Error: No trats rules files found in $INPUT_DIRECTORY." - exit 1 -fi - -kubectl create configmap "$CONFIG_MAP_NAME" \ - --from-file=trats-rules.ndjson=<(find "$INPUT_DIRECTORY" -type f -name '*.ndjson' -exec cat {} + | sed 's/^/ /') \ - --namespace="$NAMESPACE" \ - --dry-run=client -o yaml | kubectl apply -f - - -echo -e "\nTrats rules deployed successfully." diff --git a/rules/example-rules/order/verification-rules.ndjson b/rules/example-rules/order/verification-rules.ndjson deleted file mode 100644 index 5b216f0..0000000 --- a/rules/example-rules/order/verification-rules.ndjson +++ /dev/null @@ -1,43 +0,0 @@ -{ - "type": "TraT-Verification-Rule", - "service": "order", - "route": "/order", - "method": "POST", - "rules": [ - { - "traT-name": "trade-trat", - "adz-mapping": { - "action": { - "path": "$body.action", - "required": true - }, - "quantity": { - "path": "$body.quantity", - "required": true - }, - "stock": { - "path": "$body.stockID", - "required": true - } - } - } - ] -} - -{ - "type": "TraT-Verification-Rule", - "service": "order", - "route": "/order/{id}", - "method": "GET", - "rules": [ - { - "traT-name": "trade-details-trat", - "adz-mapping": { - "id": { - "mappting": "${id}", - "required": true - } - } - } - ] -} diff --git a/rules/example-rules/stocks/verification-rules.ndjson b/rules/example-rules/stocks/verification-rules.ndjson deleted file mode 100644 index 705eaee..0000000 --- a/rules/example-rules/stocks/verification-rules.ndjson +++ /dev/null @@ -1,73 +0,0 @@ -{ - "type": "TraT-Verification-Rule", - "service": "stocks", - "route": "/stocks/search", - "method": "GET", - "rules": [ - { - "traT-name": "stock-search-trat", - "adz-mapping": { - "query": { - "mapping": "$queryParameters.query", - "required": true - } - } - } - ] -} - -{ - "type": "TraT-Verification-Rule", - "service": "stocks", - "route": "/stocks/{id}", - "method": "GET", - "rules": [ - { - "traT-name": "stock-details-trat", - "adz-mapping": { - "stockID": { - "mapping": "${id}", - "required": true - } - } - } - ] -} - -{ - "type": "TraT-Verification-Rule", - "service": "stocks", - "route": "/stocks", - "method": "POST", - "rules": [ - { - "traT-name": "stock-update-trat", - "adz-mapping": { - "action": { - "path": "$body.action", - "required": true - }, - "quantity": { - "path": "$body.quantity", - "required": true - }, - "stock": { - "path": "$body.stockID", - "required": true - } - } - } - ] -} - -{ - "type": "TraT-Verification-Rule", - "service": "stocks", - "route": "/stocks/holdings", - "method": "GET", - "rules": [ - { - "traT-name": "portfolio-trat" - } - ] -} diff --git a/rules/example-rules/trats.ndjson b/rules/example-rules/trats.ndjson deleted file mode 100644 index 1c06c4f..0000000 --- a/rules/example-rules/trats.ndjson +++ /dev/null @@ -1,115 +0,0 @@ -{ - "type": "TraT", - "traT-name": "stock-search-trat", - "scope": "stock-search", - "adz-schema": { - "query": {} - } -} - -{ - "type": "TraT-Generation-Rule", - "traT-name": "stock-search-trat", - "route": "/stocks/stock-search", - "method": "GET", - "adz-mapping": { - "query": "$queryParameters.query" - } -} - -{ - "type": "TraT", - "traT-name": "stock-details-trat", - "scope": "stock-details", - "adz-schema": { - "stockID": {} - } -} - -{ - "type": "TraT-Generation-Rule", - "traT-name": "stock-details-trat", - "route": "/stocks/{id}", - "method": "GET", - "adz-mapping": { - "stockID": "${id}" - } -} - -{ - "type": "TraT", - "traT-name": "stock-update-trat", - "scope": "stock-update", - "adz-schema": { - "action": {}, - "quantity": {}, - "stock": {} - } -} - -{ - "type": "TraT-Generation-Rule", - "route": "/stock", - "method": "POST", - "traT-name": "stock-update-trat", - "adz-mapping": { - "action": "$body.action", - "quantity": "$body.quantity", - "stock": "$body.stockID" - } -} - -{ - "type": "TraT", - "traT-name": "portfolio-trat", - "scope": "portfolio" -} - -{ - "type": "TraT-Generation-Rule", - "traT-name": "portfolio-trat", - "route": "/stocks/holdings", - "method": "GET" -} - -{ - "type": "TraT", - "traT-name": "trade-trat", - "scope": "trade", - "adz-schema": { - "action": {}, - "quantity": {}, - "stock": {} - } -} - -{ - "type": "TraT-Generation-Rule", - "traT-name": "trade-trat", - "route": "/order", - "method": "POST", - "adz-mapping": { - "action": "$body.action", - "quantity": "$body.quantity", - "stock": "$body.stockID" - } -} - -{ - "type": "TraT", - "traT-name": "trade-details-trat", - "scope": "trade-details", - "adz-schema": { - "id": {} - } -} - -{ - "type": "TraT-Generation-Rule", - "traT-name": "trade-details-trat", - "route": "/order/{id}", - "method": "GET", - "adz-mapping": { - "id": "${id}" - } -} diff --git a/service/agentsmanager/agentsmanager.go b/service/agentsmanager/agentsmanager.go index fba5a42..9b0f8a2 100644 --- a/service/agentsmanager/agentsmanager.go +++ b/service/agentsmanager/agentsmanager.go @@ -1,11 +1,13 @@ package agentsmanager import ( + "fmt" "strconv" "sync" "time" "github.com/tratteria/tconfigd/common" + "github.com/tratteria/tconfigd/tconfigderrors" ) const ( @@ -14,21 +16,30 @@ const ( CLEANUP_INTERVAL = 5 * EXPIRATION_DURATION ) -type AgentRegistry struct { +type Agent struct { IpAddress string Port int ServiceName string LastHeartbeat time.Time } +type AgentLifecycleManager interface { + RegisterAgent(string, int, string) + UpdateAgentHeartbeat(string, int, string) +} + +type ActiveAgentRetriever interface { + GetServiceActiveAgents(string) ([]*Agent, error) +} + type AgentsManager struct { - agents map[string]*AgentRegistry + agents map[string]map[string]*Agent mutex sync.RWMutex } func NewAgentManager() *AgentsManager { am := &AgentsManager{ - agents: make(map[string]*AgentRegistry), + agents: make(map[string]map[string]*Agent), } go am.cleanupExpiredAgents() @@ -40,9 +51,13 @@ func (am *AgentsManager) RegisterAgent(ip string, port int, serviceName string) am.mutex.Lock() defer am.mutex.Unlock() + if am.agents[serviceName] == nil { + am.agents[serviceName] = make(map[string]*Agent) + } + key := ip + strconv.Itoa(port) - am.agents[key] = &AgentRegistry{ + am.agents[serviceName][key] = &Agent{ IpAddress: ip, Port: port, ServiceName: serviceName, @@ -50,31 +65,37 @@ func (am *AgentsManager) RegisterAgent(ip string, port int, serviceName string) } } -func (am *AgentsManager) UpdateHeartbeat(ip string, port int) { +func (am *AgentsManager) UpdateAgentHeartbeat(ip string, port int, serviceName string) { am.mutex.Lock() defer am.mutex.Unlock() key := ip + strconv.Itoa(port) - if agent, ok := am.agents[key]; ok { + if agent, ok := am.agents[serviceName][key]; ok { agent.LastHeartbeat = time.Now() } // TODO: return error if agent entry not found } -func (am *AgentsManager) GetActiveAgents() map[string]*AgentRegistry { +func (am *AgentsManager) GetServiceActiveAgents(serviceName string) ([]*Agent, error) { am.mutex.RLock() defer am.mutex.RUnlock() - activeAgents := make(map[string]*AgentRegistry) + var activeAgents []*Agent + now := time.Now() - for key, agent := range am.agents { + serviceAgents, ok := am.agents[serviceName] + if !ok { + return nil, fmt.Errorf("%w: service %s", tconfigderrors.ErrNotFound, serviceName) + } + + for _, agent := range serviceAgents { if now.Sub(agent.LastHeartbeat) <= EXPIRATION_DURATION { - activeAgents[key] = agent + activeAgents = append(activeAgents, agent) } } - return activeAgents + return activeAgents, nil } func (am *AgentsManager) cleanupExpiredAgents() { @@ -93,9 +114,11 @@ func (am *AgentsManager) removeExpiredAgents() { now := time.Now() - for key, agent := range am.agents { - if now.Sub(agent.LastHeartbeat) > EXPIRATION_DURATION { - delete(am.agents, key) + for _, serviceAgents := range am.agents { + for key, agent := range serviceAgents { + if now.Sub(agent.LastHeartbeat) > EXPIRATION_DURATION { + delete(serviceAgents, key) + } } } } diff --git a/service/api/handler/handler.go b/service/api/handler/handler.go index b4459a9..e1732c6 100644 --- a/service/api/handler/handler.go +++ b/service/api/handler/handler.go @@ -2,10 +2,8 @@ package handler import ( "encoding/json" - "errors" "net/http" - "github.com/tratteria/tconfigd/api/pkg/apierrors" "github.com/tratteria/tconfigd/api/pkg/service" "go.uber.org/zap" @@ -36,54 +34,6 @@ type heartBeatRequest struct { RulesVersionID string `json:"rulesVersionId"` } -func (h *Handlers) GetVerificationRulesHandler(w http.ResponseWriter, r *http.Request) { - queryParams := r.URL.Query() - serviceName := queryParams.Get("service") - - if serviceName == "" { - http.Error(w, "Service parameter is required", http.StatusBadRequest) - - return - } - - verificationRules, err := h.Service.GetVerificationRule(serviceName) - if err != nil { - if errors.Is(err, apierrors.ErrVerificationRuleNotFound) { - http.Error(w, err.Error(), http.StatusNotFound) - } else { - http.Error(w, "Internal Server error", http.StatusInternalServerError) - } - - return - } - - response, err := json.Marshal(verificationRules) - if err != nil { - http.Error(w, "Failed to encode verification rules", http.StatusInternalServerError) - - return - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(response) -} - -func (h *Handlers) GetGenerationRulesHandler(w http.ResponseWriter, r *http.Request) { - generationRules := h.Service.GetGenerationRule() - - response, err := json.Marshal(generationRules) - if err != nil { - http.Error(w, "Failed to encode generation rules", http.StatusInternalServerError) - - return - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write(response) -} - func (h *Handlers) RegistrationHandler(w http.ResponseWriter, r *http.Request) { var registrationRequest registrationRequest @@ -118,7 +68,7 @@ func (h *Handlers) HeartBeatHandler(w http.ResponseWriter, r *http.Request) { h.Logger.Info("Received a heartbeat.", zap.String("service", heartBeatRequest.ServiceName)) - h.Service.RegisterHeartBeat(heartBeatRequest.IpAddress, heartBeatRequest.Port) + h.Service.RegisterHeartBeat(heartBeatRequest.IpAddress, heartBeatRequest.Port, heartBeatRequest.ServiceName) // TODO: if an agent is heartbeating with an old rule version id, notify it to fetch the latest rules diff --git a/service/api/pkg/apierrors/apperrors.go b/service/api/pkg/apierrors/apperrors.go deleted file mode 100644 index db6f286..0000000 --- a/service/api/pkg/apierrors/apperrors.go +++ /dev/null @@ -1,5 +0,0 @@ -package apierrors - -import "errors" - -var ErrVerificationRuleNotFound = errors.New("verification rule not found for the service") diff --git a/service/api/pkg/rules/parse.go b/service/api/pkg/rules/parse.go deleted file mode 100644 index 0a7c0c5..0000000 --- a/service/api/pkg/rules/parse.go +++ /dev/null @@ -1,101 +0,0 @@ -package rules - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "os" -) - -func parse(path string) (map[string]TraTDefinition, map[string]GenerationRule, map[string]map[string]VerificationRule, error) { - traTs := make(map[string]TraTDefinition) - generationRules := make(map[string]GenerationRule) - verificationRules := make(map[string]map[string]VerificationRule) - - file, err := os.Open(path) - if err != nil { - return nil, nil, nil, err - } - - defer file.Close() - - reader := bufio.NewReader(file) - decoder := json.NewDecoder(reader) - - for { - var jsonData map[string]interface{} - - if err := decoder.Decode(&jsonData); err == io.EOF { - break - } else if err != nil { - return nil, nil, nil, err - } - - switch jsonType := jsonData["type"]; jsonType { - case "TraT": - var definition TraTDefinition - - jsonBytes, err := json.Marshal(jsonData) - - if err != nil { - return nil, nil, nil, err - } - - if err := json.Unmarshal(jsonBytes, &definition); err != nil { - return nil, nil, nil, err - } - - traTs[definition.TraTName] = definition - - case "TraT-Generation-Rule": - var genRule GenerationRule - - jsonBytes, err := json.Marshal(jsonData) - - if err != nil { - return nil, nil, nil, err - } - - if err := json.Unmarshal(jsonBytes, &genRule); err != nil { - return nil, nil, nil, err - } - - key := genRule.Method + genRule.Route - - if _, exists := generationRules[key]; exists { - return nil, nil, nil, fmt.Errorf("multiple generation rules for route: %s, method: %s provided", genRule.Route, genRule.Method) - } - - generationRules[key] = genRule - - case "TraT-Verification-Rule": - var verRule VerificationRule - - jsonBytes, err := json.Marshal(jsonData) - - if err != nil { - return nil, nil, nil, err - } - - if err := json.Unmarshal(jsonBytes, &verRule); err != nil { - return nil, nil, nil, err - } - - serviceVerificatinRules, exists := verificationRules[verRule.Service] - if !exists { - serviceVerificatinRules = make(map[string]VerificationRule) - verificationRules[verRule.Service] = serviceVerificatinRules - } - - key := verRule.Method + verRule.Route - if _, exists := serviceVerificatinRules[key]; exists { - return nil, nil, nil, fmt.Errorf("multiple verification rules for service: %s, route: %s, method: %s provided", verRule.Service, verRule.Route, verRule.Method) - } - - serviceVerificatinRules[key] = verRule - } - } - - return traTs, generationRules, verificationRules, nil -} diff --git a/service/api/pkg/rules/rules.go b/service/api/pkg/rules/rules.go deleted file mode 100644 index 5f891d4..0000000 --- a/service/api/pkg/rules/rules.go +++ /dev/null @@ -1,53 +0,0 @@ -package rules - -import ( - "github.com/tratteria/tconfigd/api/pkg/apierrors" -) - -type Rules struct { - traTs map[string]TraTDefinition - generationRules map[string]GenerationRule - verificationRules map[string]map[string]VerificationRule -} - -func NewRules() *Rules { - return &Rules{ - traTs: make(map[string]TraTDefinition), - generationRules: make(map[string]GenerationRule), - verificationRules: make(map[string]map[string]VerificationRule), - } -} - -func (r *Rules) Load() error { - traTs, generationRules, verificationRules, err := parse("/etc/tconfigd/rules/trats-rules.ndjson") - if err != nil { - return err - } - - if err := validateGenerationRules(generationRules, traTs); err != nil { - return err - } - - if err := validateVerificationRules(verificationRules, traTs); err != nil { - return err - } - - r.traTs = traTs - r.generationRules = generationRules - r.verificationRules = verificationRules - - return nil -} - -func (r *Rules) GetVerificationRules(service string) (map[string]VerificationRule, error) { - verificationRule, exist := r.verificationRules[service] - if !exist { - return nil, apierrors.ErrVerificationRuleNotFound - } - - return verificationRule, nil -} - -func (r *Rules) GetGenerationRules() map[string]GenerationRule { - return r.generationRules -} diff --git a/service/api/pkg/rules/types.go b/service/api/pkg/rules/types.go deleted file mode 100644 index 7941af1..0000000 --- a/service/api/pkg/rules/types.go +++ /dev/null @@ -1,62 +0,0 @@ -package rules - -import ( - "encoding/json" - "fmt" -) - -type TraTDefinition struct { - Type string `json:"type"` - TraTName string `json:"traT-name"` - Scope string `json:"scope"` - AdzSchema map[string]interface{} `json:"adz-schema"` -} - -type GenerationRule struct { - Type string `json:"type"` - Route string `json:"route"` - Method string `json:"method"` - TraTName string `json:"traT-name"` - AdzMapping map[string]interface{} `json:"adz-mapping"` -} - -type VerificationRule struct { - Type string `json:"type"` - Service string `json:"service"` - Route string `json:"route"` - Method string `json:"method"` - Rules []Rule `json:"rules"` -} - -type Rule struct { - TraTName string `json:"traT-name"` - AdzMapping map[string]VerificationAdzField `json:"adz-mapping"` -} - -type VerificationAdzField struct { - Path string `json:"path"` - Required bool `json:"required"` -} - -func (v *VerificationAdzField) UnmarshalJSON(data []byte) error { - var obj struct { - Path string `json:"path"` - Required bool `json:"required"` - } - - if err := json.Unmarshal(data, &obj); err == nil { - *v = VerificationAdzField{Path: obj.Path, Required: obj.Required} - - return nil - } - - var path string - - if err := json.Unmarshal(data, &path); err == nil { - *v = VerificationAdzField{Path: path, Required: false} - - return nil - } - - return fmt.Errorf("cannot unmarshal verification rule adz-mapping VerificationAdzField: %s", data) -} diff --git a/service/api/pkg/rules/validation.go b/service/api/pkg/rules/validation.go deleted file mode 100644 index e591700..0000000 --- a/service/api/pkg/rules/validation.go +++ /dev/null @@ -1,89 +0,0 @@ -package rules - -import ( - "fmt" -) - -// TODO -// 1. Add validation if JSON paths are valid and referencing valid variables i.e body, header, or queryParameters -// 2. Add validation if referenced url-path parameters are correct -// 3. Add validation if route is correct i.e begins with leading slash - -var validMethods = map[string]struct{}{ - "GET": {}, - "POST": {}, - "PUT": {}, - "DELETE": {}, - "PATCH": {}, - "OPTIONS": {}, - "HEAD": {}, -} - -func isValidHTTPMethod(method string) bool { - _, exists := validMethods[method] - - return exists -} - -func validateGenerationRules(genRules map[string]GenerationRule, traTs map[string]TraTDefinition) error { - for _, genRule := range genRules { - traT, exist := traTs[genRule.TraTName] - if !exist { - return fmt.Errorf("invalid TraT %s referenced in TraT generation rule for route: %s, method: %s", genRule.TraTName, genRule.Route, genRule.Method) - } - - if !isValidHTTPMethod(genRule.Method) { - return fmt.Errorf("invalid HTTP method %s in generation rule for route: %s", genRule.Method, genRule.Route) - } - - if err := validateAdzMapping(genRule.AdzMapping, traT); err != nil { - return fmt.Errorf("invalid adz mapping in TraT generation rule; traT: %s, route: %s, method: %s; error: %s", genRule.TraTName, genRule.Route, genRule.Method, err.Error()) - } - } - - return nil -} - -func validateVerificationRules(verRules map[string]map[string]VerificationRule, traTs map[string]TraTDefinition) error { - for _, serviceVerRules := range verRules { - for _, verRule := range serviceVerRules { - if !isValidHTTPMethod(verRule.Method) { - return fmt.Errorf("invalid HTTP method %s in verification rule for route: %s", verRule.Method, verRule.Route) - } - - for _, rule := range verRule.Rules { - traT, exist := traTs[rule.TraTName] - if !exist { - return fmt.Errorf("invalid TraT %s referenced in TraT verification rule for route: %s, method: %s", rule.TraTName, verRule.Route, verRule.Method) - } - - if err := validateAdzMapping(rule.AdzMapping, traT); err != nil { - return fmt.Errorf("invalid adz mapping in TraT verification rule; traT: %s, route: %s, method: %s; error: %s", rule.TraTName, verRule.Route, verRule.Method, err.Error()) - } - } - } - } - - return nil -} - -func validateAdzMapping(adzMapping interface{}, traT TraTDefinition) error { - switch v := adzMapping.(type) { - case map[string]interface{}: - for field := range v { - if _, ok := traT.AdzSchema[field]; !ok { - return fmt.Errorf("invalid key '%s' in generation rule adz mapping", field) - } - } - case map[string]VerificationAdzField: - for field := range v { - if _, ok := traT.AdzSchema[field]; !ok { - return fmt.Errorf("invalid key '%s' in verification rule adz mapping", field) - } - } - default: - return fmt.Errorf("unknown type for adz mapping") - } - - return nil -} diff --git a/service/api/pkg/service/service.go b/service/api/pkg/service/service.go index eaa7fdd..844982b 100644 --- a/service/api/pkg/service/service.go +++ b/service/api/pkg/service/service.go @@ -2,39 +2,28 @@ package service import ( "github.com/tratteria/tconfigd/agentsmanager" - "github.com/tratteria/tconfigd/api/pkg/rules" "go.uber.org/zap" ) type Service struct { - rules *rules.Rules - agentsManager *agentsmanager.AgentsManager - logger *zap.Logger + agentsLifecycleManager agentsmanager.AgentLifecycleManager + logger *zap.Logger } -func NewService(rules *rules.Rules, agentsManager *agentsmanager.AgentsManager, logger *zap.Logger) *Service { +func NewService(agentsLifecycleManager agentsmanager.AgentLifecycleManager, logger *zap.Logger) *Service { return &Service{ - rules: rules, - agentsManager: agentsManager, - logger: logger, + agentsLifecycleManager: agentsLifecycleManager, + logger: logger, } } -func (s *Service) GetVerificationRule(service string) (map[string]rules.VerificationRule, error) { - return s.rules.GetVerificationRules(service) -} - -func (s *Service) GetGenerationRule() map[string]rules.GenerationRule { - return s.rules.GetGenerationRules() -} - -func (s *Service) RegisterAgent(ipaddress string, port int, service string) { +func (s *Service) RegisterAgent(ipaddress string, port int, serviceName string) { // TODO: return rules belonging to the service - s.agentsManager.RegisterAgent(ipaddress, port, service) + s.agentsLifecycleManager.RegisterAgent(ipaddress, port, serviceName) } -func (s *Service) RegisterHeartBeat(ipaddress string, port int) { +func (s *Service) RegisterHeartBeat(ipaddress string, port int, serviceName string) { // TODO: if updateHeartBeat fails, notify agent to register // TODO: if an agent is heartbeating with an old rule version id, notify it to fetch the latest rules - s.agentsManager.UpdateHeartbeat(ipaddress, port) + s.agentsLifecycleManager.UpdateAgentHeartbeat(ipaddress, port, serviceName) } diff --git a/service/api/setup.go b/service/api/setup.go index 5b92e10..753da6f 100644 --- a/service/api/setup.go +++ b/service/api/setup.go @@ -10,24 +10,16 @@ import ( "github.com/tratteria/tconfigd/agentsmanager" "github.com/tratteria/tconfigd/api/handler" - "github.com/tratteria/tconfigd/api/pkg/rules" "github.com/tratteria/tconfigd/api/pkg/service" ) type API struct { - AgentsManager *agentsmanager.AgentsManager - Logger *zap.Logger + AgentsLifecycleManager agentsmanager.AgentLifecycleManager + Logger *zap.Logger } func (api *API) Run() error { - rules := rules.NewRules() - - err := rules.Load() - if err != nil { - return fmt.Errorf("error loading rules: %w", err) - } - - service := service.NewService(rules, api.AgentsManager, api.Logger) + service := service.NewService(api.AgentsLifecycleManager, api.Logger) handler := handler.NewHandlers(service, api.Logger) router := mux.NewRouter() @@ -54,6 +46,4 @@ func (api *API) Run() error { func initializeRulesRoutes(router *mux.Router, handler *handler.Handlers) { router.HandleFunc("/agent-register", handler.RegistrationHandler).Methods("POST") router.HandleFunc("/agent-heartbeat", handler.HeartBeatHandler).Methods("POST") - router.HandleFunc("/verification-rules", handler.GetVerificationRulesHandler).Methods("GET") - router.HandleFunc("/generation-rules", handler.GetGenerationRulesHandler).Methods("GET") } diff --git a/service/cmd/main.go b/service/cmd/main.go index 5cd4b4f..ef9caad 100644 --- a/service/cmd/main.go +++ b/service/cmd/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "net/http" "os" "os/signal" "syscall" @@ -11,6 +12,7 @@ import ( "github.com/tratteria/tconfigd/agentsmanager" "github.com/tratteria/tconfigd/api" "github.com/tratteria/tconfigd/config" + "github.com/tratteria/tconfigd/configdispatcher" "github.com/tratteria/tconfigd/tratcontroller" "github.com/tratteria/tconfigd/webhook" "go.uber.org/zap" @@ -44,14 +46,16 @@ func main() { logger.Fatal("Error reading configuration.", zap.Error(err)) } + httpClient := &http.Client{} agentsManager := agentsmanager.NewAgentManager() + configdispatcher := configdispatcher.NewConfigDispatcher(agentsManager, httpClient) go func() { logger.Info("Starting API server...") apiServer := &api.API{ - AgentsManager: agentsManager, - Logger: logger, + AgentsLifecycleManager: agentsManager, + Logger: logger, } if err := apiServer.Run(); err != nil { @@ -77,7 +81,9 @@ func main() { go func() { logger.Info("Starting TraT Controller...") - tratController := &tratcontroller.TraTController{} + tratController := &tratcontroller.TraTController{ + ConfigDispatcher: configdispatcher, + } if err := tratController.Run(); err != nil { logger.Fatal("Failed to start TraT Controller server.", zap.Error(err)) diff --git a/service/configdispatcher/configdispatcher.go b/service/configdispatcher/configdispatcher.go new file mode 100644 index 0000000..060d367 --- /dev/null +++ b/service/configdispatcher/configdispatcher.go @@ -0,0 +1,69 @@ +package configdispatcher + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/tratteria/tconfigd/agentsmanager" + "github.com/tratteria/tconfigd/tratcontroller/pkg/apis/tratteria/v1alpha1" +) + +const ( + verificationConfigWebhookEndpoint = "/config-webhook" +) + +type ConfigDispatcher struct { + activeAgentRetriever agentsmanager.ActiveAgentRetriever + httpClient *http.Client +} + +func NewConfigDispatcher(activeAgentRetriever agentsmanager.ActiveAgentRetriever, httpClient *http.Client) *ConfigDispatcher { + return &ConfigDispatcher{ + activeAgentRetriever: activeAgentRetriever, + httpClient: httpClient, + } +} + +// TODO: Implement parallel processing of HTTP requests using goroutines +func (cd *ConfigDispatcher) DispatchVerificationRules(ctx context.Context, serviceName string, verificationConfig *v1alpha1.VerificationRule) error { + agents, err := cd.activeAgentRetriever.GetServiceActiveAgents(serviceName) + if err != nil { + return fmt.Errorf("unable to retrieve active agents for service %s: %w", serviceName, err) + } + + for _, agent := range agents { + url := fmt.Sprintf("http://%s:%d%s", agent.IpAddress, agent.Port, verificationConfigWebhookEndpoint) + + jsonData, err := json.Marshal(verificationConfig) + if err != nil { + return fmt.Errorf("error marshaling verification config: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData)) + if err != nil { + return fmt.Errorf("error creating request for agent %s: %w", agent.IpAddress, err) + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := cd.httpClient.Do(req) + if err != nil { + return fmt.Errorf("error sending request to agent %s: %w", agent.IpAddress, err) + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("received non-ok status %d from agent %s", resp.StatusCode, agent.IpAddress) + } + } + + return nil +} + +func (cd *ConfigDispatcher) DispatchGenerationRule(ctx context.Context, generationRule *v1alpha1.GenerationRule) error { + return nil +} diff --git a/service/tconfigderrors/tconfigerrors.go b/service/tconfigderrors/tconfigerrors.go new file mode 100644 index 0000000..3320a46 --- /dev/null +++ b/service/tconfigderrors/tconfigerrors.go @@ -0,0 +1,7 @@ +package tconfigderrors + +import ( + "errors" +) + +var ErrNotFound = errors.New("not found") diff --git a/service/tratcontroller/controller.go b/service/tratcontroller/controller.go index f51411e..6d203d6 100644 --- a/service/tratcontroller/controller.go +++ b/service/tratcontroller/controller.go @@ -7,6 +7,8 @@ import ( "golang.org/x/time/rate" + "github.com/tratteria/tconfigd/configdispatcher" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,27 +29,38 @@ import ( listers "github.com/tratteria/tconfigd/tratcontroller/pkg/generated/listers/tratteria/v1alpha1" ) -const controllerAgentName = "trat-controller" +type Status string + +const ( + PendingStatus Status = "PENDING" + DoneStatus Status = "DONE" +) + +type Stage string const ( - SuccessApplied = "Applied" - MessageResourceApplied = "TraT applied successfully" + VerificationApplicationStage Stage = "verification application stage" + GenerationApplicationStage Stage = "generation application stage" ) +const controllerAgentName = "trat-controller" + type Controller struct { - kubeclientset kubernetes.Interface - sampleclientset clientset.Interface - tratsLister listers.TraTLister - tratsSynced cache.InformerSynced - workqueue workqueue.TypedRateLimitingInterface[string] - recorder record.EventRecorder + kubeclientset kubernetes.Interface + sampleclientset clientset.Interface + tratsLister listers.TraTLister + tratsSynced cache.InformerSynced + workqueue workqueue.TypedRateLimitingInterface[string] + recorder record.EventRecorder + configDispatcher *configdispatcher.ConfigDispatcher } func NewController( ctx context.Context, kubeclientset kubernetes.Interface, sampleclientset clientset.Interface, - tratInformer informers.TraTInformer) *Controller { + tratInformer informers.TraTInformer, + configDispatcher *configdispatcher.ConfigDispatcher) *Controller { logger := klog.FromContext(ctx) utilruntime.Must(samplescheme.AddToScheme(scheme.Scheme)) @@ -66,12 +79,13 @@ func NewController( ) controller := &Controller{ - kubeclientset: kubeclientset, - sampleclientset: sampleclientset, - tratsLister: tratInformer.Lister(), - tratsSynced: tratInformer.Informer().HasSynced, - workqueue: workqueue.NewTypedRateLimitingQueue(ratelimiter), - recorder: recorder, + kubeclientset: kubeclientset, + sampleclientset: sampleclientset, + tratsLister: tratInformer.Lister(), + tratsSynced: tratInformer.Informer().HasSynced, + workqueue: workqueue.NewTypedRateLimitingQueue(ratelimiter), + recorder: recorder, + configDispatcher: configDispatcher, } logger.Info("Setting up event handlers") @@ -158,7 +172,6 @@ func (c *Controller) syncHandler(ctx context.Context, key string) error { } trat, err := c.tratsLister.TraTs(namespace).Get(name) - if err != nil { if errors.IsNotFound(err) { utilruntime.HandleError(fmt.Errorf("trat '%s' in work queue no longer exists", key)) @@ -169,23 +182,101 @@ func (c *Controller) syncHandler(ctx context.Context, key string) error { return err } - err = c.updateTraTStatus(trat) + verificationRules, err := trat.GetVerificationRules() + if err != nil { + messagedErr := fmt.Errorf("error retrieving verification rules from %s trat: %w", name, err) + + c.recorder.Event(trat, corev1.EventTypeWarning, "error", messagedErr.Error()) + + if updateErr := c.updateErrorTraTStatus(ctx, trat, VerificationApplicationStage, err); updateErr != nil { + return fmt.Errorf("failed to update status for %s trat after verification rules retrieval error: %w", name, updateErr) + } + + return messagedErr + } + + // TODO: Implement parallel dispatching of rules using goroutines + for service, serviceVerificationRule := range verificationRules { + err := c.configDispatcher.DispatchVerificationRules(ctx, service, serviceVerificationRule) + if err != nil { + messagedErr := fmt.Errorf("error dispatching %s trat verification rule to %s service: %w", name, service, err) + + c.recorder.Event(trat, corev1.EventTypeWarning, "error", messagedErr.Error()) + + if updateErr := c.updateErrorTraTStatus(ctx, trat, VerificationApplicationStage, err); updateErr != nil { + return fmt.Errorf("failed to update status for %s trat after verification rules dispatch error: %w", name, updateErr) + } + + return messagedErr + } + } + + c.recorder.Event(trat, corev1.EventTypeNormal, string(VerificationApplicationStage)+" successful", string(VerificationApplicationStage)+" completed successfully") + generationRule, err := trat.GetGenerationRule() if err != nil { - return err + messagedErr := fmt.Errorf("error retrieving generation rules from %s trat: %w", name, err) + + c.recorder.Event(trat, corev1.EventTypeWarning, "error", messagedErr.Error()) + + if updateErr := c.updateErrorTraTStatus(ctx, trat, GenerationApplicationStage, err); updateErr != nil { + return fmt.Errorf("failed to update status for %s trat after generation rules retrieval error: %w", name, updateErr) + } + + return messagedErr + } + + err = c.configDispatcher.DispatchGenerationRule(ctx, generationRule) + if err != nil { + messagedErr := fmt.Errorf("error dispatching %s trat generation rule: %w", name, err) + + c.recorder.Event(trat, corev1.EventTypeWarning, "error", messagedErr.Error()) + + if updateErr := c.updateErrorTraTStatus(ctx, trat, GenerationApplicationStage, err); updateErr != nil { + return fmt.Errorf("failed to update status for %s trat after generation rule dispatch error: %w", name, updateErr) + } + + return messagedErr + } + + if updateErr := c.updateSuccessTratStatus(ctx, trat); updateErr != nil { + return fmt.Errorf("failed to update success status for %s trat: %w", name, updateErr) } - c.recorder.Event(trat, corev1.EventTypeNormal, SuccessApplied, MessageResourceApplied) + c.recorder.Event(trat, corev1.EventTypeNormal, string(GenerationApplicationStage)+" successful", string(GenerationApplicationStage)+" completed successfully") return nil } -func (c *Controller) updateTraTStatus(trat *samplev1alpha1.TraT) error { +func (c *Controller) updateErrorTraTStatus(ctx context.Context, trat *samplev1alpha1.TraT, stage Stage, err error) error { tratCopy := trat.DeepCopy() - tratCopy.Status.Applied = true - _, err := c.sampleclientset.TratteriaV1alpha1().TraTs(trat.Namespace).UpdateStatus(context.TODO(), tratCopy, metav1.UpdateOptions{}) - return err + tratCopy.Status.VerificationApplied = false + tratCopy.Status.GenerationApplied = false + tratCopy.Status.Status = string(PendingStatus) + tratCopy.Status.Retries += 1 + + if stage == GenerationApplicationStage { + tratCopy.Status.VerificationApplied = true + } + + tratCopy.Status.LastErrorMessage = err.Error() + + _, updateErr := c.sampleclientset.TratteriaV1alpha1().TraTs(trat.Namespace).UpdateStatus(ctx, tratCopy, metav1.UpdateOptions{}) + + return updateErr +} + +func (c *Controller) updateSuccessTratStatus(ctx context.Context, trat *samplev1alpha1.TraT) error { + tratCopy := trat.DeepCopy() + + tratCopy.Status.VerificationApplied = true + tratCopy.Status.GenerationApplied = true + tratCopy.Status.Status = string(DoneStatus) + + _, updateErr := c.sampleclientset.TratteriaV1alpha1().TraTs(trat.Namespace).UpdateStatus(ctx, tratCopy, metav1.UpdateOptions{}) + + return updateErr } func (c *Controller) enqueueTraT(obj interface{}) { diff --git a/service/tratcontroller/pkg/apis/tratteria/v1alpha1/types.go b/service/tratcontroller/pkg/apis/tratteria/v1alpha1/types.go index af8dfd3..9142319 100644 --- a/service/tratcontroller/pkg/apis/tratteria/v1alpha1/types.go +++ b/service/tratcontroller/pkg/apis/tratteria/v1alpha1/types.go @@ -1,6 +1,9 @@ package v1alpha1 import ( + "fmt" + + "github.com/tratteria/tconfigd/tconfigderrors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -16,19 +19,31 @@ type TraT struct { } type TraTSpec struct { - Path string `json:"path"` + Endpoint string `json:"endpoint"` Method string `json:"method"` - AzdMapping map[string]AzdField `json:"azd-mapping"` - Services []string `json:"services"` + Purp string `json:"purp"` + AzdMapping map[string]AzdField `json:"azdMapping,omitempty"` + Services []ServiceSpec `json:"services"` +} + +type ServiceSpec struct { + Name string `json:"name"` + Endpoint string `json:"endpoint,omitempty"` + AzdMapping AzdMapping `json:"azdMapping,omitempty"` } +type AzdMapping map[string]AzdField type AzdField struct { Required bool `json:"required"` Value string `json:"value"` } type TraTStatus struct { - Applied bool `json:"applied"` + VerificationApplied bool `json:"verificationApplied"` + GenerationApplied bool `json:"generationApplied"` + Status string `json:"status"` + LastErrorMessage string `json:"lastErrorMessage,omitempty"` + Retries int32 `json:"retries"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -39,3 +54,55 @@ type TraTList struct { Items []TraT `json:"items"` } + +type VerificationRule struct { + Endpoint string `json:"endpoint"` + Method string `json:"method"` + Purp string `json:"purp"` + AzdMapping AzdMapping `json:"azdmapping"` +} + +type GenerationRule struct { + Endpoint string `json:"endpoint"` + Method string `json:"method"` + Purp string `json:"purp"` + AzdMapping AzdMapping `json:"azdmapping"` +} + +func (trat *TraT) GetVerificationRules() (map[string]*VerificationRule, error) { + verificationRules := make(map[string]*VerificationRule) + + // TODO: do basic check and return err if failed + + for _, serviceSpec := range trat.Spec.Services { + endpoint := trat.Spec.Endpoint + azdMapping := trat.Spec.AzdMapping + + if serviceSpec.Endpoint != "" { + endpoint = serviceSpec.Endpoint + } + + if serviceSpec.AzdMapping != nil { + azdMapping = serviceSpec.AzdMapping + } + + verificationRules[serviceSpec.Name] = &VerificationRule{ + Endpoint: endpoint, + Method: trat.Spec.Method, + Purp: trat.Spec.Purp, + AzdMapping: azdMapping, + } + + } + + if len(verificationRules) == 0 { + return nil, fmt.Errorf("%w: verification rules for %s trat", tconfigderrors.ErrNotFound, trat.Name) + } + + return verificationRules, nil +} + +func (trat *TraT) GetGenerationRule() (*GenerationRule, error) { + + return &GenerationRule{}, nil +} diff --git a/service/tratcontroller/pkg/apis/tratteria/v1alpha1/zz_generated.deepcopy.go b/service/tratcontroller/pkg/apis/tratteria/v1alpha1/zz_generated.deepcopy.go index d786070..2c3e95c 100644 --- a/service/tratcontroller/pkg/apis/tratteria/v1alpha1/zz_generated.deepcopy.go +++ b/service/tratcontroller/pkg/apis/tratteria/v1alpha1/zz_generated.deepcopy.go @@ -41,6 +41,74 @@ func (in *AzdField) DeepCopy() *AzdField { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in AzdMapping) DeepCopyInto(out *AzdMapping) { + { + in := &in + *out = make(AzdMapping, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzdMapping. +func (in AzdMapping) DeepCopy() AzdMapping { + if in == nil { + return nil + } + out := new(AzdMapping) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GenerationRule) DeepCopyInto(out *GenerationRule) { + *out = *in + if in.AzdMapping != nil { + in, out := &in.AzdMapping, &out.AzdMapping + *out = make(AzdMapping, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenerationRule. +func (in *GenerationRule) DeepCopy() *GenerationRule { + if in == nil { + return nil + } + out := new(GenerationRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { + *out = *in + if in.AzdMapping != nil { + in, out := &in.AzdMapping, &out.AzdMapping + *out = make(AzdMapping, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSpec. +func (in *ServiceSpec) DeepCopy() *ServiceSpec { + if in == nil { + return nil + } + out := new(ServiceSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TraT) DeepCopyInto(out *TraT) { *out = *in @@ -114,8 +182,10 @@ func (in *TraTSpec) DeepCopyInto(out *TraTSpec) { } if in.Services != nil { in, out := &in.Services, &out.Services - *out = make([]string, len(*in)) - copy(*out, *in) + *out = make([]ServiceSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } @@ -145,3 +215,26 @@ func (in *TraTStatus) DeepCopy() *TraTStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VerificationRule) DeepCopyInto(out *VerificationRule) { + *out = *in + if in.AzdMapping != nil { + in, out := &in.AzdMapping, &out.AzdMapping + *out = make(AzdMapping, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VerificationRule. +func (in *VerificationRule) DeepCopy() *VerificationRule { + if in == nil { + return nil + } + out := new(VerificationRule) + in.DeepCopyInto(out) + return out +} diff --git a/service/tratcontroller/setup.go b/service/tratcontroller/setup.go index 07e205b..d9cd0a3 100644 --- a/service/tratcontroller/setup.go +++ b/service/tratcontroller/setup.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "github.com/tratteria/tconfigd/configdispatcher" + "github.com/tratteria/tconfigd/tratcontroller/pkg/signals" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -14,7 +16,9 @@ import ( informers "github.com/tratteria/tconfigd/tratcontroller/pkg/generated/informers/externalversions" ) -type TraTController struct{} +type TraTController struct { + ConfigDispatcher *configdispatcher.ConfigDispatcher +} func (tc *TraTController) Run() error { klog.InitFlags(nil) @@ -41,7 +45,7 @@ func (tc *TraTController) Run() error { exampleInformerFactory := informers.NewSharedInformerFactory(exampleClient, time.Second*30) - controller := NewController(ctx, kubeClient, exampleClient, exampleInformerFactory.Tratteria().V1alpha1().TraTs()) + controller := NewController(ctx, kubeClient, exampleClient, exampleInformerFactory.Tratteria().V1alpha1().TraTs(), tc.ConfigDispatcher) exampleInformerFactory.Start(ctx.Done())