re-implement http sniffing
This commit is contained in:
parent
07cc4bfb7c
commit
3b2d378058
177
builder/xenserver/common/workaround/step_sniffing_http.go
Normal file
177
builder/xenserver/common/workaround/step_sniffing_http.go
Normal file
@ -0,0 +1,177 @@
|
||||
package workaround
|
||||
|
||||
// TODO Replace with sdk once https://github.com/hashicorp/packer-plugin-sdk/pull/80 is merged.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/didyoumean"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/net"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func HTTPServerFromHTTPConfig(cfg *commonsteps.HTTPConfig) *StepHTTPServer {
|
||||
server := StepHTTPServer{
|
||||
HTTPDir: cfg.HTTPDir,
|
||||
HTTPContent: cfg.HTTPContent,
|
||||
HTTPPortMin: cfg.HTTPPortMin,
|
||||
HTTPPortMax: cfg.HTTPPortMax,
|
||||
HTTPAddress: cfg.HTTPAddress,
|
||||
}
|
||||
|
||||
server.httpWrapper = server.wrapper()
|
||||
|
||||
server.AddCallback(logRequest)
|
||||
|
||||
return &server
|
||||
}
|
||||
|
||||
func logRequest(r *http.Request) {
|
||||
log.Printf("http_server: hit %s - \"%s HTTP/%d.%d %s\"", r.RemoteAddr, r.Method, r.ProtoMajor, r.ProtoMinor, r.URL.Path)
|
||||
}
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// ui packersdk.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type StepHTTPServer struct {
|
||||
HTTPDir string
|
||||
HTTPContent map[string]string
|
||||
HTTPPortMin int
|
||||
HTTPPortMax int
|
||||
HTTPAddress string
|
||||
|
||||
httpWrapper HTTPWrapper
|
||||
l *net.Listener
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) AddCallback(callback func(r *http.Request)) {
|
||||
s.httpWrapper.Callbacks = append(s.httpWrapper.Callbacks, callback)
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) wrapper() HTTPWrapper {
|
||||
if s.HTTPDir != "" {
|
||||
return HTTPWrapper{
|
||||
HTTPHandler: http.FileServer(http.Dir(s.HTTPDir)),
|
||||
}
|
||||
}
|
||||
|
||||
return HTTPWrapper{
|
||||
HTTPHandler: MapServer(s.HTTPContent),
|
||||
}
|
||||
}
|
||||
|
||||
type HTTPWrapper struct {
|
||||
HTTPHandler http.Handler
|
||||
Callbacks []func(r *http.Request)
|
||||
}
|
||||
|
||||
func (s HTTPWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
for _, callback := range s.Callbacks {
|
||||
callback(r)
|
||||
}
|
||||
|
||||
s.HTTPHandler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
type MapServer map[string]string
|
||||
|
||||
func (s MapServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
path := path.Clean(r.URL.Path)
|
||||
content, found := s[path]
|
||||
if !found {
|
||||
paths := make([]string, 0, len(s))
|
||||
for k := range s {
|
||||
paths = append(paths, k)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
err := fmt.Sprintf("%s not found.", path)
|
||||
if sug := didyoumean.NameSuggestion(path, paths); sug != "" {
|
||||
err += fmt.Sprintf(" Did you mean %q?", sug)
|
||||
}
|
||||
|
||||
http.Error(w, err, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte(content)); err != nil {
|
||||
// log err in case the file couldn't be 100% transferred for example.
|
||||
log.Printf("http_content serve error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.HTTPDir == "" && len(s.HTTPContent) == 0 {
|
||||
state.Put("http_port", 0)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if s.HTTPDir != "" {
|
||||
if _, err := os.Stat(s.HTTPDir); err != nil {
|
||||
err := fmt.Errorf("Error finding %q: %s", s.HTTPDir, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Find an available TCP port for our HTTP server
|
||||
var err error
|
||||
s.l, err = net.ListenRangeConfig{
|
||||
Min: s.HTTPPortMin,
|
||||
Max: s.HTTPPortMax,
|
||||
Addr: s.HTTPAddress,
|
||||
Network: "tcp",
|
||||
}.Listen(ctx)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error finding port: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", s.l.Port))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
server := &http.Server{Addr: "", Handler: s.httpWrapper}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
state.Put("http_port", s.l.Port)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) Cleanup(state multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// Close the listener so that the HTTP server stops
|
||||
if err := s.l.Close(); err != nil {
|
||||
err = fmt.Errorf("Failed closing http server on port %d: %w", s.l.Port, err)
|
||||
ui.Error(err.Error())
|
||||
// Here this error should be shown to the UI but it won't
|
||||
// specifically stop Packer from terminating successfully. It could
|
||||
// cause a "Listen leak" if it happenned a lot. Though Listen will
|
||||
// try other ports if one is already used. In the case we want to
|
||||
// Listen on only one port, the next Listen call could fail or be
|
||||
// longer than expected.
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,11 @@ import (
|
||||
"errors"
|
||||
artifact2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/artifact"
|
||||
steps2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/steps"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/workaround"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
@ -47,6 +51,21 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
|
||||
httpReqChan := make(chan string, 1)
|
||||
|
||||
httpServerStep := workaround.HTTPServerFromHTTPConfig(&self.config.HTTPConfig)
|
||||
httpServerStep.AddCallback(func(req *http.Request) {
|
||||
log.Printf("HTTP: %s %s %s", req.RemoteAddr, req.Method, req.URL)
|
||||
ip, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
|
||||
if err == nil && ip != "" {
|
||||
select {
|
||||
case httpReqChan <- ip:
|
||||
log.Printf("Remembering remote address '%s'", ip)
|
||||
default:
|
||||
// if ch is already full, don't block waiting to send the address, just drop it
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//Build the steps
|
||||
download_steps := []multistep.Step{
|
||||
&commonsteps.StepDownload{
|
||||
@ -123,7 +142,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
new(steps2.StepSetVmHostSshAddress),
|
||||
new(steps2.StepHTTPIPDiscover),
|
||||
&steps2.StepCreateProxy{},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&self.config.HTTPConfig),
|
||||
httpServerStep,
|
||||
new(steps2.StepBootWait),
|
||||
&steps2.StepTypeBootCommand{
|
||||
Ctx: *self.config.GetInterpContext(),
|
||||
|
@ -42,7 +42,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
//Share state between the other steps using a statebag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("client", c)
|
||||
// state.Put("config", self.config)
|
||||
state.Put("config", self.config)
|
||||
state.Put("commonconfig", self.config.CommonConfig)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
Loading…
Reference in New Issue
Block a user