Skip to content
Merged
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
4 changes: 2 additions & 2 deletions hossted/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package hossted
import (
"crypto/tls"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
)
Expand Down Expand Up @@ -54,7 +54,7 @@ func (h *HosstedRequest) SendRequest() (string, error) {
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
Expand Down
8 changes: 4 additions & 4 deletions hossted/service/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ func HttpRequest(method, url, token string, body []byte) error {
Message interface{} `json:"message"`
}

if resp.StatusCode != 200 {
return fmt.Errorf("rrror sending event, errcode: %d", resp.StatusCode)
}

respBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
Expand All @@ -76,6 +72,10 @@ func HttpRequest(method, url, token string, body []byte) error {
return err
}

if resp.StatusCode != 200 {
return fmt.Errorf("error sending event, errcode: %d, msg: %s", resp.StatusCode, apiResponse.Message)
}

if !apiResponse.Success {
return fmt.Errorf("api response indicates failure: %v", apiResponse)
}
Expand Down
32 changes: 16 additions & 16 deletions hossted/service/compose/reconcile_compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ func writeComposeRequest2File(
return isComposeStateChange, nil
}

type optionsState struct {
Monitoring bool `json:"monitoring"`
Logging bool `json:"logging"`
CVEScan bool `json:"cve_scan"`
type OptionsState struct {
Monitoring bool `json:"monitoring,omitempty"`
Logging bool `json:"logging,omitempty"`
CVEScan bool `json:"cve_scan,omitempty"`
}

type URLInfo struct {
Expand All @@ -194,7 +194,7 @@ type AccessInfo struct {
URLs []URLInfo `json:"urls"`
}

type request struct {
type Request struct {
UUID string `json:"uuid"`
OsUUID string `json:"osuuid"`
OrgID string `json:"org_id"`
Expand All @@ -203,7 +203,7 @@ type request struct {
Product string `json:"product,omitempty"`
CPUNum string `json:"cpunum,omitempty"`
Memory string `json:"memory,omitempty"`
OptionsState optionsState `json:"options_state"`
OptionsState OptionsState `json:"options_state,omitempty"`
ComposeFile string `json:"compose_file,omitempty"`
AccessInfo AccessInfo `json:"access_info,omitempty"`
}
Expand Down Expand Up @@ -238,7 +238,7 @@ func sendComposeInfo(appFilePath string, osInfo OsInfo) error {
return err
}

accessInfo := getAccessInfo("/opt/" + projectName + "/.env")
accessInfo := GetAccessInfo("/opt/" + projectName + "/.env")

var data map[string]AppRequest
if err = json.Unmarshal(composeInfo, &data); err != nil {
Expand Down Expand Up @@ -276,7 +276,7 @@ func sendComposeInfo(appFilePath string, osInfo OsInfo) error {
// registerApplications registers all applications with the specified API URL.
func registerApplications(data map[string]AppRequest, osInfo OsInfo, accessInfo AccessInfo, cpu, mem, orgID, token, composeUrl string) error {
for appName, compose := range data {
newReq := request{
newReq := Request{
UUID: compose.AppAPIInfo.AppUUID,
OsUUID: compose.AppAPIInfo.OsUUID,
Email: compose.AppAPIInfo.EmailID,
Expand All @@ -286,15 +286,15 @@ func registerApplications(data map[string]AppRequest, osInfo OsInfo, accessInfo
Product: appName,
CPUNum: cpu,
Memory: mem,
OptionsState: optionsState{
OptionsState: OptionsState{
Monitoring: true,
Logging: true,
CVEScan: true,
},
ComposeFile: compose.AppInfo.ComposeFile,
}

if err := sendRequest("POST", composeUrl, token, newReq); err != nil {
if err := SendRequest("POST", composeUrl, token, newReq); err != nil {
return err
}
fmt.Printf("Successfully registered app [%s] with appID [%s]\n", appName, compose.AppAPIInfo.AppUUID)
Expand Down Expand Up @@ -325,7 +325,7 @@ func registerDockerInstances(data map[string]AppRequest, osInfo OsInfo, token, c
CreatedAt: ci.CreatedAt,
}

if err := sendRequest("POST", containersUrl, token, newDI); err != nil {
if err := SendRequest("POST", containersUrl, token, newDI); err != nil {
return err
}

Expand All @@ -345,7 +345,7 @@ func submitPatchRequest(osInfo OsInfo, compose map[string]AppRequest, accessInfo
applicationName = appName
}

newReq := request{
newReq := Request{
UUID: osInfo.AppUUID,
OsUUID: osInfo.OsUUID,
AccessInfo: accessInfo,
Expand All @@ -354,19 +354,19 @@ func submitPatchRequest(osInfo OsInfo, compose map[string]AppRequest, accessInfo
Product: applicationName,
CPUNum: cpu,
Memory: mem,
OptionsState: optionsState{
OptionsState: OptionsState{
Monitoring: true,
Logging: true,
CVEScan: false,
},
ComposeFile: composeFile,
}

return sendRequest(http.MethodPatch, composeUrl, osInfo.Token, newReq)
return SendRequest(http.MethodPatch, composeUrl, osInfo.Token, newReq)
}

// sendRequest handles HTTP requests for a given method, URL, token, and request body.
func sendRequest(method, url, token string, reqBody interface{}) error {
func SendRequest(method, url, token string, reqBody interface{}) error {
body, err := json.Marshal(reqBody)
if err != nil {
return fmt.Errorf("failed to marshal JSON: %v", err)
Expand Down Expand Up @@ -705,7 +705,7 @@ func runMonitoringCompose(monitoringEnable, osUUID, appUUID string) error {
return nil
}

func getAccessInfo(filepath string) *AccessInfo {
func GetAccessInfo(filepath string) *AccessInfo {
file, err := os.Open(filepath)
if err != nil {
return &AccessInfo{}
Expand Down
159 changes: 130 additions & 29 deletions hossted/setDomain.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"errors"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"

"github.com/hossted/cli/hossted/service/compose"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -82,6 +84,12 @@ func SetDomain(env, app, domain string) error {
fmt.Println("Hossted Platform Domain set successfully")

} else {

err = CheckHosstedAuthFiles()
if err != nil {
fmt.Println("Please run hossted activate -t compose, to activate the vm")
os.Exit(1)
}
if !HasContainerRunning() {
fmt.Println("The application still in configuration")
os.Exit(0)
Expand All @@ -94,23 +102,18 @@ func SetDomain(env, app, domain string) error {
return fmt.Errorf("\n\n%w", err)
}

err = AddDomainToMotd(domain)
err = ChangeMOTD(domain)
if err != nil {
return err
}

check := verifyInputFormat(domain, "domain")
if !check {
return fmt.Errorf("Invalid domain input. Expecting domain name (e.g. example.com).\nInput - %s\n", domain)
return fmt.Errorf("invalid domain input. Expecting domain name (e.g. example.com). input - %s", domain)
}

// Get .env file and appDir
appConfig, err := config.GetAppConfig(app)
if err != nil {
return err
}
appDir := appConfig.AppPath
envPath, err := getAppFilePath(appConfig.AppPath, ".env")
appDir := "/opt/" + app
envPath, err := getAppFilePath(appDir, ".env")
if err != nil {
return err
}
Expand Down Expand Up @@ -149,45 +152,143 @@ func SetDomain(env, app, domain string) error {
typeActivity := "set_domain"

sendActivityLog(env, uuid, fullCommand, options, typeActivity)

osInfo, err := compose.GetClusterInfo()
if err != nil {
return fmt.Errorf("error getting cluster info %s", err)
}

projectName, err := getProjectName()
if err != nil {
return fmt.Errorf("error getting project name %s", err)
}

accessInfo := compose.GetAccessInfo("/opt/" + projectName + "/.env")

err = submitPatchRequest(osInfo, *accessInfo)
if err != nil {
return fmt.Errorf("error submitting patch request %v", err)
}

return nil
}
return nil
}

// ChangeMOTD changes the content of the MOTD file, to match the set domain changes
// TODO: print status
// TODO: Allow domain to be something other than .com by changing the regex patten
func ChangeMOTD(domain string) error {
// submitPatchRequest sends a PATCH request with VM info for marketplace setups.
func submitPatchRequest(osInfo compose.OsInfo, accessInfo compose.AccessInfo) error {
composeUrl := osInfo.HosstedApiUrl + "/compose/hosts/" + osInfo.OsUUID

type req struct {
UUID string `json:"uuid"` // Application UUID
OsUUID string `json:"osuuid"` // Operating System UUID
AccessInfo compose.AccessInfo `json:"access_info"` // Access information for the VM
Type string `json:"type"` // Type of the request, e.g., "vm"
}

newReq := req{
UUID: osInfo.AppUUID,
OsUUID: osInfo.OsUUID,
AccessInfo: accessInfo,
Type: "vm",
}

return compose.SendRequest(http.MethodPatch, composeUrl, osInfo.Token, newReq)
}

func ChangeMOTD(domain string) error {
filepath := "/etc/motd"

// Read the file
b, err := readProtected(filepath)
if err != nil {
return fmt.Errorf("Can't read the /etc/motd file. Please check - %s and contact administrator.\n%w\n", filepath, err)
return fmt.Errorf("unable to read the /etc/motd file. Please check %s and contact administrator: %w", filepath, err)
}
content := string(b)

// Currently only .com is supported. Looking for line like
// Your ^[[01;32mgitbucket^[[0m is available under ^[[01;34m http://3.215.23.221.c.hossted.com ^[[0m
re, err := regexp.Compile(`(.*available under\s*.*https?:\/\/)(.*\.com)(.*)`)
// Match and update any URL that starts with https:// followed by a domain
re := regexp.MustCompile(`https://[\w\.\-]+\.\w+`)
updatedContent := re.ReplaceAllString(content, fmt.Sprintf("https://%s", domain))

if updatedContent == content {
return errors.New("no matching pattern found in /etc/motd. Please ensure the content is formatted correctly")
}

// Write the updated content back to the file
err = writeProtected(filepath, []byte(updatedContent))
if err != nil {
return err
return fmt.Errorf("failed to write to the /etc/motd file: %w", err)
}

matches := re.FindAllStringSubmatch(content, -1)
if len(matches) > 0 {
if len(matches[0]) == 4 {
new := matches[0][1] + domain + matches[0][3]
content = strings.Replace(content, matches[0][0], new, 1) // Replace the containing new with new string
}
return nil
}

// CheckHosstedAuthFiles checks if the files ~/.hossted/auth.json and ~/.hossted/authresp.json exist.
func CheckHosstedAuthFiles() error {
// Get the home directory
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("failed to get home directory: %w", err)
}

// Define the file paths
authFilePath := filepath.Join(homeDir, ".hossted", "auth.json")
authRespFilePath := filepath.Join(homeDir, ".hossted", "authresp.json")

// Check if auth.json exists
if _, err := os.Stat(authFilePath); os.IsNotExist(err) {
return fmt.Errorf("file %s does not exist", authFilePath)
}

// Check if authresp.json exists
if _, err := os.Stat(authRespFilePath); os.IsNotExist(err) {
return fmt.Errorf("file %s does not exist", authRespFilePath)
}

// Both files exist
return nil
}

func getSoftwarePath() (string, error) {
path := "/opt/hossted/run/software.txt"
if _, err := os.Stat(path); os.IsNotExist(err) {
return "", nil
} else {
return errors.New("No matching pattern in /etc/motd. Please check.\n")
return path, nil
}

// Write back to file
err = writeProtected(filepath, []byte(content))
}

func getProjectName() (string, error) {
path, err := getSoftwarePath()
if err != nil {
return err
fmt.Println("Error getting software path", err)
}

return nil
// its a market place VM, access info object will exist
if path == "/opt/hossted/run/software.txt" {
// read the file in this path
// file will have this convention - Linnovate-AWS-keycloak
// capture the last word ie keycloak in this case.
// and use this last work ie instead of osInfo.ProjectName
data, err := os.ReadFile(path)
if err != nil {
fmt.Println("Error reading file:", err)
return "", err
}

// The file will have the convention Linnovate-AWS-keycloak
// Capture the last word (i.e., keycloak in this case)
softwareName := strings.TrimSpace(string(data))
words := strings.Split(softwareName, "-")
if len(words) > 0 {
projectName := words[len(words)-1]
// Use this last word (i.e., keycloak) instead of osInfo.ProjectName
return projectName, nil
}
} else if path == "" {
fmt.Println("Contact Hossted support to add Access Info object")
return "", nil
}
return "", nil
}
2 changes: 1 addition & 1 deletion hossted/structMethod.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (c *Config) GetAppConfig(in string) (ConfigApplication, error) {
}
// Check if any matched
if ca.AppName == "" {
return ca, fmt.Errorf("No Config found for app - %s", in)
return ca, fmt.Errorf("no config found for app - %s", in)
}
return ca, nil
}
Expand Down
Loading