Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uid key (not finished but this is the changes I have made so far) #561

Closed
wants to merge 10 commits into from
66 changes: 29 additions & 37 deletions api/agent/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"strings"

"github.com/google/uuid"
trustdomain "github.com/spiffe/spire-api-sdk/proto/spire/api/server/trustdomain/v1"
"google.golang.org/protobuf/encoding/protojson"
)
Expand Down Expand Up @@ -913,106 +914,97 @@ func (s *Server) clusterCreate(w http.ResponseWriter, r *http.Request) {
buf := new(strings.Builder)
n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error parsing data: %v", err.Error()), http.StatusBadRequest)
return
}
data := buf.String()

var input RegisterClusterRequest
if n == 0 {
input = RegisterClusterRequest{}
} else {
err := json.Unmarshal([]byte(data), &input)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error parsing data: %v", err.Error()), http.StatusBadRequest)
return
}
}

input.UID = uuid.New().String()

err = s.DefineCluster(input)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error: %v", err.Error()), http.StatusBadRequest)
return
}
cors(w, r)
_, err = w.Write([]byte("SUCCESS"))
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}
json.NewEncoder(w).Encode(map[string]string{"uid": input.UID, "message": "Cluster created successfully"})
}

func (s *Server) clusterEdit(w http.ResponseWriter, r *http.Request) {
buf := new(strings.Builder)
n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error parsing data: %v", err.Error()), http.StatusBadRequest)
return
}
data := buf.String()

var input EditClusterRequest
if n == 0 {
input = EditClusterRequest{}
} else {
err := json.Unmarshal([]byte(data), &input)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error parsing data: %v", err.Error()), http.StatusBadRequest)
return
}
}
err = s.EditCluster(input)

// Check that the UID exists in the database
_, err = s.Db.GetClusterByUID(input.UID)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error: Cluster not found: %v", err.Error()), http.StatusBadRequest)
return
}
cors(w, r)
_, err = w.Write([]byte("SUCCESS"))

err = s.EditCluster(input)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error: %v", err.Error()), http.StatusBadRequest)
return
}
cors(w, r)
w.Write([]byte("SUCCESS"))
}

func (s *Server) clusterDelete(w http.ResponseWriter, r *http.Request) {
buf := new(strings.Builder)
n, err := io.Copy(buf, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error parsing data: %v", err.Error()), http.StatusBadRequest)
return
}
data := buf.String()

var input DeleteClusterRequest
if n == 0 {
input = DeleteClusterRequest{}
} else {
err := json.Unmarshal([]byte(data), &input)
if err != nil {
emsg := fmt.Sprintf("Error parsing data: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error parsing data: %v", err.Error()), http.StatusBadRequest)
return
}
}
err = s.DeleteCluster(input)

// Delete by UID
err = s.Db.DeleteClusterEntry(input.UID)
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
retError(w, fmt.Sprintf("Error: %v", err.Error()), http.StatusBadRequest)
return
}
cors(w, r)
_, err = w.Write([]byte("SUCCESS"))
if err != nil {
emsg := fmt.Sprintf("Error: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}

w.Write([]byte("SUCCESS"))
}

/********* END CLUSTER *********/
4 changes: 2 additions & 2 deletions api/agent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ func (s *Server) GetRouter() http.Handler {
apiRtr.HandleFunc("/api/v1/spire/entries", s.entryList).Methods(http.MethodGet, http.MethodOptions)
apiRtr.HandleFunc("/api/v1/spire/entries", s.entryCreate).Methods(http.MethodPost)
apiRtr.HandleFunc("/api/v1/spire/entries", s.entryDelete).Methods(http.MethodDelete)

// SPIRE server bundles
apiRtr.HandleFunc("/api/v1/spire/bundle", s.bundleGet).Methods(http.MethodGet, http.MethodOptions)
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleList).Methods(http.MethodGet, http.MethodOptions)
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleCreate).Methods(http.MethodPost)
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleUpdate).Methods(http.MethodPatch)
apiRtr.HandleFunc("/api/v1/spire/federations/bundles", s.federatedBundleDelete).Methods(http.MethodDelete)

// SPIRE server federations
apiRtr.HandleFunc("/api/v1/spire/federations", s.federationList).Methods(http.MethodGet, http.MethodOptions)
apiRtr.HandleFunc("/api/v1/spire/federations", s.federationCreate).Methods(http.MethodPost)
Expand Down
33 changes: 26 additions & 7 deletions api/agent/tornjak_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"errors"

"github.com/google/uuid"
tornjakTypes "github.com/spiffe/tornjak/pkg/agent/types"
)

Expand Down Expand Up @@ -83,37 +84,55 @@ func (s *Server) ListClusters(inp ListClustersRequest) (*ListClustersResponse, e
return (*ListClustersResponse)(&retVal), nil
}

type RegisterClusterRequest tornjakTypes.ClusterInput

// DefineCluster registers cluster to local DB
// DefineCluster registers a cluster to the local DB
func (s *Server) DefineCluster(inp RegisterClusterRequest) error {
cinfo := tornjakTypes.ClusterInfo(inp.ClusterInstance)

// Validation for mandatory fields
if len(cinfo.Name) == 0 {
return errors.New("cluster definition missing mandatory field - Name")
} else if len(cinfo.PlatformType) == 0 {
return errors.New("cluster definition missing mandatory field - PlatformType")
} else if len(cinfo.EditedName) > 0 {
return errors.New("cluster definition attempts renaming on create cluster - EditedName")
}

// Generate UID for the cluster
cinfo.UID = uuid.New().String()

return s.Db.CreateClusterEntry(cinfo)
}

type EditClusterRequest tornjakTypes.ClusterInput

// EditCluster registers cluster to local DB
// EditCluster registers updates to a cluster in the local DB
func (s *Server) EditCluster(inp EditClusterRequest) error {
cinfo := tornjakTypes.ClusterInfo(inp.ClusterInstance)

// Validation for mandatory fields
if len(cinfo.Name) == 0 {
return errors.New("cluster definition missing mandatory field - Name")
} else if len(cinfo.PlatformType) == 0 {
return errors.New("cluster definition missing mandatory field - PlatformType")
} else if len(cinfo.EditedName) == 0 {
return errors.New("cluster definition missing mandatory field - EditedName")
}
return s.Db.EditClusterEntry(cinfo)
}

type DeleteClusterRequest tornjakTypes.ClusterInput
// Retrieve existing cluster by UID to ensure it exists
existingCluster, err := s.Db.GetClusterByUID(cinfo.UID)
if err != nil {
return errors.New("cluster not found in database")
}

// Update the cluster fields
existingCluster.Name = cinfo.Name
existingCluster.PlatformType = cinfo.PlatformType
existingCluster.ManagedBy = cinfo.ManagedBy
existingCluster.DomainName = cinfo.DomainName
existingCluster.AgentsList = cinfo.AgentsList

return s.Db.EditClusterEntry(existingCluster)
}

// DeleteCluster deletes cluster with name cinfo.Name and assignment to agents
func (s *Server) DeleteCluster(inp DeleteClusterRequest) error {
Expand Down
27 changes: 22 additions & 5 deletions api/agent/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ type SPIREConfig struct {
Plugins ast.Node `hcl:"plugins"`
}

// RegisterClusterRequest represents the request for creating a cluster
type RegisterClusterRequest struct {
// ClusterInstance ClusterInfo `json:"cluster_instance"`
UID string `json:"uid"`
}

// EditClusterRequest represents the request for editing a cluster
type EditClusterRequest struct {
// ClusterInstance ClusterInfo `json:"cluster_instance"`
UID string `json:"uid"`
}

// DeleteClusterRequest represents the request for deleting a cluster
type DeleteClusterRequest struct {
UID string `json:"uid"`
}

type TornjakConfig struct {
Server *serverConfig `hcl:"server"`
Plugins *ast.Node `hcl:"plugins"`
Expand Down Expand Up @@ -123,14 +140,14 @@ type AuthRole struct {
}

type APIv1RoleMapping struct {
Name string `hcl:",key"`
Method string `hcl:"-"`
Path string `hcl:"-"`
Name string `hcl:",key"`
Method string `hcl:"-"`
Path string `hcl:"-"`
AllowedRoles []string `hcl:"allowed_roles"`
}

type pluginAuthorizerRBAC struct {
Name string `hcl:"name"`
RoleList []*AuthRole `hcl:"role,block"`
Name string `hcl:"name"`
RoleList []*AuthRole `hcl:"role,block"`
APIv1RoleMappings []*APIv1RoleMapping `hcl:"APIv1,block"`
}
3 changes: 1 addition & 2 deletions api/manager/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,13 @@ func (s *Server) apiServerProxyFunc(apiPath string, apiMethod string) func(w htt
return
}

req, err := http.NewRequest(apiMethod, strings.TrimSuffix(sinfo.Address, "/") + apiPath, r.Body)
req, err := http.NewRequest(apiMethod, strings.TrimSuffix(sinfo.Address, "/")+apiPath, r.Body)
if err != nil {
emsg := fmt.Sprintf("Error creating http request: %v", err.Error())
retError(w, emsg, http.StatusBadRequest)
return
}


resp, err := client.Do(req)
if err != nil {
emsg := fmt.Sprintf("Error making api call to server: %v", err.Error())
Expand Down
6 changes: 4 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"tss-react": "^4.8.4",
"typescript": "5.0.3",
"url-join": "^4.0.1",
"web-vitals": "^0.2.4"
"web-vitals": "^0.2.4",
"uuid": "^9.0.0"
},
"overrides": {
"svgo": "3.0.2",
Expand Down Expand Up @@ -105,7 +106,8 @@
"react-inject-env": "^2.1.0",
"react-test-renderer": "^18.2.0",
"redux-mock-store": "^1.5.4",
"markdownlint-cli2": "^0.7.1"
"markdownlint-cli2": "^0.7.1",
"@types/uuid": "^9.0.0"
},
"jest": {
"moduleNameMapper": {
Expand Down
46 changes: 24 additions & 22 deletions frontend/src/components/cluster-create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import TornjakApi from './tornjak-api-helpers';
import { clusterType } from '../data/data';
import { ToastContainer } from 'react-toastify';
import './style.css';
import { v4 as uuidv4 } from 'uuid';
import {
clusterTypeInfoFunc,
serverSelectedFunc,
Expand Down Expand Up @@ -232,13 +233,14 @@ class ClusterCreate extends Component<ClusterCreateProp, ClusterCreateState> {

var cjtData = {
cluster: {
name: this.state.clusterName,
platformType: this.state.clusterType,
domainName: this.state.clusterDomainName,
managedBy: this.state.clusterManagedBy,
agentsList: this.state.clusterAgentsList ? this.state.clusterAgentsList : []
uid: uniqueID,
name: this.state.clusterName,
platformType: this.state.clusterType,
domainName: this.state.clusterDomainName,
managedBy: this.state.clusterManagedBy,
agentsList: this.state.clusterAgentsList ? this.state.clusterAgentsList : []
}
}
}

let endpoint = this.getApiEntryCreateEndpoint()

Expand All @@ -247,20 +249,20 @@ class ClusterCreate extends Component<ClusterCreateProp, ClusterCreateState> {
}

axios.post(endpoint, cjtData)
.then(
res => {
this.setState({
message: "Request:" + JSON.stringify(cjtData, null, ' ') + "\n\nSuccess:" + JSON.stringify(res.data, null, ' '),
statusOK: "OK",
})
}
)
.catch(err => showResponseToast(err))
.then(
res => {
this.setState({
message: "Request:" + JSON.stringify(cjtData, null, ' ') + "\n\nSuccess:" + JSON.stringify(res.data, null, ' '),
statusOK: "OK",
})
}
)
.catch(err => showResponseToast(err))
//scroll to bottom of page after submission
setTimeout(() => {
window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' });
window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' });
}, 100);
}
}

render() {
const ClusterType = this.props.clusterTypeList;
Expand Down Expand Up @@ -375,14 +377,14 @@ class ClusterCreate extends Component<ClusterCreateProp, ClusterCreateState> {
hideCloseButton
title="CLUSTER SUCCESSFULLY CREATED"
subtitle={
<div className="toast-messege" data-test="alert-primary">
<pre className="toast-messege-color">
{this.state.message}
</pre>
<div className="toast-message" data-test="alert-primary">
<pre className="toast-message-color">
{this.state.message}
</pre>
<p>Unique ID: {cjtData.cluster.uid}</p>
</div>
}
/>
}
{(this.state.statusOK === "ERROR") &&
<InlineNotification
kind="error"
Expand Down
1 change: 1 addition & 0 deletions pkg/agent/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type AgentDB interface {
// CLUSTER interface
GetClusters() (types.ClusterInfoList, error)
CreateClusterEntry(cinfo types.ClusterInfo) error
GetClusterByUID(uid string) (types.ClusterInfo, error)
EditClusterEntry(cinfo types.ClusterInfo) error
DeleteClusterEntry(name string) error

Expand Down
Loading