Skip to content

Commit

Permalink
info() implementation, auth handling
Browse files Browse the repository at this point in the history
  • Loading branch information
pierotofy committed Jan 9, 2019
1 parent a8f39bb commit eca83fd
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 45 deletions.
39 changes: 39 additions & 0 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package cmd

import (
"fmt"
"mime"
"os"
"path/filepath"
"strconv"
Expand All @@ -25,11 +26,13 @@ import (
"github.com/OpenDroneMap/CloudODM/internal/config"
"github.com/OpenDroneMap/CloudODM/internal/fs"
"github.com/OpenDroneMap/CloudODM/internal/logger"
"github.com/OpenDroneMap/CloudODM/internal/odm"

"github.com/spf13/cobra"
)

var outputPath string
var nodeName string

var rootCmd = &cobra.Command{
Use: "odm [flags] <images> [<gcp>] [parameters]",
Expand All @@ -43,13 +46,35 @@ var rootCmd = &cobra.Command{
}

inputFiles, options := parseArgs(args)
inputFiles = filterImagesAndText(inputFiles)

logger.Verbose("Input Files (" + strconv.Itoa(len(inputFiles)) + ")")
for _, file := range inputFiles {
logger.Debug(" * " + file)
}

logger.Debug("Options: " + strings.Join(options, " "))

node, err := config.User.GetNode(nodeName)
if err != nil {
logger.Error(err)
}

info, err := node.Info()
err = node.CheckAuthorization(err)
if err != nil {
if err == odm.ErrAuthRequired {
logger.Debug("AUTH")
}
logger.Error(err)
}

// Check max images
if len(inputFiles) > info.MaxImages {
logger.Error("Cannot process", len(inputFiles), "files with this node, the node has a limit of", info.MaxImages)
}

logger.Debug("NodeODM version: " + info.Version)
},

TraverseChildren: true,
Expand All @@ -69,6 +94,7 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&logger.VerboseFlag, "verbose", "v", false, "show verbose output")
rootCmd.PersistentFlags().BoolVarP(&logger.DebugFlag, "debug", "d", false, "show debug output")
rootCmd.Flags().StringVarP(&outputPath, "output", "o", "./output", "directory where to store processing results")
rootCmd.Flags().StringVarP(&nodeName, "node", "n", "default", "Processing node to use")
rootCmd.Flags().SetInterspersed(false)
}

Expand Down Expand Up @@ -99,3 +125,16 @@ func parseArgs(args []string) ([]string, []string) {

return inputFiles, options
}

func filterImagesAndText(files []string) []string {
var result []string

for _, file := range files {
mimeType := mime.TypeByExtension(filepath.Ext(file))
if strings.HasPrefix(mimeType, "image") || strings.HasPrefix(mimeType, "text") {
result = append(result, file)
}
}

return result
}
59 changes: 48 additions & 11 deletions internal/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package config

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/url"
"os"
"path/filepath"
"strconv"

"github.com/OpenDroneMap/CloudODM/internal/fs"
"github.com/OpenDroneMap/CloudODM/internal/logger"
"github.com/OpenDroneMap/CloudODM/internal/odm"

homedir "github.com/mitchellh/go-homedir"
)
Expand All @@ -21,30 +24,23 @@ var User Configuration
// NewConfiguration creates a new configuration from a specified file path
func NewConfiguration(filePath string) Configuration {
conf := Configuration{}
conf.Nodes = map[string]Node{}
conf.Nodes = map[string]odm.Node{}
conf.filePath = filePath
return conf
}

// Save saves the configuration to file
func (c Configuration) Save() {
saveToFile(c, c.filePath)
}

// Configuration is a collection of config values
type Configuration struct {
Nodes map[string]Node `json:"nodes"`
Nodes map[string]odm.Node `json:"nodes"`

filePath string
}

type Node struct {
Url string `json:"url"`
Token string `json:"token"`
}

func (n Node) String() string {
return n.Url
}

// Initialize the configuration
func Initialize() {
// Find home directory.
Expand Down Expand Up @@ -118,3 +114,44 @@ func loadFromFile(filePath string) Configuration {

return conf
}

// AddNode adds a new node to the configuration
func (c Configuration) AddNode(name string, nodeURL string) error {
if _, ok := c.Nodes[name]; ok {
return errors.New("node" + name + " already exists. Remove it first.")
}

u, err := url.ParseRequestURI(nodeURL)
if err != nil {
return errors.New(nodeURL + " is not a valid URL. A valid URL looks like: http://hostname:port/?token=optional")
}

c.Nodes[name] = odm.Node{URL: u.Scheme + "://" + u.Host, Token: u.Query().Get("token")}
c.Save()

return nil
}

// RemoveNode removes a node from the configuration
func (c Configuration) RemoveNode(name string) bool {
_, ok := c.Nodes[name]
if ok {
delete(c.Nodes, name)
c.Save()
}
return ok
}

// GetNode gets a Node instance given its name
func (c Configuration) GetNode(name string) (*odm.Node, error) {
if len(c.Nodes) == 0 {
return nil, errors.New("No nodes. Add one with ./odm node")
}

node, ok := c.Nodes[name]
if !ok {
return nil, errors.New("node: " + name + " does not exist")
}

return &node, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestNodes(t *testing.T) {
if value.Token != "123" {
t.Error("Token should be 123")
}
if value.Url != "https://localhost:8080" {
if value.URL != "https://localhost:8080" {
t.Error("URL is not properly set")
}

Expand All @@ -46,4 +46,12 @@ func TestNodes(t *testing.T) {
if c.RemoveNode("test1") {
t.Error("Function should have returned false (already deleted)")
}

if _, err := c.GetNode("default"); err != nil {
t.Error("Cannot get default node")
}

if _, err := c.GetNode("nonexistant"); err == nil {
t.Error("Can get nonexistant node")
}
}
33 changes: 0 additions & 33 deletions internal/config/nodes.go

This file was deleted.

8 changes: 8 additions & 0 deletions internal/config/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"nodes": {
"default": {
"url": "http://localhost",
"token": ""
}
}
}
124 changes: 124 additions & 0 deletions internal/odm/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package odm

import (
"encoding/json"
"errors"
"io/ioutil"
"math"
"net/http"
"net/url"
"strconv"
"strings"

"github.com/OpenDroneMap/CloudODM/internal/logger"
)

// ErrUnauthorized means a response was not authorized
var ErrUnauthorized = errors.New("Unauthorized")

// ErrAuthRequired means authorization is required
var ErrAuthRequired = errors.New("Auth Required")

type InfoResponse struct {
Version string `json:"version"`
MaxImages int `json:"maxImages"`

Error string `json:"error"`
}

// Node is a NodeODM processing node
type Node struct {
URL string `json:"url"`
Token string `json:"token"`

_debugUnauthorized bool
}

func (n Node) String() string {
return n.URL
}

// URLFor builds a URL path
func (n Node) URLFor(path string) string {
u, err := url.ParseRequestURI(n.URL + path)
if err != nil {
return ""
}
q := u.Query()
if len(n.Token) > 0 {
q.Add("token", n.Token)
}
if n._debugUnauthorized {
q.Add("_debugUnauthorized", "1")
}
u.RawQuery = q.Encode()
return u.String()
}

func (n Node) apiGET(path string) ([]byte, error) {
url := n.URLFor(path)
logger.Debug("GET: " + url)

resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode == 401 {
return nil, ErrUnauthorized
}
if resp.StatusCode != 200 {
return nil, errors.New("Server returned status code: " + strconv.Itoa(resp.StatusCode))
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.Info(err)
return nil, err
}

return body, nil
}

// Info GET: /info
func (n Node) Info() (*InfoResponse, error) {
res := InfoResponse{}
body, err := n.apiGET("/info")
if err != nil {
return nil, err
}
if err := json.Unmarshal(body, &res); err != nil {
return nil, err
}

if res.Error != "" {
if strings.HasPrefix(res.Error, "Invalid authentication token") {
return nil, ErrUnauthorized
}
return nil, errors.New(res.Error)
}

if res.MaxImages == 0 {
res.MaxImages = math.MaxInt32
}

return &res, nil
}

func (n Node) CheckAuthorization(err error) error {
if err != nil {
if err == ErrUnauthorized {
// Is there a token?
if n.Token == "" {
return ErrAuthRequired
} else {
return errors.New("Cannot authenticate with the node (invalid token).")
}
} else {
return err
}
}

return nil
}
Loading

0 comments on commit eca83fd

Please sign in to comment.