Browse Source

cleaning up authorizedDomains & serving out tenantInfo for desktop app

master
forest 4 months ago
parent
commit
0c830370ee
5 changed files with 77 additions and 61 deletions
  1. +54
    -35
      backend.go
  2. +2
    -0
      db_model.go
  3. +6
    -8
      gandi_service.go
  4. +8
    -18
      public_api.go
  5. +7
    -0
      threshold_provisioning_service.go

+ 54
- 35
backend.go View File

@ -38,7 +38,6 @@ type BackendApp struct {
Model *DBModel
DigitalOcean *DigitalOceanService
Gandi *GandiService
FreeSubdomainDomain string
BackblazeB2 *BackblazeB2Service
SSH *SSHService
ThresholdProvisioning *ThresholdProvisioningService
@ -55,6 +54,19 @@ type ThresholdMetrics struct {
OutboundByTenant map[string]int64
}
type ThresholdTenantInfo struct {
ClientStates map[string]ThresholdClientState
Listeners []ThresholdTunnel
AuthorizedDomains []string
PortStart int
PortEnd int
}
type ThresholdClientState struct {
CurrentState string
LastState string
}
type VPSInstanceWithTenants struct {
TenantSettings map[string]TunnelSettings
VPSInstance *VPSInstance
@ -114,8 +126,7 @@ func initBackend(
EmailService: emailService,
Model: model,
DigitalOcean: NewDigitalOceanService(config),
Gandi: NewGandiService(config, freeSubdomainDomain),
FreeSubdomainDomain: freeSubdomainDomain,
Gandi: NewGandiService(config),
BackblazeB2: NewBackblazeB2Service(config),
SSH: NewSSHService(config),
ThresholdProvisioning: NewThresholdProvisioningService(config, pkiService, config.AdminTenantId, adminThresholdNodeId, greenhouseThresholdServiceId),
@ -299,7 +310,12 @@ func (app *BackendApp) ConsumeMetrics() error {
return nil
}
func (app *BackendApp) GetActiveNodeIdsForTenant(tenantId int) (map[string]bool, error) {
func (app *BackendApp) GetTenantInfo(tenantId int) (*ThresholdTenantInfo, error) {
tenant, err := app.Model.GetTenant(tenantId)
if err != nil {
return nil, err
}
vpsInstances, err := app.Model.GetVPSInstances()
if err != nil {
@ -320,28 +336,20 @@ func (app *BackendApp) GetActiveNodeIdsForTenant(tenantId int) (map[string]bool,
if hasVpsInstance && row.TenantId == tenantId && row.Active {
actions = append(actions, func() taskResult {
result := []string{}
responseBytes, err := app.MyHTTP200(
"GET",
fmt.Sprintf("https://%s:%d/clientStates?tenantId=%d", vpsInstance.IPV4, app.ThresholdManagementPort, tenantId),
fmt.Sprintf("https://%s:%d/tenantInfo?tenantId=%d", vpsInstance.IPV4, app.ThresholdManagementPort, tenantId),
nil,
nil,
)
clientStates := map[string]struct{ CurrentState string }{}
tenantInfo := ThresholdTenantInfo{}
if err == nil {
err = json.Unmarshal(responseBytes, &clientStates)
}
if err == nil {
for nodeId, clientState := range clientStates {
if clientState.CurrentState == "ClientConnected" {
result = append(result, nodeId)
}
}
err = json.Unmarshal(responseBytes, &tenantInfo)
}
return taskResult{
Name: row.GetVPSInstanceId(),
Err: err,
Result: result,
Result: tenantInfo,
}
})
}
@ -350,21 +358,40 @@ func (app *BackendApp) GetActiveNodeIdsForTenant(tenantId int) (map[string]bool,
results := doInParallel(false, actions...)
errorStrings := []string{}
resultMap := map[string]bool{}
toReturn := ThresholdTenantInfo{
ClientStates: map[string]ThresholdClientState{},
Listeners: []ThresholdTunnel{},
AuthorizedDomains: tenant.TunnelSettings.AuthorizedDomains,
PortStart: tenant.TunnelSettings.PortStart,
PortEnd: tenant.TunnelSettings.PortEnd,
}
listenersToReturn := map[string]ThresholdTunnel{}
for _, result := range results {
if result.Err != nil {
errorStrings = append(errorStrings, fmt.Sprintf("error getting active nodeIds for tenant %d from %s: %+v", tenantId, result.Name, result.Err))
} else {
for _, nodeId := range result.Result.([]string) {
resultMap[nodeId] = true
tenantInfo := result.Result.(ThresholdTenantInfo)
// deduplicate ClientStates from multiple threshold servers, preferring the connected state
for clientNodeId, state := range tenantInfo.ClientStates {
alreadyExistingState, hasAlreadyExistingState := toReturn.ClientStates[clientNodeId]
if !hasAlreadyExistingState || alreadyExistingState.CurrentState != "ClientConnected" {
toReturn.ClientStates[clientNodeId] = state
}
}
// deduplicate listeners lists from multiple threshold servers via map
for _, listener := range tenantInfo.Listeners {
listenersToReturn[listener.String()] = listener
}
}
}
for _, listener := range listenersToReturn {
toReturn.Listeners = append(toReturn.Listeners, listener)
}
if len(errorStrings) > 0 {
return nil, errors.Errorf("ConsumeMetrics() doInParallel(actions...): %s\n\n", strings.Join(errorStrings, "\n"))
}
return resultMap, nil
return &toReturn, nil
}
func (app *BackendApp) GetInstances() (map[string]*VPSInstance, map[string]*VPSInstance, map[string]*VPSInstance, error) {
@ -923,16 +950,6 @@ func (app *BackendApp) Rebalance() (bool, error) {
}
}
authorizeFreeSubdomain := func(tenant *TenantInfo) *TunnelSettings {
if tenant.Subdomain != "" {
tenant.TunnelSettings.AuthorizedDomains = append(
tenant.TunnelSettings.AuthorizedDomains,
fmt.Sprintf("%s.%s", tenant.Subdomain, app.FreeSubdomainDomain),
)
}
return tenant.TunnelSettings
}
// Add both workingAllocations and pinned to newConfig
for instanceId, instanceAllocations := range workingAllocations {
for tenantId := range instanceAllocations {
@ -940,7 +957,8 @@ func (app *BackendApp) Rebalance() (bool, error) {
newConfig[instanceId] = map[int]*TunnelSettings{}
}
if !tenants[tenantId].Deactivated {
newConfig[instanceId][tenantId] = authorizeFreeSubdomain(tenants[tenantId])
// TODO handle other authorized domains
newConfig[instanceId][tenantId] = tenants[tenantId].TunnelSettings
}
}
}
@ -950,7 +968,8 @@ func (app *BackendApp) Rebalance() (bool, error) {
newConfig[instanceId] = map[int]*TunnelSettings{}
}
if !tenants[tenantId].Deactivated {
newConfig[instanceId][tenantId] = authorizeFreeSubdomain(tenants[tenantId])
// TODO handle other authorized domains
newConfig[instanceId][tenantId] = tenants[tenantId].TunnelSettings
}
}
}
@ -1053,7 +1072,7 @@ func (app *BackendApp) WriteAdminTenantThresholdConfig() error {
for tenantId, tenant := range tenants {
if tenantId == app.AdminTenantId {
clientConfig, err := app.ThresholdProvisioning.GetClientConfig(
app.AdminTenantId, fmt.Sprintf("%s.%s", tenant.Subdomain, app.FreeSubdomainDomain), adminThresholdNodeId, "api_key_n_a",
app.AdminTenantId, fmt.Sprintf("%s.%s", tenant.Subdomain, freeSubdomainDomain), adminThresholdNodeId, "api_key_n_a",
)
if err != nil {
return err
@ -1108,14 +1127,14 @@ func (app *BackendApp) ConfigureAdminTenantOnThresholdServer() error {
ClientId: fmt.Sprintf("%d.%s", app.AdminTenantId, app.AdminThresholdNodeId),
ListenPort: 80,
ListenAddress: "0.0.0.0",
ListenHostnameGlob: fmt.Sprintf("%s.%s", tenant.Subdomain, app.FreeSubdomainDomain),
ListenHostnameGlob: fmt.Sprintf("%s.%s", tenant.Subdomain, freeSubdomainDomain),
BackEndService: app.GreenhouseThresholdServiceId,
},
ThresholdTunnel{
ClientId: fmt.Sprintf("%d.%s", app.AdminTenantId, app.AdminThresholdNodeId),
ListenPort: 443,
ListenAddress: "0.0.0.0",
ListenHostnameGlob: fmt.Sprintf("%s.%s", tenant.Subdomain, app.FreeSubdomainDomain),
ListenHostnameGlob: fmt.Sprintf("%s.%s", tenant.Subdomain, freeSubdomainDomain),
BackEndService: app.GreenhouseThresholdServiceId,
},
})


+ 2
- 0
db_model.go View File

@ -545,6 +545,7 @@ func (model *DBModel) GetTenants() (map[int]*TenantInfo, error) {
subdomainString := ""
if subdomain != nil {
subdomainString = *subdomain
thisTenantDomains = append(thisTenantDomains, fmt.Sprintf("%s.%s", subdomainString, freeSubdomainDomain))
}
toReturn[tenantId] = &TenantInfo{
@ -617,6 +618,7 @@ func (model *DBModel) GetTenant(tenantId int) (*TenantInfo, error) {
subdomainString := ""
if subdomain != nil {
subdomainString = *subdomain
authorizedDomains = append(authorizedDomains, fmt.Sprintf("%s.%s", subdomainString, freeSubdomainDomain))
}
smsString := ""
if smsAlarmNumber != nil {


+ 6
- 8
gandi_service.go View File

@ -12,9 +12,8 @@ import (
type GandiService struct {
BaseHTTPService
APIUrl string
APIKey string
FreeSubdomainDomain string
APIUrl string
APIKey string
}
type GandiDomainRecords struct {
@ -29,11 +28,10 @@ type GandiDomainRecord struct {
TTL int `json:"rrset_ttl"`
}
func NewGandiService(config *Config, freeSubdomainDomain string) *GandiService {
func NewGandiService(config *Config) *GandiService {
toReturn := &GandiService{
APIUrl: "https://api.gandi.net/v5",
APIKey: config.GandiAPIKey,
FreeSubdomainDomain: "greenhouseusers.com",
APIUrl: "https://api.gandi.net/v5",
APIKey: config.GandiAPIKey,
}
toReturn.ClientFactory = func() (*http.Client, *time.Time, error) {
@ -62,7 +60,7 @@ func (service *GandiService) UpdateFreeSubdomains(freeSubdomains map[string][]st
return err
}
endpoint := fmt.Sprintf("/livedns/domains/%s/records", service.FreeSubdomainDomain)
endpoint := fmt.Sprintf("/livedns/domains/%s/records", freeSubdomainDomain)
responseBytes, err := service.GandiHTTP("PUT", endpoint, bytes.NewBuffer(requestBodyBytes))
if err != nil {
log.Printf("Failed to update free subdomains on gandi: %s \n\nResponse was: %s\n\n", err, string(responseBytes))


+ 8
- 18
public_api.go View File

@ -11,31 +11,21 @@ import (
func AddAPIRoutesToFrontend(app *FrontendApp) {
handleWithAPIToken(app, "/api/try_register", func(responseWriter http.ResponseWriter, request *http.Request, user Session) {
if request.Method != "POST" {
responseWriter.Header().Add("Allow", "POST")
http.Error(responseWriter, "405 Method Not Allowed, try POST", http.StatusMethodNotAllowed)
return
}
newNodeId := request.URL.Query().Get("serverName")
if newNodeId == "" {
http.Error(responseWriter, "404 Not Found, a server name must be provided, like /api/try_register?serverName=my_server", http.StatusNotFound)
return
}
activeNodeIds, err := app.Backend.GetActiveNodeIdsForTenant(user.TenantId)
handleWithAPIToken(app, "/api/tenant_info", func(responseWriter http.ResponseWriter, request *http.Request, user Session) {
tenantInfo, err := app.Backend.GetTenantInfo(user.TenantId)
if err != nil {
app.unhandledError(responseWriter, err)
return
}
nodeIdIsAlreadyActive := activeNodeIds[newNodeId]
if nodeIdIsAlreadyActive {
http.Error(responseWriter, fmt.Sprintf("400 bad request, a server named '%s' is already connected", newNodeId), http.StatusBadRequest)
tenantInfoBytes, err := json.Marshal(tenantInfo)
if err != nil {
app.unhandledError(responseWriter, err)
return
}
responseWriter.Write([]byte("ok"))
responseWriter.Header().Set("Content-Type", "application/json")
responseWriter.Write(tenantInfoBytes)
})
handleWithAPIToken(app, "/api/client_config", func(responseWriter http.ResponseWriter, request *http.Request, user Session) {
@ -56,7 +46,7 @@ func AddAPIRoutesToFrontend(app *FrontendApp) {
return
}
clientConfig, err := app.Backend.ThresholdProvisioning.GetClientConfig(
user.TenantId, fmt.Sprintf("%s.%s", tenant.Subdomain, app.Backend.FreeSubdomainDomain), newNodeId, user.APIToken,
user.TenantId, fmt.Sprintf("%s.%s", tenant.Subdomain, freeSubdomainDomain), newNodeId, user.APIToken,
)
if err != nil {
app.unhandledError(responseWriter, err)


+ 7
- 0
threshold_provisioning_service.go View File

@ -34,6 +34,13 @@ type ThresholdTunnel struct {
HaProxyProxyProtocol bool
}
func (tunnel ThresholdTunnel) String() string {
return fmt.Sprintf(
"%s:%d (%s) -> %s (%s,HaProxyProxyProtocol=%t)",
tunnel.ListenAddress, tunnel.ListenPort, tunnel.ListenHostnameGlob, tunnel.ClientId, tunnel.BackEndService, tunnel.HaProxyProxyProtocol,
)
}
type ThresholdClientConfig struct {
ClientId string
GreenhouseDomain string


Loading…
Cancel
Save