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

Add delete remote repo endpoint for locally created application #256

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions lib/factory/application_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package factory

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"

"strings"

"github.com/google/go-github/v41/github"
"github.com/sdslabs/gasper/configs"
Expand Down Expand Up @@ -125,7 +131,7 @@ func NewApplicationFactory(bindings pb.ApplicationFactoryServer) *grpc.Server {
}

// CreateGithubRepository returns a git clone URL after creating a new repository
func CreateGithubRepository(repoName string) (*types.RepositoryResponse, error) {
func CreateGithubRepository(repoName string) (*types.ApplicationRemote, error) {
tc := oauth2.NewClient(
context.Background(),
oauth2.StaticTokenSource(
Expand All @@ -140,12 +146,37 @@ func CreateGithubRepository(repoName string) (*types.RepositoryResponse, error)
Private: github.Bool(true),
}
repo, _, err := client.Repositories.Create(context.Background(), "", repo)
response := &types.RepositoryResponse{
CloneURL: *repo.CloneURL,
PAT: configs.GithubConfig.PAT,
Username: configs.GithubConfig.Username,
Repository: repoName,
Email: configs.GithubConfig.Email,
response := &types.ApplicationRemote{
GitURL: *repo.CloneURL,
}
return response, err
}

// DeleteGithubRepository deletes the specified repository
func DeleteGithubRepository(repoURL string) (bool, error) {
tc := oauth2.NewClient(
context.Background(),
oauth2.StaticTokenSource(
&oauth2.Token{
AccessToken: configs.GithubConfig.PAT, //PAT
},
),
)
parts := strings.Split(repoURL, "/")
repoName := strings.TrimSuffix(parts[len(parts)-1], ".git")
client := github.NewClient(tc)
_, err := client.Repositories.Delete(context.Background(), configs.GithubConfig.Username, repoName)
if err != nil {
return false, err
}
return true, nil
}

// Encrypt encrypts the PAT using the public key
func Encrypt(key rsa.PublicKey) (string, error) {
label := []byte("OAEP Encrypted")
rng := rand.Reader
secretMessage := configs.GithubConfig.PAT
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(secretMessage), label)
return base64.StdEncoding.EncodeToString(ciphertext), err
}
22 changes: 22 additions & 0 deletions services/master/controllers/application.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package controllers

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strconv"
Expand Down Expand Up @@ -303,3 +305,23 @@ func FetchMetrics(c *gin.Context) {
"data": metricsRecord,
})
}

// FetchAppRemote retrieves the remote URL of an application
func FetchAppRemote(c *gin.Context) {
appName := c.Param("app")
config, err := mongo.FetchSingleApp(appName)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
}

response := &types.ApplicationRemote{
GitURL: config.Git.RepoURL,
}

responseBody := new(bytes.Buffer)
json.NewEncoder(responseBody).Encode(response)
c.Data(200, "application/json", responseBody.Bytes())
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line

63 changes: 63 additions & 0 deletions services/master/controllers/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
_ "io/ioutil"

"github.com/gin-gonic/gin"
"github.com/sdslabs/gasper/configs"
"github.com/sdslabs/gasper/lib/factory"
"github.com/sdslabs/gasper/lib/utils"
"github.com/sdslabs/gasper/services/master/middlewares"
Expand Down Expand Up @@ -47,3 +48,65 @@ func CreateRepository(c *gin.Context) {
json.NewEncoder(responseBody).Encode(response)
c.Data(200, "application/json", responseBody.Bytes())
}

func FetchPAT(c *gin.Context) {
raw, err := c.GetRawData()
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
}
var data *types.EncryptKey= &types.EncryptKey{}
err = json.Unmarshal(raw, data)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
}
encryptedPAT, err := factory.Encrypt(data.PublicKey)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
}
response := &types.AccessToken{
PAT: encryptedPAT,
Username: configs.GithubConfig.Username,
Email: configs.GithubConfig.Email,
}
responseBody := new(bytes.Buffer)
json.NewEncoder(responseBody).Encode(response)
c.Data(200, "application/json", responseBody.Bytes())
}

func DeleteRepository(c *gin.Context){
raw, err := c.GetRawData()
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
}
var data *types.ApplicationRemote = &types.ApplicationRemote{}
err = json.Unmarshal(raw, data)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
}
success, err := factory.DeleteGithubRepository(data.GitURL)
if !success {
c.AbortWithStatusJSON(400, gin.H{
"success": false,
"error": err.Error(),
})
} else {
c.JSON(200, gin.H{
"success": true,
})
}
}
9 changes: 8 additions & 1 deletion services/master/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ func NewService() http.Handler {

router.GET("/instances", m.AuthRequired(), c.FetchAllInstancesByUser)
router.POST("/gctllogin", m.JWTGctl.MiddlewareFunc(), c.GctlLogin)
router.POST("/github", m.AuthRequired(), c.CreateRepository)

github := router.Group("/github")
{
github.POST("", m.AuthRequired(), c.CreateRepository)
github.POST("/token", m.AuthRequired(), c.FetchPAT)
github.POST("/delete", m.AuthRequired(), c.DeleteRepository)
}

app := router.Group("/apps")
app.Use(m.AuthRequired())
Expand All @@ -72,6 +78,7 @@ func NewService() http.Handler {
app.PATCH("/:app/transfer/:user", m.IsAppOwner, c.TransferApplicationOwnership)
app.GET("/:app/term", m.IsAppOwner, c.DeployWebTerminal)
app.GET("/:app/metrics", m.IsAppOwner, c.FetchMetrics)
app.GET("/:app/remote", m.IsAppOwner, c.FetchAppRemote)
}

db := router.Group("/dbs")
Expand Down
26 changes: 20 additions & 6 deletions types/application.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package types

import (
"crypto/rsa"
"fmt"
"math"
"time"
Expand Down Expand Up @@ -56,16 +57,14 @@ type Resources struct {
CPU float64 `json:"cpu" bson:"cpu" valid:"float~Field 'cpu' inside field 'resources' should be of type float"`
}

//RepositoryRequest is the request body for containing name of the application for repository creation
type RepositoryRequest struct {
Name string `json:"name" bson:"name" valid:"required~Field 'name' is required but was not provided,alphanum~Field 'name' should only have alphanumeric characters,stringlength(3|40)~Field 'name' should have length between 3 to 40 characters,lowercase~Field 'name' should have only lowercase characters"`
}

type RepositoryResponse struct {
CloneURL string `json:"cloneurl" bson:"cloneurl"`
PAT string `json:"pat" bson:"pat"`
Username string `json:"username" bson:"username"`
Repository string `json:"repository" bson:"repository"`
Email string `json:"email" bson:"email"`
//EncryptKey is the request body for containing public key for encrypting the access token
type EncryptKey struct {
PublicKey rsa.PublicKey `json:"public_key"`
}

// ApplicationConfig is the configuration required for creating an application
Expand Down Expand Up @@ -93,6 +92,21 @@ type ApplicationConfig struct {
Success bool `json:"success,omitempty" bson:"-"`
}

// ApplicationRemote is the struct containing the remote URL of the application's git repository
type ApplicationRemote struct {
GitURL string `json:"giturl" bson:"giturl"`
}

//AccessToken is the struct containing the access token for the application's git repository
type AccessToken struct {
// PAT for pushing code to repository
PAT string `json:"pat" bson:"pat"`
// Username of Gasper Github user
Username string `json:"username" bson:"username"`
// Email id of Gasper Github user
Email string `json:"email" bson:"email"`
}

// GetName returns the application's name
func (app *ApplicationConfig) GetName() string {
return app.Name
Expand Down