diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6d9a1b4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +# Dockerfile for Spring Boot (Java) +FROM eclipse-temurin:17-jdk-focal + +WORKDIR /app + +COPY .mvn/ .mvn +COPY mvnw pom.xml ./ +RUN ./mvnw dependency:go-offline + +COPY src ./src + +CMD ["./mvnw", "spring-boot:run"] diff --git a/defaults/constant.go b/defaults/constant.go index 668f113..c23ed6e 100644 --- a/defaults/constant.go +++ b/defaults/constant.go @@ -57,3 +57,5 @@ const GITHUB_USERNAME_PLACEHOLDER = "GITHUB_USERNAME" const DELEGATE_NAME_PLACEHOLDER = "DELEGATE_NAME" const GITHUB_PAT_PLACEHOLDER = "GITHUB-PAT" const HARNESS_PROD_URL = "https://app.harness.io/" + +const TEMPFILEPATH = "project_info.tmp" diff --git a/deploy.go b/deploy.go index 9b24969..eeca47f 100644 --- a/deploy.go +++ b/deploy.go @@ -1,12 +1,210 @@ package main import ( + "bufio" "fmt" + "github.com/fatih/color" "github.com/urfave/cli/v2" + "harness/defaults" + "os" + "strings" ) -func deploy(c *cli.Context) error { +func DeployProject(c *cli.Context) error { + color.Set(color.FgYellow) fmt.Println("Deploying project...") - // Implement deploy functionality here + color.Unset() + + framework, language, err := loadProjectInfo() + if err != nil { + return err + } + + fmt.Printf("Loaded framework: %s\n", framework) + fmt.Printf("Loaded language: %s\n", language) + + hasDockerfile, err := checkDockerfile() + if err != nil { + return err + } + + if !hasDockerfile { + fmt.Print("No Dockerfile found. Would you like to create one? (y/n) : ") + var response string + fmt.Scanln(&response) + + if response == "y" { + err = createDockerfile(framework, language) + if err != nil { + return err + } + color.Set(color.FgGreen) + fmt.Println("Dockerfile created.") + color.Unset() + hasDockerfile = true + } + } + + err = saveDockerfileInfo(hasDockerfile) + if err != nil { + return err + } + + fmt.Print("Do you want to proceed deploying using Harness? (y/n): ") + var proceed string + fmt.Scanln(&proceed) + + if proceed != "y" { + fmt.Println("Deployment aborted.") + return nil + } + + orgName := promptUser("Org Name (default)", "default") + projectName := promptUser("Project Name (default_project)", "default_project") + + _, err = CheckOrgExistsAndCreate(c, orgName) + if err != nil { + return err + } + _, err = CheckProjectExistsAndCreate(c, orgName, projectName) + if err != nil { + return err + } + + //CheckConnectorExistsAndCreate(c, orgName, projectName) + + fmt.Println("Deployment process initialized.") + + // Further deployment steps would go here + + return nil +} + +func promptUser(prompt, defaultValue string) string { + fmt.Printf("%s: ", prompt) + reader := bufio.NewReader(os.Stdin) + input, _ := reader.ReadString('\n') + input = strings.TrimSpace(input) + + if input == "" { + return defaultValue + } + return input +} + +func loadProjectInfo() (string, string, error) { + file, err := os.Open(defaults.TEMPFILEPATH) + if err != nil { + return "", "", fmt.Errorf("failed to open temp file: %v", err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + var framework, language string + + for scanner.Scan() { + line := scanner.Text() + parts := strings.Split(line, "=") + if len(parts) == 2 { + switch parts[0] { + case "framework": + framework = parts[1] + case "language": + language = parts[1] + } + } + } + + if err := scanner.Err(); err != nil { + return "", "", fmt.Errorf("failed to read temp file: %v", err) + } + + return framework, language, nil +} + +func checkDockerfile() (bool, error) { + if _, err := os.Stat("Dockerfile"); err == nil { + return true, nil + } else if os.IsNotExist(err) { + return false, nil + } else { + return false, fmt.Errorf("failed to check Dockerfile: %v", err) + } +} + +func createDockerfile(framework, language string) error { + // TODO : uncomment this block + if framework != "Spring Boot" || language != "Java" { + return fmt.Errorf("unsupported framework or language: %s (%s)", framework, language) + } + + file, err := os.Create("Dockerfile") + if err != nil { + return fmt.Errorf("failed to create Dockerfile: %v", err) + } + defer file.Close() + + _, err = file.WriteString(`# Dockerfile for Spring Boot (Java) +FROM eclipse-temurin:17-jdk-focal + +WORKDIR /app + +COPY .mvn/ .mvn +COPY mvnw pom.xml ./ +RUN ./mvnw dependency:go-offline + +COPY src ./src + +CMD ["./mvnw", "spring-boot:run"] +`) + if err != nil { + return fmt.Errorf("failed to write Dockerfile: %v", err) + } + + return nil +} + +func saveDockerfileInfo(hasDockerfile bool) error { + file, err := os.Open(defaults.TEMPFILEPATH) + if err != nil { + return fmt.Errorf("failed to open temp file: %v", err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + var lines []string + dockerfileExists := false + for scanner.Scan() { + line := scanner.Text() + parts := strings.Split(line, "=") + if len(parts) == 2 && parts[0] == "dockerfile" { + lines = append(lines, fmt.Sprintf("dockerfile=%t", hasDockerfile)) + dockerfileExists = true + } else { + lines = append(lines, line) + } + } + + if err := scanner.Err(); err != nil { + return fmt.Errorf("failed to read temp file: %v", err) + } + + if !dockerfileExists { + lines = append(lines, fmt.Sprintf("dockerfile=%t", hasDockerfile)) + } + + file, err = os.OpenFile(defaults.TEMPFILEPATH, os.O_WRONLY|os.O_TRUNC, 0600) + if err != nil { + return fmt.Errorf("failed to open temp file for writing: %v", err) + } + defer file.Close() + + for _, line := range lines { + _, err = file.WriteString(line + "\n") + if err != nil { + return fmt.Errorf("failed to write to temp file: %v", err) + } + } + return nil } diff --git a/globals/globals.go b/globals/globals.go new file mode 100644 index 0000000..c1660a2 --- /dev/null +++ b/globals/globals.go @@ -0,0 +1,8 @@ +package globals + +var ( + ApiKey string + AccountId string + BaseURL string + UserId string +) diff --git a/init.go b/init.go index c08f954..9bf84a8 100644 --- a/init.go +++ b/init.go @@ -9,9 +9,10 @@ import ( "time" "github.com/urfave/cli/v2" + "harness/defaults" ) -var tempFilePath = "project_info.tmp" +//var tempFilePath = "project_info.tmp" func InitProject(c *cli.Context) error { @@ -101,7 +102,7 @@ func detectProjectFramework() (string, string) { } func saveProjectInfo(framework, language string) error { - file, err := os.Create(tempFilePath) + file, err := os.Create(defaults.TEMPFILEPATH) if err != nil { return fmt.Errorf("failed to create temp file: %v", err) } diff --git a/main.go b/main.go index b065981..cd5ff5c 100644 --- a/main.go +++ b/main.go @@ -1,17 +1,28 @@ package main import ( + "encoding/json" "fmt" "github.com/fatih/color" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" "harness/auth" + "harness/defaults" + "harness/globals" . "harness/share" + "harness/utils" "os" ) -var Version = "Harness CLI : Hack Week 24" +var Version = "Harness CLI : Hack Week 24 🎉" + +type UserDataLoad struct { + ApiKey string `json:"apiKey"` + AccountId string `json:"accountId"` + BaseURL string `json:"baseUrl"` + UserId string `json:"userId"` +} var asciiArt = ` @@ -64,15 +75,23 @@ func main() { Name: "init", Usage: "Initialize Harness CLI in the project", Action: func(context *cli.Context) error { - return cliWrapper(func(context *cli.Context) error { - return InitProject(context) - }, context) + if err := LoadCredentials(); err != nil { + fmt.Println(err, "\nPlease log in first.") + return auth.Login(context) + } + return cliWrapper(InitProject, context) }, }, { - Name: "deploy", - Usage: "Deploy the project using Harness", - Action: deploy, + Name: "deploy", + Usage: "Deploy the project using Harness", + Action: func(context *cli.Context) error { + if err := LoadCredentials(); err != nil { + fmt.Println(err, "\nPlease log in first.") + return auth.Login(context) + } + return cliWrapper(DeployProject, context) + }, }, }, } @@ -96,3 +115,21 @@ func cliWrapper(fn cliFnWrapper, ctx *cli.Context) error { func beforeAction(globalFlags []cli.Flag) { altsrc.InitInputSourceWithContext(globalFlags, altsrc.NewYamlSourceFromFlagFunc("load")) } + +func LoadCredentials() error { + exactFilePath := utils.GetUserHomePath() + "/" + defaults.SECRETS_STORE_PATH + credsJson, err := os.ReadFile(exactFilePath) + if err != nil { + return fmt.Errorf("error reading creds file: %v", err) + } + var UserData UserDataLoad + err = json.Unmarshal(credsJson, &UserData) + if err != nil { + return fmt.Errorf("error unmarshaling creds file: %v", err) + } + globals.ApiKey = UserData.ApiKey + globals.AccountId = UserData.AccountId + globals.BaseURL = UserData.BaseURL + globals.UserId = UserData.UserId + return nil +} diff --git a/netclient/netclient.go b/netclient/netclient.go index 8bcaaf9..d13bfdd 100644 --- a/netclient/netclient.go +++ b/netclient/netclient.go @@ -122,3 +122,64 @@ func AuthHeaderKey(auth string) string { } return "x-api-key" } + +func GetNew(reqUrl string, auth string) (respBodyObj ResponseBody, err error) { + req, err := http.NewRequest("GET", reqUrl, nil) + if err != nil { + return + } + log.WithFields(log.Fields{ + "url": reqUrl, + }).Trace("The request details") + req.Header.Set("Content-Type", "application/json") + req.Header.Set(AuthHeaderKey(auth), auth) + return handleResp(req) +} + +func PostNew(reqUrl string, auth string, body interface{}) (respBodyObj ResponseBody, err error) { + postBody, _ := json.Marshal(body) + requestBody := bytes.NewBuffer(postBody) + log.WithFields(log.Fields{ + "url": reqUrl, + "body": string(postBody), + }).Trace("The request details") + req, err := http.NewRequest("POST", reqUrl, requestBody) + if err != nil { + return + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set(AuthHeaderKey(auth), auth) + return handleRespNew(req) +} + +func handleRespNew(req *http.Request) (respBodyObj ResponseBody, err error) { + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return + } + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return + } + log.WithFields(log.Fields{ + "body": string(respBody), + }).Trace("The response body") + err = json.Unmarshal(respBody, &respBodyObj) + if err != nil { + log.Fatalln("There was error while parsing the response from server. Exiting...", err) + } + if resp.StatusCode != 200 { + if len(respBodyObj.Message) > 0 { + log.Error(respBodyObj.Message) + } else if len(respBodyObj.Messages) > 0 && len(respBodyObj.Messages[0].Message) > 0 { + log.Error(respBodyObj.Messages[0].Message) + } + return respBodyObj, errors.New("received non 200 response code. The response code was " + strconv.Itoa(resp.StatusCode)) + } + + return respBodyObj, nil +}