re-implement http sniffing

This commit is contained in:
flx5 2021-10-03 22:23:55 +02:00
parent 07cc4bfb7c
commit 3b2d378058
3 changed files with 198 additions and 2 deletions

View 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.
}
}
}

View File

@ -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(),

View File

@ -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)