0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

feat(GraphQL): playground, served by zot in specific binary (#753)

Signed-off-by: Catalin Hofnar <catalin.hofnar@gmail.com>
This commit is contained in:
Catalin Hofnar 2022-10-05 22:56:41 +03:00 committed by GitHub
parent c146448f01
commit ffc9929c1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 317 additions and 0 deletions

View file

@ -0,0 +1,21 @@
{
"distSpecVersion": "1.0.1-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address": "127.0.0.1",
"port": "5000"
},
"log": {
"level": "debug"
},
"extensions": {
"search": {
"enable": true,
"cve": {
"updateInterval": "24h"
}
}
}
}

View file

@ -27,6 +27,7 @@ import (
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/api/constants"
gqlPlayground "zotregistry.io/zot/pkg/debug/gqlplayground"
debug "zotregistry.io/zot/pkg/debug/swagger"
ext "zotregistry.io/zot/pkg/extensions"
"zotregistry.io/zot/pkg/log"
@ -117,6 +118,7 @@ func (rh *RouteHandler) SetupRoutes() {
// extended build
ext.SetupMetricsRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log)
ext.SetupSearchRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log)
gqlPlayground.SetupGQLPlaygroundRoutes(rh.c.Config, rh.c.Router, rh.c.StoreController, rh.c.Log)
}
}
}

View file

@ -0,0 +1,6 @@
package constants
const (
Debug = "/_zot/debug"
GQLPlaygroundEndpoint = Debug + "/graphql-playground"
)

View file

@ -0,0 +1,53 @@
//go:build debug
// +build debug
package debug
import (
"embed"
"html/template"
"net/http"
"github.com/gorilla/mux"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
debugCst "zotregistry.io/zot/pkg/debug/constants"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
)
//go:embed index.html.tmpl
var playgroundHTML embed.FS
// SetupGQLPlaygroundRoutes ...
func SetupGQLPlaygroundRoutes(conf *config.Config, router *mux.Router,
storeController storage.StoreController, l log.Logger,
) {
log := log.Logger{Logger: l.With().Caller().Timestamp().Logger()}
log.Info().Msg("setting up graphql playground route")
templ, err := template.ParseFS(playgroundHTML, "gqlplayground/index.html.tmpl")
if err != nil {
log.Fatal().Err(err)
}
//nolint:lll
router.PathPrefix(constants.RoutePrefix + debugCst.GQLPlaygroundEndpoint).HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
writer.Header().Add("Content-Type", "text/html")
proto := ""
if req.TLS == nil {
proto += "http://"
} else {
proto += "https://"
}
target := proto + req.Host + constants.ExtSearchPrefix
// respond with the output of template execution
_ = templ.Execute(writer, struct {
Target string
}{Target: target})
})
}

View file

@ -0,0 +1,19 @@
//go:build !debug
// +build !debug
package debug
import (
"github.com/gorilla/mux"
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
)
// SetupGQLPlaygroundRoutes ...
func SetupGQLPlaygroundRoutes(conf *config.Config, router *mux.Router,
storeController storage.StoreController, log log.Logger,
) {
log.Warn().Msg("skipping enabling graphql playground extension because given zot binary" +
"doesn't include this feature, please build a binary that does so")
}

View file

@ -0,0 +1,67 @@
<!--
* Copyright (c) 2021 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
-->
<!DOCTYPE html>
<html>
<head>
<title>zot GraphQL playground</title>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<!--
This GraphiQL example depends on Promise and fetch, which are available in
modern browsers, but can be "polyfilled" for older browsers.
GraphiQL itself depends on React DOM.
If you do not want to rely on a CDN, you can host these files locally or
include them directly in your favored resource bundler.
-->
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
></script>
<!--
These two files can be found in the npm module, however you may wish to
copy them directly into your environment, or perhaps include them in your
favored resource bundler.
-->
<link rel="stylesheet" href="https://unpkg.com/graphiql/graphiql.min.css" />
</head>
<body>
<div id="graphiql">Loading...</div>
<script
src="https://unpkg.com/graphiql/graphiql.min.js"
type="application/javascript"
></script>
<script>
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: GraphiQL.createFetcher({
url: {{.Target}},
}),
defaultEditorToolsVisibility: true,
}),
document.getElementById('graphiql'),
);
</script>
</body>
</html>

View file

@ -776,11 +776,17 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
var sources = []*ast.Source{
{Name: "../schema.graphql", Input: `scalar Time
"""
Contains the tag of the image and a list of CVEs
"""
type CVEResultForImage {
Tag: String
CVEList: [CVE]
}
"""
Contains various details about the CVE and a list of PackageInfo about the affected packages
"""
type CVE {
Id: String
Title: String
@ -789,12 +795,18 @@ type CVE {
PackageList: [PackageInfo]
}
"""
Contains the name of the package, the current installed version and the version where the CVE was fixed
"""
type PackageInfo {
Name: String
InstalledVersion: String
FixedVersion: String
}
"""
Contains details about the repo: a list of image summaries and a summary of the repo
"""
type RepoInfo {
Images: [ImageSummary]
Summary: RepoSummary
@ -802,6 +814,9 @@ type RepoInfo {
# Search results in all repos/images/layers
# There will be other more structures for more detailed information
"""
Search everything. Can search Images, Repos and Layers
"""
type GlobalSearchResult {
Images: [ImageSummary]
Repos: [RepoSummary]
@ -810,6 +825,9 @@ type GlobalSearchResult {
# Brief on a specific image to be used in queries returning a list of images
# We define an image as a pairing or a repo and a tag belonging to that repo
"""
Contains details about the image
"""
type ImageSummary {
RepoName: String
Tag: String
@ -839,6 +857,9 @@ type ImageVulnerabilitySummary {
}
# Brief on a specific repo to be used in queries returning a list of repos
"""
Contains details about the repo
"""
type RepoSummary {
Name: String
LastUpdated: Time
@ -854,6 +875,9 @@ type RepoSummary {
# Currently the same as LayerInfo, we can refactor later
# For detailed information on the layer a ImageListForDigest call can be made
"""
Contains details about the layer
"""
type LayerSummary {
Size: String # Int64 is not supported.
Digest: String
@ -885,22 +909,68 @@ type LayerHistory {
HistoryDescription: HistoryDescription
}
"""
Contains details about the supported OS and architecture of the image
"""
type OsArch {
Os: String
Arch: String
}
type Query {
"""
Returns a CVE list for the image specified in the arugment
"""
CVEListForImage(image: String!): CVEResultForImage!
"""
Returns a list of images vulnerable to the CVE of the specified ID
"""
ImageListForCVE(id: String!): [ImageSummary!]
"""
Returns a list of images that are no longer vulnerable to the CVE of the specified ID, from the specified image (repo)
"""
ImageListWithCVEFixed(id: String!, image: String!): [ImageSummary!]
"""
Returns a list of images which contain the specified digest
"""
ImageListForDigest(id: String!): [ImageSummary!]
"""
Returns a list of repos with the newest tag within
"""
RepoListWithNewestImage: [RepoSummary!]! # Newest based on created timestamp
"""
Returns all the images from the specified repo
"""
ImageList(repo: String!): [ImageSummary!]
"""
Returns information about the specified repo
"""
ExpandedRepoInfo(repo: String!): RepoInfo!
"""
Searches within repos, images, and layers
"""
GlobalSearch(query: String!): GlobalSearchResult!
"""
List of images which use the argument image
"""
DerivedImageList(image: String!): [ImageSummary!]
"""
List of images on which the argument image depends on
"""
BaseImageList(image: String!): [ImageSummary!]
"""
Search for a specific image using its name
"""
Image(image: String!): ImageSummary
}
`, BuiltIn: false},

View file

@ -6,6 +6,7 @@ import (
"time"
)
// Contains various details about the CVE and a list of PackageInfo about the affected packages
type Cve struct {
ID *string `json:"Id"`
Title *string `json:"Title"`
@ -14,11 +15,13 @@ type Cve struct {
PackageList []*PackageInfo `json:"PackageList"`
}
// Contains the tag of the image and a list of CVEs
type CVEResultForImage struct {
Tag *string `json:"Tag"`
CVEList []*Cve `json:"CVEList"`
}
// Search everything. Can search Images, Repos and Layers
type GlobalSearchResult struct {
Images []*ImageSummary `json:"Images"`
Repos []*RepoSummary `json:"Repos"`
@ -37,6 +40,7 @@ type HistoryDescription struct {
EmptyLayer *bool `json:"EmptyLayer"`
}
// Contains details about the image
type ImageSummary struct {
RepoName *string `json:"RepoName"`
Tag *string `json:"Tag"`
@ -70,28 +74,33 @@ type LayerHistory struct {
HistoryDescription *HistoryDescription `json:"HistoryDescription"`
}
// Contains details about the layer
type LayerSummary struct {
Size *string `json:"Size"`
Digest *string `json:"Digest"`
Score *int `json:"Score"`
}
// Contains details about the supported OS and architecture of the image
type OsArch struct {
Os *string `json:"Os"`
Arch *string `json:"Arch"`
}
// Contains the name of the package, the current installed version and the version where the CVE was fixed
type PackageInfo struct {
Name *string `json:"Name"`
InstalledVersion *string `json:"InstalledVersion"`
FixedVersion *string `json:"FixedVersion"`
}
// Contains details about the repo: a list of image summaries and a summary of the repo
type RepoInfo struct {
Images []*ImageSummary `json:"Images"`
Summary *RepoSummary `json:"Summary"`
}
// Contains details about the repo
type RepoSummary struct {
Name *string `json:"Name"`
LastUpdated *time.Time `json:"LastUpdated"`

View file

@ -1,10 +1,16 @@
scalar Time
"""
Contains the tag of the image and a list of CVEs
"""
type CVEResultForImage {
Tag: String
CVEList: [CVE]
}
"""
Contains various details about the CVE and a list of PackageInfo about the affected packages
"""
type CVE {
Id: String
Title: String
@ -13,12 +19,18 @@ type CVE {
PackageList: [PackageInfo]
}
"""
Contains the name of the package, the current installed version and the version where the CVE was fixed
"""
type PackageInfo {
Name: String
InstalledVersion: String
FixedVersion: String
}
"""
Contains details about the repo: a list of image summaries and a summary of the repo
"""
type RepoInfo {
Images: [ImageSummary]
Summary: RepoSummary
@ -26,6 +38,9 @@ type RepoInfo {
# Search results in all repos/images/layers
# There will be other more structures for more detailed information
"""
Search everything. Can search Images, Repos and Layers
"""
type GlobalSearchResult {
Images: [ImageSummary]
Repos: [RepoSummary]
@ -34,6 +49,9 @@ type GlobalSearchResult {
# Brief on a specific image to be used in queries returning a list of images
# We define an image as a pairing or a repo and a tag belonging to that repo
"""
Contains details about the image
"""
type ImageSummary {
RepoName: String
Tag: String
@ -63,6 +81,9 @@ type ImageVulnerabilitySummary {
}
# Brief on a specific repo to be used in queries returning a list of repos
"""
Contains details about the repo
"""
type RepoSummary {
Name: String
LastUpdated: Time
@ -78,6 +99,9 @@ type RepoSummary {
# Currently the same as LayerInfo, we can refactor later
# For detailed information on the layer a ImageListForDigest call can be made
"""
Contains details about the layer
"""
type LayerSummary {
Size: String # Int64 is not supported.
Digest: String
@ -109,21 +133,67 @@ type LayerHistory {
HistoryDescription: HistoryDescription
}
"""
Contains details about the supported OS and architecture of the image
"""
type OsArch {
Os: String
Arch: String
}
type Query {
"""
Returns a CVE list for the image specified in the arugment
"""
CVEListForImage(image: String!): CVEResultForImage!
"""
Returns a list of images vulnerable to the CVE of the specified ID
"""
ImageListForCVE(id: String!): [ImageSummary!]
"""
Returns a list of images that are no longer vulnerable to the CVE of the specified ID, from the specified image (repo)
"""
ImageListWithCVEFixed(id: String!, image: String!): [ImageSummary!]
"""
Returns a list of images which contain the specified digest
"""
ImageListForDigest(id: String!): [ImageSummary!]
"""
Returns a list of repos with the newest tag within
"""
RepoListWithNewestImage: [RepoSummary!]! # Newest based on created timestamp
"""
Returns all the images from the specified repo
"""
ImageList(repo: String!): [ImageSummary!]
"""
Returns information about the specified repo
"""
ExpandedRepoInfo(repo: String!): RepoInfo!
"""
Searches within repos, images, and layers
"""
GlobalSearch(query: String!): GlobalSearchResult!
"""
List of images which use the argument image
"""
DerivedImageList(image: String!): [ImageSummary!]
"""
List of images on which the argument image depends on
"""
BaseImageList(image: String!): [ImageSummary!]
"""
Search for a specific image using its name
"""
Image(image: String!): ImageSummary
}