New app shell construct and an example usage
This commit is contained in:
parent
8dc00128b6
commit
241e6bd124
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"wfe": {
|
||||
"baseURL": "http://localhost:4000",
|
||||
"listenAddress": "0.0.0.0:4000"
|
||||
},
|
||||
"ca": {
|
||||
},
|
||||
"sa": {
|
||||
"dbDriver": "sqlite3",
|
||||
"dbName": ":memory:"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2014 ISRG. All rights reserved
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
// Load both drivers to allow configuring either
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
_ "github.com/ziutek/mymysql"
|
||||
|
||||
"github.com/letsencrypt/boulder/ca"
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
"github.com/letsencrypt/boulder/ra"
|
||||
"github.com/letsencrypt/boulder/sa"
|
||||
"github.com/letsencrypt/boulder/va"
|
||||
"github.com/letsencrypt/boulder/wfe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cmd.NewAppShell("boulder")
|
||||
app.Action = func(c cmd.Config) {
|
||||
// Create the components
|
||||
wfe := wfe.NewWebFrontEndImpl()
|
||||
sa, err := sa.NewSQLStorageAuthority(c.SA.DBDriver, c.SA.DBName)
|
||||
cmd.FailOnError(err, "Unable to create SA")
|
||||
err = sa.InitTables()
|
||||
cmd.FailOnError(err, "Unable to initialize SA")
|
||||
ra := ra.NewRegistrationAuthorityImpl()
|
||||
va := va.NewValidationAuthorityImpl()
|
||||
ca, err := ca.NewCertificateAuthorityImpl(c.CA.Server, c.CA.AuthKey, c.CA.Profile)
|
||||
cmd.FailOnError(err, "Unable to create CA")
|
||||
|
||||
// Wire them up
|
||||
wfe.RA = &ra
|
||||
wfe.SA = sa
|
||||
ra.CA = ca
|
||||
ra.SA = sa
|
||||
ra.VA = &va
|
||||
va.RA = &ra
|
||||
ca.SA = sa
|
||||
|
||||
// Go!
|
||||
newRegPath := "/acme/new-reg"
|
||||
regPath := "/acme/reg/"
|
||||
newAuthzPath := "/acme/new-authz"
|
||||
authzPath := "/acme/authz/"
|
||||
newCertPath := "/acme/new-cert"
|
||||
certPath := "/acme/cert/"
|
||||
wfe.NewReg = c.WFE.BaseURL + newRegPath
|
||||
wfe.RegBase = c.WFE.BaseURL + regPath
|
||||
wfe.NewAuthz = c.WFE.BaseURL + newAuthzPath
|
||||
wfe.AuthzBase = c.WFE.BaseURL + authzPath
|
||||
wfe.NewCert = c.WFE.BaseURL + newCertPath
|
||||
wfe.CertBase = c.WFE.BaseURL + certPath
|
||||
http.HandleFunc(newRegPath, wfe.NewRegistration)
|
||||
http.HandleFunc(newAuthzPath, wfe.NewAuthorization)
|
||||
http.HandleFunc(newCertPath, wfe.NewCertificate)
|
||||
http.HandleFunc(regPath, wfe.Registration)
|
||||
http.HandleFunc(authzPath, wfe.Authorization)
|
||||
http.HandleFunc(certPath, wfe.Certificate)
|
||||
|
||||
// Add a simple ToS
|
||||
termsPath := "/terms"
|
||||
http.HandleFunc(termsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "You agree to do the right thing")
|
||||
})
|
||||
wfe.SubscriberAgreementURL = c.WFE.BaseURL + termsPath
|
||||
|
||||
// We need to tell the RA how to make challenge URIs
|
||||
// XXX: Better way to do this? Part of improved configuration
|
||||
ra.AuthzBase = wfe.AuthzBase
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Server running, listening on %s...\n", c.WFE.ListenAddress)
|
||||
err = http.ListenAndServe(c.WFE.ListenAddress, nil)
|
||||
cmd.FailOnError(err, "Error starting HTTP server")
|
||||
}
|
||||
|
||||
app.Run()
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2014 ISRG. All rights reserved
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// This package provides utilities that underlie the specific commands.
|
||||
// The idea is to make the specific command files very small, e.g.:
|
||||
//
|
||||
// func main() {
|
||||
// app := cmd.NewAppShell("command-name")
|
||||
// app.Action = func(c cmd.Config) {
|
||||
// // command logic
|
||||
// }
|
||||
// app.Run()
|
||||
// }
|
||||
//
|
||||
// All commands share the same invocation pattern. They take a single
|
||||
// parameter "-config", which is the name of a JSON file containing
|
||||
// the configuration for the app. This JSON file is unmarshalled into
|
||||
// a Config object, which is provided to the app.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/letsencrypt/boulder/rpc"
|
||||
"github.com/streadway/amqp"
|
||||
)
|
||||
|
||||
// Config stores configuration parameters that applications
|
||||
// will need. For simplicity, we just lump them all into
|
||||
// one struct, and use encoding/json to read it from a file.
|
||||
//
|
||||
// Note: NO DEFAULTS are provided.
|
||||
type Config struct {
|
||||
// General
|
||||
AMQPServer string
|
||||
|
||||
WFE struct {
|
||||
BaseURL string
|
||||
ListenAddress string
|
||||
}
|
||||
|
||||
CA struct {
|
||||
Server string
|
||||
AuthKey string
|
||||
Profile string
|
||||
}
|
||||
|
||||
SA struct {
|
||||
DBDriver string
|
||||
DBName string
|
||||
}
|
||||
}
|
||||
|
||||
type AppShell struct {
|
||||
Action func(Config)
|
||||
app *cli.App
|
||||
}
|
||||
|
||||
func NewAppShell(name string) (shell *AppShell) {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = name
|
||||
app.Version = "0.0.0"
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Value: "config.json",
|
||||
EnvVar: "BOULDER_CONFIG",
|
||||
},
|
||||
}
|
||||
|
||||
return &AppShell{app: app}
|
||||
}
|
||||
|
||||
func (as *AppShell) Run() {
|
||||
as.app.Action = func(c *cli.Context) {
|
||||
configFileName := c.GlobalString("config")
|
||||
configJSON, err := ioutil.ReadFile(configFileName)
|
||||
FailOnError(err, "Unable to read config file")
|
||||
|
||||
var config Config
|
||||
err = json.Unmarshal(configJSON, &config)
|
||||
|
||||
as.Action(config)
|
||||
}
|
||||
|
||||
err := as.app.Run(os.Args)
|
||||
FailOnError(err, "Failed to run application")
|
||||
}
|
||||
|
||||
// Exit and print error message if we encountered a problem
|
||||
func FailOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the same as amqpConnect in boulder, but with even
|
||||
// more aggressive error dropping
|
||||
func AmqpChannel(url string) (ch *amqp.Channel) {
|
||||
conn, err := amqp.Dial(url)
|
||||
FailOnError(err, "Unable to connect to AMQP server")
|
||||
|
||||
ch, err = conn.Channel()
|
||||
FailOnError(err, "Unable to establish channel to AMQP server")
|
||||
return
|
||||
}
|
||||
|
||||
// Start the server and wait around
|
||||
func RunForever(server *rpc.AmqpRPCServer) {
|
||||
forever := make(chan bool)
|
||||
server.Start()
|
||||
fmt.Fprintf(os.Stderr, "Server running...\n")
|
||||
<-forever
|
||||
}
|
Loading…
Reference in New Issue