Skip to content
This repository has been archived by the owner on Dec 9, 2022. It is now read-only.

factor out image serving #39

Merged
merged 2 commits into from
Apr 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/image-inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"

iiapi "github.com/openshift/image-inspector/pkg/api"
iicmd "github.com/openshift/image-inspector/pkg/cmd"
ii "github.com/openshift/image-inspector/pkg/inspector"
)
Expand All @@ -20,7 +21,7 @@ func main() {
flag.Var(&inspectorOptions.DockerCfg, "dockercfg", "Location of the docker configuration files. May be specified more than once")
flag.StringVar(&inspectorOptions.Username, "username", inspectorOptions.Username, "username for authenticating with the docker registry")
flag.StringVar(&inspectorOptions.PasswordFile, "password-file", inspectorOptions.PasswordFile, "Location of a file that contains the password for authentication with the docker registry")
flag.StringVar(&inspectorOptions.ScanType, "scan-type", inspectorOptions.ScanType, fmt.Sprintf("The type of the scan to be done on the inspected image. Available scan types are: %v", iicmd.ScanOptions))
flag.StringVar(&inspectorOptions.ScanType, "scan-type", inspectorOptions.ScanType, fmt.Sprintf("The type of the scan to be done on the inspected image. Available scan types are: %v", iiapi.ScanOptions))
flag.StringVar(&inspectorOptions.ScanResultsDir, "scan-results-dir", inspectorOptions.ScanResultsDir, "The directory that will contain the results of the scan")
flag.BoolVar(&inspectorOptions.OpenScapHTML, "openscap-html-report", inspectorOptions.OpenScapHTML, "Generate an OpenScap HTML report in addition to the ARF formatted report")
flag.StringVar(&inspectorOptions.CVEUrlPath, "cve-url", inspectorOptions.CVEUrlPath, "An alternative URL source for CVE files")
Expand Down
44 changes: 44 additions & 0 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package api

import (
docker "github.com/fsouza/go-dockerclient"
"time"
)

// OpenSCAPStatus is the status of openscap scan
type OpenSCAPStatus string

const (
StatusNotRequested OpenSCAPStatus = "NotRequested"
StatusSuccess OpenSCAPStatus = "Success"
StatusError OpenSCAPStatus = "Error"
)

type OpenSCAPMetadata struct {
Status OpenSCAPStatus // Status of the OpenSCAP scan report
ErrorMessage string // Error message from the openscap
ContentTimeStamp string // Timestamp for this data
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a string? Would there be a benefit to leaving it as Time? If it is because of issues with json marshalling should we take the Kube approach and provide a wrapper (see unversioned.Time in Kube) so we retain the option to perform time based functions on this field?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH it is string because it was string before [1], I didn't go into this that much when I moved these pieces of code around. I agree that it will be nice to have it as Time but I think that changing this should be done in a different PR because the issue is different than the one this PR is about.

[1]https://github.com/openshift/image-inspector/pull/39/files#diff-f5d24f0dc00fe1c3eeda8d22b2e523ebL20

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, ok. Been a while, sorry. I agree that we should leave it for this refactor

}

func (osm *OpenSCAPMetadata) SetError(err error) {
osm.Status = StatusError
osm.ErrorMessage = err.Error()
osm.ContentTimeStamp = string(time.Now().Format(time.RFC850))
}

var (
ScanOptions = []string{"openscap"}
)

// InspectorMetadata is the metadata type with information about image-inspector's operation
type InspectorMetadata struct {
docker.Image // Metadata about the inspected image
// OpenSCAP describes the state of the OpenSCAP scan
OpenSCAP *OpenSCAPMetadata
}

// APIVersions holds a slice of supported API versions.
type APIVersions struct {
// Versions is the supported API versions
Versions []string `json:"versions"`
}
13 changes: 7 additions & 6 deletions pkg/cmd/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package cmd

import (
"fmt"

oscapscanner "github.com/openshift/image-inspector/pkg/openscap"
"os"
)

var (
ScanOptions = []string{"openscap"}
iiapi "github.com/openshift/image-inspector/pkg/api"

"os"
)

// MultiStringVar is implementing flag.Value
Expand Down Expand Up @@ -110,14 +110,15 @@ func (i *ImageInspectorOptions) Validate() error {
}
if len(i.ScanType) > 0 {
var found bool = false
for _, opt := range ScanOptions {
for _, opt := range iiapi.ScanOptions {
if i.ScanType == opt {
found = true
break
}
}
if !found {
return fmt.Errorf("%s is not one of the available scan-types which are %v", i.ScanType, ScanOptions)
return fmt.Errorf("%s is not one of the available scan-types which are %v",
i.ScanType, iiapi.ScanOptions)
}

}
Expand Down
41 changes: 41 additions & 0 deletions pkg/imageserver/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package imageserver

import (
iiapi "github.com/openshift/image-inspector/pkg/api"
)

// ImageServer abstracts the serving of image information.
type ImageServer interface {
// ServeImage Serves the image
ServeImage(meta *iiapi.InspectorMetadata,
scanReport []byte,
htmlScanReport []byte) error
}

// ImageServerOptions is used to configure an image server.
type ImageServerOptions struct {
// ServePath is the root path/port of serving. ex 0.0.0.0:8080
ServePath string
// HealthzURL is the relative url of the health check. ex /healthz
HealthzURL string
// APIURL is the relative url where the api will be served. ex /api
APIURL string
// APIVersions are the supported API versions.
APIVersions iiapi.APIVersions
// MetadataURL is the relative url of the metadata content. ex /api/v1/metadata
MetadataURL string
// ContentURL is the relative url of the content. ex /api/v1/content/
ContentURL string
// ImageServeURL is the location that the image is being served from.
// NOTE: if the image server supports a chroot the server implementation will perform
// the chroot based on this URL.
ImageServeURL string
// ScanType is the type of the scan that was done on the inspected image
ScanType string
// ScanReportURL is the url to publish the scan report
ScanReportURL string
// HTMLScanReport wether or not to publish an HTML scan report
HTMLScanReport bool
// HTMLScanReportURL url for the scan html report
HTMLScanReportURL string
}
112 changes: 112 additions & 0 deletions pkg/imageserver/webdav.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package imageserver

import (
"encoding/json"
"fmt"
"log"
"net/http"
"syscall"

"golang.org/x/net/webdav"

iiapi "github.com/openshift/image-inspector/pkg/api"
)

const (
// CHROOT_SERVE_PATH is the path to server if we are performing a chroot
// this probably does not belong here.
CHROOT_SERVE_PATH = "/"
)

// webdavImageServer implements ImageServer.
type webdavImageServer struct {
opts ImageServerOptions
chroot bool
}

// ensures this always implements the interface or fail compilation.
var _ ImageServer = &webdavImageServer{}

// NewWebdavImageServer creates a new webdav image server.
func NewWebdavImageServer(opts ImageServerOptions, chroot bool) ImageServer {
return &webdavImageServer{
opts: opts,
chroot: chroot,
}
}

// ServeImage Serves the image.
func (s *webdavImageServer) ServeImage(meta *iiapi.InspectorMetadata,
scanReport []byte,
htmlScanReport []byte) error {

servePath := s.opts.ImageServeURL
if s.chroot {
if err := syscall.Chroot(s.opts.ImageServeURL); err != nil {
return fmt.Errorf("Unable to chroot into %s: %v\n", s.opts.ImageServeURL, err)
}
servePath = CHROOT_SERVE_PATH
} else {
log.Printf("!!!WARNING!!! It is insecure to serve the image content without changing")
log.Printf("root (--chroot). Absolute-path symlinks in the image can lead to disclose")
log.Printf("information of the hosting system.")
}

log.Printf("Serving image content %s on webdav://%s%s", s.opts.ImageServeURL, s.opts.ServePath, s.opts.ContentURL)

http.HandleFunc(s.opts.HealthzURL, func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok\n"))
})

http.HandleFunc(s.opts.APIURL, func(w http.ResponseWriter, r *http.Request) {
body, err := json.MarshalIndent(s.opts.APIVersions, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(body)
})

http.HandleFunc(s.opts.MetadataURL, func(w http.ResponseWriter, r *http.Request) {
body, err := json.MarshalIndent(meta, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(body)
})

http.HandleFunc(s.opts.ScanReportURL, func(w http.ResponseWriter, r *http.Request) {
if s.opts.ScanType != "" && meta.OpenSCAP.Status == iiapi.StatusSuccess {
w.Write(scanReport)
} else {
if meta.OpenSCAP.Status == iiapi.StatusError {
http.Error(w, fmt.Sprintf("OpenSCAP Error: %s", meta.OpenSCAP.ErrorMessage),
http.StatusInternalServerError)
} else {
http.Error(w, "OpenSCAP option was not chosen", http.StatusNotFound)
}
}
})

http.HandleFunc(s.opts.HTMLScanReportURL, func(w http.ResponseWriter, r *http.Request) {
if s.opts.ScanType != "" && meta.OpenSCAP.Status == iiapi.StatusSuccess && s.opts.HTMLScanReport {
w.Write(htmlScanReport)
} else {
if meta.OpenSCAP.Status == iiapi.StatusError {
http.Error(w, fmt.Sprintf("OpenSCAP Error: %s", meta.OpenSCAP.ErrorMessage),
http.StatusInternalServerError)
} else {
http.Error(w, "OpenSCAP option was not chosen", http.StatusNotFound)
}
}
})

http.Handle(s.opts.ContentURL, &webdav.Handler{
Prefix: s.opts.ContentURL,
FileSystem: webdav.Dir(servePath),
LockSystem: webdav.NewMemLS(),
})

return http.ListenAndServe(s.opts.ServePath, nil)
}
Loading