Skip to content

Commit

Permalink
Merge pull request #7 from arminc/server
Browse files Browse the repository at this point in the history
Add server with web ui
  • Loading branch information
arminc committed Dec 19, 2019
2 parents 5ce216d + 6754f68 commit 3c37d68
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 91 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ This project helps you keep track of all your software and tools that are used o
- [x] Possibility to provide local tool versions (like terraform) and find the new versions on GitHub
- [x] Keep track of Helm chart deployments and track new versions of the charts
- [x] Present the information command line
- [x] Present the information trough a web UI

### Todo

* Run as a daemon
* Provide a web UI
* Have a helm chart to deploy the app into Kubernetes
* Automatically fetch new versions every X time
* Add a possibility to whitelist vulnerabilities so only changes are presented
* Provide information on Kubernetes version (for example AWS EKS and it's components)
* Add tests (unit or integration)

* Architecture diagram
* Use Clair as a vulnerability scanning option
* Add Slack/Teams integration
* Push changes/vulnerabilities list to a ConfigMap so anyone with kubectl access can see it
Expand Down Expand Up @@ -59,10 +59,13 @@ Flags:
--debug Show debug information, debug includes verbose. This overrides the config setting
--jsonLogging Log in json format
--logFile=LOGFILE Log file path
--server Start the server
```

## Example output

### Command Line

```bash
+---------------------------------------+-------------------+----------+-------+
| IMAGE | VERSION | LATEST | CVES |
Expand All @@ -84,4 +87,8 @@ Flags:
| derailed/popeye | v0.4.1 | v0.5.0 |
| hashicorp/terraform | 0.11.14 | v0.12.18 |
+---------------------+---------+----------+
```
```

### Web UI

<img src="assets/screenshot.png" width="800">
Binary file added assets/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions cmd/lcm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func initFlags() config.AppConfig {
app.Flag("debug", "Show debug information, debug includes verbose. This overrides the config setting").BoolVar(&cliFlags.Debug)
app.Flag("jsonLogging", "Log in json format").BoolVar(&cliFlags.JsonLoggingEnabled)
app.Flag("logFile", "Log file path").StringVar(&cliFlags.LogFile)
app.Flag("server", "Start the server").BoolVar(&cliFlags.StartServer)
kingpin.MustParse(app.Parse(os.Args[1:]))

return *cliFlags
Expand All @@ -56,4 +57,7 @@ func main() {
initLogging(config)
log.WithField("version", Version).Info("Running version")
internal.Execute(config)
if config.CliFlags.StartServer {
internal.StartServer()
}
}
1 change: 1 addition & 0 deletions exampleConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type: yml # default type
# debug: true # Enable debug logging, default is false
# jsonLoggingEnabled: true # Enable json logging format, default is false. When logging to json format no output table is shown
# logFile: /path/where/to/log.json # Path to log to a file. No standard output is available anymore. When logging to json format no output table is shown
# startServer: true # Run as a web server, default is false

# Don't check for information in Kubernetes cluster, default is true
#kubernetesFetchEnabled: false
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ require (
github.com/alecthomas/kingpin v2.2.6+incompatible
github.com/docker/distribution v2.7.1+incompatible
github.com/google/go-github/v28 v28.1.1
github.com/gorilla/mux v1.7.3
github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5
github.com/jfrog/jfrog-client-go v0.5.9
github.com/knadh/koanf v0.6.0
Expand All @@ -14,6 +16,7 @@ require (
github.com/prometheus/common v0.7.0
github.com/sirupsen/logrus v1.4.2
github.com/target/go-arty v0.0.0-20191122155631-9967a6326524
github.com/urfave/negroni v1.0.0
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6
google.golang.org/appengine v1.6.5
gopkg.in/yaml.v2 v2.2.4
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40 h1:GT4RsKmHh1uZyhmTkWJTDALRjSHYQp6FRKrotf0zhAs=
github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40/go.mod h1:NtmN9h8vrTveVQRLHcX2HQ5wIPBDCsZ351TGbZWgg38=
github.com/heroku/docker-registry-client v0.0.0-20190909225348-afc9e1acc3d5/go.mod h1:Yho0S7KhsnHQRCC5lDraYF1SsLMeWtf/tKdufKu3TJA=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
Expand Down Expand Up @@ -498,6 +500,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
Expand Down
3 changes: 2 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Config struct {
type AppConfig struct {
Locally bool
ConfigFile string
StartServer bool `koanf:"startServer"`
JsonLoggingEnabled bool `koanf:"jsonLoggingEnabled"`
LogFile string `koanf:"logFile"`
Verbose bool `koanf:"verbose"`
Expand Down Expand Up @@ -100,5 +101,5 @@ func (c Config) LogToFilePath() (bool, string) {
// PrettyPrintAllowed returns true when pretty print is allowed
func (c Config) PrettyPrintAllowed() bool {
logFileEnabled := c.CliFlags.LogFile != "" || c.AppConfig.LogFile != ""
return !logFileEnabled || !c.IsJsonLoggingEnabled()
return !logFileEnabled && !c.IsJsonLoggingEnabled() && !c.CliFlags.StartServer
}
106 changes: 28 additions & 78 deletions internal/lcm.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package internal

import (
"os"
"sort"
"strconv"
"time"

"github.com/arminc/k8s-platform-lcm/internal/config"
"github.com/arminc/k8s-platform-lcm/internal/kubernetes"
"github.com/arminc/k8s-platform-lcm/internal/registries"
"github.com/arminc/k8s-platform-lcm/internal/scanning"
"github.com/arminc/k8s-platform-lcm/internal/versioning"
"github.com/olekukonko/tablewriter"
)

// ToolInfo contains tool information with the latest version
Expand All @@ -29,33 +25,43 @@ type ChartInfo struct {
type ContainerInfo struct {
Container kubernetes.Container
LatestVersion string
VersionStatus string
Fetched bool
Cves []string
}

// Execute runs all the checks for LCM
func Execute(config config.Config) {

WebDataVar.Status = "Running"

var containers = []kubernetes.Container{}
if config.IsKubernetesFetchEnabled() {
containers = kubernetes.GetContainersFromNamespaces(config.Namespaces, config.RunningLocally())
}

containers = getExtraImages(config.Images, containers)
info := getLatestVersionsForContainers(containers, config.ImageRegistries)
info = getVulnerabilities(info, config)
if config.PrettyPrintAllowed() {
prettyPrintContainerInfo(info)
}
WebDataVar.ContainerInfo = info

if config.IsKubernetesFetchEnabled() {
charts := getLatestVersionsForHelmCharts(config.HelmRegistries, config.Namespaces, config.RunningLocally())
if config.PrettyPrintAllowed() {
prettyPrintChartInfo(charts)
}
WebDataVar.ChartInfo = charts
}

tools := getLatestVersionsForTools(config.Tools, config.ToolRegistries)
if config.PrettyPrintAllowed() {
prettyPrintToolInfo(tools)
}
WebDataVar.ToolInfo = tools
WebDataVar.Status = "Done"
WebDataVar.LastTimeFetched = time.Now().Format("15:04:05 02-01-2006")
}

func getExtraImages(images []string, containers []kubernetes.Container) []kubernetes.Container {
Expand All @@ -75,9 +81,12 @@ func getLatestVersionsForContainers(containers []kubernetes.Container, registrie
containerInfo = append(containerInfo, ContainerInfo{
Container: container,
LatestVersion: version,
VersionStatus: versioning.DetermineLifeCycleStatus(version, container.Version),
})
}

sort.Slice(containerInfo, func(i, j int) bool {
return containerInfo[i].Container.Name < containerInfo[j].Container.Name
})
return containerInfo
}

Expand All @@ -88,8 +97,13 @@ func getVulnerabilities(containerInfo []ContainerInfo, config config.Config) []C
ci.Cves = vulnerabilities
containerInfoWithVul = append(containerInfoWithVul, ci)
}

sort.Slice(containerInfoWithVul, func(i, j int) bool {
return containerInfoWithVul[i].Container.Name < containerInfoWithVul[j].Container.Name
})
return containerInfoWithVul
}

func getLatestVersionsForHelmCharts(helmRegistries registries.HelmRegistries, namespaces []string, local bool) []ChartInfo {
var chartInfo []ChartInfo
charts := kubernetes.GetHelmChartsFromNamespaces(namespaces, local)
Expand All @@ -100,6 +114,10 @@ func getLatestVersionsForHelmCharts(helmRegistries registries.HelmRegistries, na
LatestVersion: version,
})
}

sort.Slice(chartInfo, func(i, j int) bool {
return chartInfo[i].Chart.Name < chartInfo[j].Chart.Name
})
return chartInfo
}

Expand All @@ -112,77 +130,9 @@ func getLatestVersionsForTools(tools []registries.Tool, registries registries.To
LatestVersion: version,
})
}
return toolInfo
}

func prettyPrintContainerInfo(info []ContainerInfo) {
sort.Slice(info, func(i, j int) bool {
return info[i].Container.Name < info[j].Container.Name
})

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Image", "Version", "Latest", "Cves"})
table.SetColumnAlignment([]int{3, 1, 1, 3})

for _, container := range info {
cve := strconv.Itoa(len(container.Cves))

if len(container.Cves) == 1 {
switch container.Cves[0] {
case scanning.ERROR:
cve = scanning.ERROR
case scanning.NODATA:
cve = scanning.NODATA
}
}

row := []string{
container.Container.Name,
container.Container.Version,
container.LatestVersion,
cve,
}
table.Append(row)
}
table.Render()
}

func prettyPrintToolInfo(tools []ToolInfo) {
sort.Slice(tools, func(i, j int) bool {
return tools[i].Tool.Repo < tools[j].Tool.Repo
sort.Slice(toolInfo, func(i, j int) bool {
return toolInfo[i].Tool.Repo < toolInfo[j].Tool.Repo
})

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Tool", "Version", "Latest"})
table.SetColumnAlignment([]int{3, 1, 1})

for _, tool := range tools {
row := []string{
tool.Tool.Repo,
tool.Tool.Version,
tool.LatestVersion,
}
table.Append(row)
}
table.Render()
}

func prettyPrintChartInfo(charts []ChartInfo) {
sort.Slice(charts, func(i, j int) bool {
return charts[i].Chart.Name < charts[j].Chart.Name
})

table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Chart", "Version", "Latest"})
table.SetColumnAlignment([]int{3, 1, 1})

for _, chart := range charts {
row := []string{
chart.Chart.Name,
chart.Chart.Version,
chart.LatestVersion,
}
table.Append(row)
}
table.Render()
return toolInfo
}
Loading

0 comments on commit 3c37d68

Please sign in to comment.