Skip to content

Commit

Permalink
Merge pull request #248 from kube-tarian/cloud-git-apis
Browse files Browse the repository at this point in the history
Cloud git apis
  • Loading branch information
vramk23 committed Oct 20, 2023
2 parents c10d4fe + 9f51fa5 commit ab3cb60
Show file tree
Hide file tree
Showing 29 changed files with 1,347 additions and 674 deletions.
10 changes: 10 additions & 0 deletions capten/agent/pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package agent

import (
"context"
"fmt"

"github.com/intelops/go-common/logging"
captenstore "github.com/kube-tarian/kad/capten/agent/pkg/capten-store"
Expand Down Expand Up @@ -49,3 +50,12 @@ func (a *Agent) Ping(ctx context.Context, request *agentpb.PingRequest) (*agentp
a.log.Infof("Ping request received")
return &agentpb.PingResponse{Status: agentpb.StatusCode_OK}, nil
}

func validateArgs(args ...string) error {
for _, arg := range args {
if len(arg) == 0 {
return fmt.Errorf("mandatory argument is empty")
}
}
return nil
}
225 changes: 210 additions & 15 deletions capten/agent/pkg/agent/plugin_git_apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,240 @@ import (
"context"
"fmt"

"github.com/google/uuid"
"github.com/intelops/go-common/credentials"
"github.com/kube-tarian/kad/capten/agent/pkg/pb/captenpluginspb"
)

const gitProjectEntityName = "gitproject"

func (a *Agent) AddGitProject(ctx context.Context, request *captenpluginspb.AddGitProjectRequest) (
*captenpluginspb.AddGitProjectResponse, error) {
if err := validateArgs(request.ProjectUrl, request.AccessToken); err != nil {
a.log.Infof("request validation failed", err)
return &captenpluginspb.AddGitProjectResponse{
Status: captenpluginspb.StatusCode_INVALID_ARGUMENT,
StatusMessage: "request validation failed",
}, nil
}

a.log.Infof("Add Git project %s request recieved", request.ProjectUrl)

id := uuid.New()
if err := a.storeGitProjectCredential(ctx, id.String(), request.AccessToken); err != nil {
return &captenpluginspb.AddGitProjectResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to add gitProject credential in vault",
}, nil
}

gitProject := captenpluginspb.GitProject{
Id: id.String(),
ProjectUrl: request.ProjectUrl,
Labels: request.Labels,
LastUpdateTime: request.LastUpdateTime,
}
if err := a.as.UpsertGitProject(&gitProject); err != nil {
a.log.Errorf("failed to store git project to DB, %v", err)
return &captenpluginspb.AddGitProjectResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to add gitProject in db",
}, nil
}

a.log.Infof("Git project %s added with id", request.ProjectUrl, id)
return &captenpluginspb.AddGitProjectResponse{
Status: captenpluginspb.StatusCode_NOT_FOUND,
StatusMessage: "not implemented",
}, fmt.Errorf("not implemented")
Id: id.String(),
Status: captenpluginspb.StatusCode_OK,
StatusMessage: "ok",
}, nil
}

func (a *Agent) UpdateGitProject(ctx context.Context, request *captenpluginspb.UpdateGitProjectRequest) (
*captenpluginspb.UpdateGitProjectResponse, error) {
if err := validateArgs(request.ProjectUrl, request.AccessToken, request.Id); err != nil {
a.log.Infof("request validation failed", err)
return &captenpluginspb.UpdateGitProjectResponse{
Status: captenpluginspb.StatusCode_INVALID_ARGUMENT,
StatusMessage: "request validation failed",
}, nil
}
a.log.Infof("Update Git project %s, %s request recieved", request.ProjectUrl, request.Id)

id, err := uuid.Parse(request.Id)
if err != nil {
a.log.Infof("request validation failed", err)
return &captenpluginspb.UpdateGitProjectResponse{
Status: captenpluginspb.StatusCode_INVALID_ARGUMENT,
StatusMessage: fmt.Sprintf("invalid uuid: %s", request.Id),
}, nil
}

if err := a.storeGitProjectCredential(ctx, request.Id, request.AccessToken); err != nil {
return &captenpluginspb.UpdateGitProjectResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to add gitProject credential in vault",
}, nil
}

gitProject := captenpluginspb.GitProject{
Id: id.String(),
ProjectUrl: request.ProjectUrl,
Labels: request.Labels,
LastUpdateTime: request.LastUpdateTime,
}
if err := a.as.UpsertGitProject(&gitProject); err != nil {
a.log.Errorf("failed to update gitProject in db, %v", err)
return &captenpluginspb.UpdateGitProjectResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to update gitProject in db",
}, nil
}

a.log.Infof("Git project %s, %s updated", request.ProjectUrl, request.Id)
return &captenpluginspb.UpdateGitProjectResponse{
Status: captenpluginspb.StatusCode_NOT_FOUND,
StatusMessage: "not implemented",
}, fmt.Errorf("not implemented")
Status: captenpluginspb.StatusCode_OK,
StatusMessage: "ok",
}, nil
}

func (a *Agent) DeleteGitProject(ctx context.Context, request *captenpluginspb.DeleteGitProjectRequest) (
*captenpluginspb.DeleteGitProjectResponse, error) {
if err := validateArgs(request.Id); err != nil {
a.log.Infof("request validation failed", err)
return &captenpluginspb.DeleteGitProjectResponse{
Status: captenpluginspb.StatusCode_INVALID_ARGUMENT,
StatusMessage: "request validation failed",
}, nil
}
a.log.Infof("Delete Git project %s request recieved", request.Id)

if err := a.as.DeleteGitProjectById(request.Id); err != nil {
a.log.Errorf("failed to delete gitProject from db, %v", err)
return &captenpluginspb.DeleteGitProjectResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to delete gitProject from db",
}, nil
}

a.log.Infof("Git project %s deleted", request.Id)
return &captenpluginspb.DeleteGitProjectResponse{
Status: captenpluginspb.StatusCode_NOT_FOUND,
StatusMessage: "not implemented",
}, fmt.Errorf("not implemented")
Status: captenpluginspb.StatusCode_OK,
StatusMessage: "ok",
}, nil
}

func (a *Agent) GetGitProjects(ctx context.Context, request *captenpluginspb.GetGitProjectsRequest) (
*captenpluginspb.GetGitProjectsResponse, error) {
a.log.Infof("Get Git projects request recieved")
res, err := a.as.GetGitProjects()
if err != nil {
a.log.Errorf("failed to get gitProjects from db, %v", err)
return &captenpluginspb.GetGitProjectsResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to fetch git projects",
}, nil
}

for _, r := range res {
accessToken, err := a.getGitProjectCredential(ctx, r.Id)
if err != nil {
a.log.Errorf("failed to get credential, %v", err)
return &captenpluginspb.GetGitProjectsResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to fetch git projects",
}, nil
}
r.AccessToken = accessToken
}

a.log.Infof("Found %d git projects", len(res))
return &captenpluginspb.GetGitProjectsResponse{
Status: captenpluginspb.StatusCode_NOT_FOUND,
StatusMessage: "not implemented",
}, fmt.Errorf("not implemented")
Status: captenpluginspb.StatusCode_OK,
StatusMessage: "successful",
Projects: res,
}, nil

}

func (a *Agent) GetGitProjectsForLabels(ctx context.Context, request *captenpluginspb.GetGitProjectsForLabelsRequest) (
*captenpluginspb.GetGitProjectsForLabelsResponse, error) {
if len(request.Labels) == 0 {
a.log.Infof("request validation failed")
return &captenpluginspb.GetGitProjectsForLabelsResponse{
Status: captenpluginspb.StatusCode_INVALID_ARGUMENT,
StatusMessage: "request validation failed",
}, nil
}
a.log.Infof("Get Git projects with labels %v request recieved", request.Labels)

res, err := a.as.GetGitProjectsByLabels(request.Labels)
if err != nil {
a.log.Errorf("failed to get gitProjects for labels from db, %v", err)
return &captenpluginspb.GetGitProjectsForLabelsResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to fetch git projects",
}, nil
}

for _, r := range res {
accessToken, err := a.getGitProjectCredential(ctx, r.Id)
if err != nil {
a.log.Errorf("failed to get credential, %v", err)
return &captenpluginspb.GetGitProjectsForLabelsResponse{
Status: captenpluginspb.StatusCode_INTERNAL_ERROR,
StatusMessage: "failed to fetch git projects",
}, nil
}
r.AccessToken = accessToken
}

a.log.Infof("Found %d git projects for lables %v", len(res), request.Labels)
return &captenpluginspb.GetGitProjectsForLabelsResponse{
Status: captenpluginspb.StatusCode_NOT_FOUND,
StatusMessage: "not implemented",
}, fmt.Errorf("not implemented")
Status: captenpluginspb.StatusCode_OK,
StatusMessage: "successful",
Projects: res,
}, nil
}

func (a *Agent) getGitProjectCredential(ctx context.Context, id string) (string, error) {
credPath := fmt.Sprintf("%s/%s/%s", credentials.GenericCredentialType, gitProjectEntityName, id)
credAdmin, err := credentials.NewCredentialAdmin(ctx)
if err != nil {
a.log.Audit("security", "storecred", "failed", "system", "failed to intialize credentials client for %s", credPath)
a.log.Errorf("failed to get crendential for %s, %v", credPath, err)
return "", err
}

cred, err := credAdmin.GetCredential(ctx, credentials.GenericCredentialType, gitProjectEntityName, id)
if err != nil {
a.log.Errorf("failed to get credential for %s, %v", credPath, err)
return "", err
}
return cred["accessToken"], nil
}

func (a *Agent) storeGitProjectCredential(ctx context.Context, id string, accessToken string) error {
credPath := fmt.Sprintf("%s/%s/%s", credentials.GenericCredentialType, gitProjectEntityName, id)
credAdmin, err := credentials.NewCredentialAdmin(ctx)
if err != nil {
a.log.Audit("security", "storecred", "failed", "system", "failed to intialize credentials client for %s", credPath)
a.log.Errorf("failed to store credential for %s, %v", credPath, err)
return err
}

credentialMap := map[string]string{
"accessToken": accessToken,
}
err = credAdmin.PutCredential(ctx, credentials.GenericCredentialType, gitProjectEntityName,
id, credentialMap)

if err != nil {
a.log.Audit("security", "storecred", "failed", "system", "failed to store crendential for %s", credPath)
a.log.Errorf("failed to store credential for %s, %v", credPath, err)
return err
}
a.log.Audit("security", "storecred", "success", "system", "credential stored for %s", credPath)
a.log.Infof("stored credential for entity %s", credPath)
return nil
}
122 changes: 122 additions & 0 deletions capten/agent/pkg/capten-store/git_projects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package captenstore

import (
"fmt"
"strings"

"github.com/gocql/gocql"
"github.com/kube-tarian/kad/capten/agent/pkg/pb/captenpluginspb"
"github.com/pkg/errors"
)

const (
insertGitProject = "INSERT INTO %s.GitProjects(id, project_url, labels, last_update_time) VALUES (?)"
insertGitProjectId = "INSERT INTO %s.GitProjects(id) VALUES (?)"
updateGitProjectById = "UPDATE %s.GitProjects SET %s WHERE id = ?"
deleteGitProjectById = "DELETE FROM %s.GitProjects WHERE id= ?"
selectAllGitProjects = "SELECT id, project_url, labels, last_update_time FROM %s.GitProjects"
selectAllGitProjectsByLabels = "SELECT id, project_url, labels, last_update_time FROM %s.GitProjects WHERE %s"
)

// labels CONTAINS ? OR labels CONTAINS ?

func (a *Store) UpsertGitProject(config *captenpluginspb.GitProject) error {
kvPairs, isEmptyUpdate := formUpdateKvPairsForGitProject(config)
batch := a.client.Session().NewBatch(gocql.LoggedBatch)
batch.Query(fmt.Sprintf(insertGitProjectId, a.keyspace), config.Id)
if !isEmptyUpdate {
batch.Query(fmt.Sprintf(updateGitProjectById, a.keyspace, kvPairs), config.Id)
}
return a.client.Session().ExecuteBatch(batch)
}

func (a *Store) DeleteGitProjectById(id string) error {
deleteAction := a.client.Session().Query(fmt.Sprintf(deleteGitProjectById,
a.keyspace), id)
err := deleteAction.Exec()
if err != nil {
return err
}
return nil
}

func (a *Store) GetGitProjects() ([]*captenpluginspb.GitProject, error) {
query := fmt.Sprintf(selectAllGitProjects, a.keyspace)
return a.executeSelectQuery(query)
}

func (a *Store) GetGitProjectsByLabels(searchLabels []string) ([]*captenpluginspb.GitProject, error) {
if len(searchLabels) == 0 {
return nil, fmt.Errorf("searchLabels empty")
}

labelContains := []string{}
for _, label := range searchLabels {
labelContains = append(labelContains, fmt.Sprintf("labels CONTAINS '%s'", label))
}
whereLabelsClause := strings.Join(labelContains, " OR ")
whereLabelsClause += " ALLOW FILTERING"
query := fmt.Sprintf(selectAllGitProjectsByLabels, a.keyspace, whereLabelsClause)
return a.executeSelectQuery(query)
}

func (a *Store) executeSelectQuery(query string) ([]*captenpluginspb.GitProject, error) {
selectQuery := a.client.Session().Query(query)
iter := selectQuery.Iter()

config := captenpluginspb.GitProject{}
var labels []string

ret := make([]*captenpluginspb.GitProject, 0)
for iter.Scan(
&config.Id, &config.ProjectUrl,
&labels, &config.LastUpdateTime,
) {
labelsTmp := make([]string, len(labels))
copy(labelsTmp, labels)
gitProject := &captenpluginspb.GitProject{
Id: config.Id,
ProjectUrl: config.ProjectUrl,
Labels: labelsTmp,
LastUpdateTime: config.LastUpdateTime,
}
ret = append(ret, gitProject)
}

if err := iter.Close(); err != nil {
return nil, errors.WithMessage(err, "failed to iterate through results:")
}

return ret, nil
}

func formUpdateKvPairsForGitProject(config *captenpluginspb.GitProject) (kvPairs string, emptyUpdate bool) {
params := []string{}

if config.ProjectUrl != "" {
params = append(params,
fmt.Sprintf("project_url = '%s'", config.ProjectUrl))
}

// comma separated labels, change this later
if len(config.Labels) > 0 {
labels := []string{}
for _, label := range config.Labels {
labels = append(labels, fmt.Sprintf("'%s'", label))
}
param := "{" + strings.Join(labels, ", ") + "}"
params = append(params,
fmt.Sprintf("labels = %v", param))
}

if (config.LastUpdateTime) != "" {
params = append(params,
fmt.Sprintf("last_update_time = '%v'", config.LastUpdateTime))
}

if len(params) == 0 {
// query is empty there is nothing to update
return "", true
}
return strings.Join(params, ", "), false
}
Loading

0 comments on commit ab3cb60

Please sign in to comment.