Mercurial > gemma
view pkg/geoserver/boot.go @ 874:da526b58c9c4 geo-style
Added a flag geoserver-clean to force deletion of the gemma workspace before configuring the GeoServer again.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Sun, 30 Sep 2018 15:36:17 +0200 |
parents | aa8f30c1ed27 |
children | 371c756f0370 |
line wrap: on
line source
package geoserver import ( "bytes" "encoding/json" "fmt" "log" "net" "net/http" "net/url" "strings" "time" "gemma.intevation.de/gemma/pkg/config" "gemma.intevation.de/gemma/pkg/models" ) const ( workspaceName = "gemma" datastoreName = "gemma" databaseScheme = "waterway" databaseType = "postgis" ) const ( startupSQL = `SELECT public.setrole('${user,'||encode('waterway_user', 'hex')||'}')` closeupSQL = `RESET ROLE` ) func basicAuth(user, password string) func(req *http.Request) { return func(req *http.Request) { req.SetBasicAuth(user, password) } } func asJSON(req *http.Request) { req.Header.Set("Content-Type", "application/json") } func ensureWorkspace() error { var ( url = config.GeoServerURL() user = config.GeoServerUser() password = config.GeoServerPassword() auth = basicAuth(user, password) ) // Probe workspace. req, err := http.NewRequest( http.MethodGet, url+"/rest/workspaces/"+workspaceName+".json", nil) if err != nil { return err } auth(req) resp, err := http.DefaultClient.Do(req) if err != nil { return err } if resp.StatusCode != http.StatusNotFound { log.Println("info: workspace " + workspaceName + " already exists.") return nil } // Create workspace log.Println("info: creating workspace " + workspaceName) const createJSON = `{"workspace":{"name":"` + workspaceName + `"}}` req, err = http.NewRequest( http.MethodPost, url+"/rest/workspaces", strings.NewReader(createJSON)) if err != nil { return err } auth(req) asJSON(req) if resp, err = http.DefaultClient.Do(req); err != nil { return err } if resp.StatusCode != http.StatusCreated { err = fmt.Errorf("Status code '%s' (%d)", http.StatusText(resp.StatusCode), resp.StatusCode) } return err } func ensureDataStore() error { var ( url = config.GeoServerURL() user = config.GeoServerUser() password = config.GeoServerPassword() auth = basicAuth(user, password) ) // Probe datastore. req, err := http.NewRequest( http.MethodGet, url+"/rest/workspaces/"+workspaceName+"/datastores/"+datastoreName+".json", nil) if err != nil { return err } auth(req) resp, err := http.DefaultClient.Do(req) if err != nil { return err } if resp.StatusCode != http.StatusNotFound { log.Println("info: datastore " + datastoreName + " already exists.") return nil } // Create datastore. log.Println("info: creating datastore " + datastoreName) type entry struct { Key interface{} `json:"@key"` Value interface{} `json:"$"` } // Create datastore. ds := map[string]interface{}{ "dataStore": map[string]interface{}{ "name": datastoreName, "connectionParameters": map[string]interface{}{ "entry": []entry{ {"host", config.DBHost()}, {"port", config.DBPort()}, {"database", config.DBName()}, {"schema", databaseScheme}, {"user", config.DBUser()}, {"passwd", config.DBPassword()}, {"dbtype", databaseType}, {"Session startup SQL", startupSQL}, {"Session close-up SQL", closeupSQL}, }, }, }, } var out bytes.Buffer enc := json.NewEncoder(&out) if err := enc.Encode(&ds); err != nil { return err } req, err = http.NewRequest( http.MethodPost, url+"/rest/workspaces/"+workspaceName+"/datastores", bytes.NewReader(out.Bytes())) if err != nil { return err } auth(req) asJSON(req) resp, err = http.DefaultClient.Do(req) if err != nil { return err } if resp.StatusCode != http.StatusCreated { err = fmt.Errorf("Status code '%s' (%d)", http.StatusText(resp.StatusCode), resp.StatusCode) } return err } func ensureFeatures() error { var ( url = config.GeoServerURL() user = config.GeoServerUser() password = config.GeoServerPassword() auth = basicAuth(user, password) ) tables := models.InternalServices.Filter(models.IntWFS) if len(tables) == 0 { log.Println("info: no tables to publish") return nil } log.Printf("info: number of tables to publish %d\n", len(tables)) var features struct { FeatureTypes struct { FeatureType []struct { Name string `json:"name"` } `json:"featureType"` } `json:"featureTypes"` } hasFeature := func(name string) bool { for _, ft := range features.FeatureTypes.FeatureType { if ft.Name == name { return true } } return false } // Fetch all featuretypes. req, err := http.NewRequest( http.MethodGet, url+"/rest/workspaces/"+workspaceName+ "/datastores/"+datastoreName+ "/featuretypes.json", nil) if err != nil { return err } auth(req) resp, err := http.DefaultClient.Do(req) if err != nil { return err } err = json.NewDecoder(resp.Body).Decode(&features) resp.Body.Close() if err != nil { // XXX: Quirk in the JSON return by GeoServer: // If there are no features in the datastore // featureType deserializes to an empty string "" // instead of an empty array *palmface*. // So assume there no features. hasFeature = func(string) bool { return false } } for i := range tables { table := tables[i].Name if hasFeature(table) { log.Printf("info: featuretype %s already exists.\n", table) continue } // Create featuretype. log.Printf("info: creating featuretype %s.\n", table) // Create featuretype ft := map[string]interface{}{ "featureType": map[string]interface{}{ "name": table, "nativeName": table, "title": table, }, } var out bytes.Buffer enc := json.NewEncoder(&out) if err := enc.Encode(&ft); err != nil { return err } req, err := http.NewRequest( http.MethodPost, url+"/rest/workspaces/"+workspaceName+ "/datastores/"+datastoreName+ "/featuretypes", bytes.NewReader(out.Bytes())) if err != nil { return err } auth(req) asJSON(req) resp, err := http.DefaultClient.Do(req) if err != nil { return err } if resp.StatusCode != http.StatusCreated { return fmt.Errorf("Status code '%s' (%d)", http.StatusText(resp.StatusCode), resp.StatusCode) } } return nil } func deleteWorkspace() error { log.Println("info: delete workspace " + workspaceName) var ( url = config.GeoServerURL() user = config.GeoServerUser() password = config.GeoServerPassword() auth = basicAuth(user, password) ) req, err := http.NewRequest( http.MethodDelete, url+"/rest/workspaces/"+workspaceName+"?recurse=true", nil) if err != nil { return err } auth(req) _, err = http.DefaultClient.Do(req) return err } func prepareGeoServer() error { if config.DBUser() == "" { log.Println("info: Need metamorphic db user to configure GeoServer") return nil } if config.GeoServerURL() == "" { log.Println("info: No tables to publish on GeoServer") return nil } // Should we delete our workspace first? if config.GeoServerClean() { if err := deleteWorkspace(); err != nil { return err } } if err := ensureWorkspace(); err != nil { return err } if err := ensureDataStore(); err != nil { return err } // TODO: Styles return ensureFeatures() } func ConfigureBoot() { log.Println("Configure GeoServer...") const maxTries = 10 const sleep = time.Second * 5 for try := 1; try <= maxTries; try++ { err := prepareGeoServer() if err == nil { break } if try < maxTries { if uerr, ok := err.(*url.Error); ok { if oerr, ok := uerr.Err.(*net.OpError); ok && oerr.Op == "dial" { log.Printf("Failed attempt %d of %d to configure GeoServer. "+ "Will try again in %s...\n", try, maxTries, sleep) time.Sleep(sleep) continue } } } log.Printf("warn: configure GeoServer failed: %v\n", err) break } }