From d8d3e9f678dc03a1382d18ed19adaefe444f518c Mon Sep 17 00:00:00 2001 From: ricoberger Date: Mon, 2 Aug 2021 22:48:07 +0200 Subject: [PATCH] Add support for Ephemeral Containers It is now possible to view Ephemeral Containers in the overview page of a pod and to create Ephemeral Containers via the debug actions. To make it easier for users to create such containers it is possible to specify a list of templates for the Ephemeral Containers in the configuration file via the 'ephemeralContainers' key in the resources plugin section. To make use of an created Ephemeral Container we also added them to the terminal modal. This allows our users to exec into the created containers. To support the parsing of these templates in from the configuration file, we had to switch our used yaml package from gopkg.in/yaml.v2 to sigs.k8s.io/yaml, which also supports parsing of yaml files via the specified json tags. --- CHANGELOG.md | 1 + cmd/kobs/config/config.go | 6 +- cmd/kobs/plugins/plugins.go | 22 +-- deploy/demo/kind-with-registry.sh | 2 + docs/configuration/plugins.md | 1 + docs/contributing/using-the-kobsio-app.md | 6 +- go.mod | 2 +- pkg/api/clusters/cluster/cluster.go | 14 +- pkg/api/clusters/clusters.go | 2 +- .../clusters/provider/incluster/incluster.go | 2 +- .../provider/kubeconfig/kubeconfig.go | 2 +- pkg/api/clusters/provider/provider.go | 6 +- plugins/applications/applications.go | 16 ++- .../elasticsearch/pkg/instance/instance.go | 14 +- plugins/jaeger/pkg/instance/instance.go | 14 +- plugins/kiali/pkg/instance/instance.go | 20 +-- plugins/opsgenie/pkg/instance/instance.go | 12 +- plugins/prometheus/pkg/instance/instance.go | 14 +- plugins/resources/resources.go | 17 ++- .../src/components/panel/details/Actions.tsx | 25 ++++ .../actions/CreateEphemeralContainer.tsx | 132 ++++++++++++++++++ .../panel/details/actions/Terminal.tsx | 10 +- .../components/panel/details/overview/Pod.tsx | 8 ++ 23 files changed, 270 insertions(+), 78 deletions(-) create mode 100644 plugins/resources/src/components/panel/details/actions/CreateEphemeralContainer.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 351bb620b..caee8cc6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan - [#93](https://github.com/kobsio/kobs/pull/93): Show status of Kubernetes resource in the table of the resources plugin. - [#97](https://github.com/kobsio/kobs/pull/97): Add support for Kiali metrics. - [#98](https://github.com/kobsio/kobs/pull/98): Add terminal support for Kubernetes Pods. +- [#100](https://github.com/kobsio/kobs/pull/100): Add support for Ephemeral Containers. ### Fixed diff --git a/cmd/kobs/config/config.go b/cmd/kobs/config/config.go index 1fd055470..11d45bed0 100644 --- a/cmd/kobs/config/config.go +++ b/cmd/kobs/config/config.go @@ -7,13 +7,13 @@ import ( "github.com/kobsio/kobs/cmd/kobs/plugins" "github.com/kobsio/kobs/pkg/api/clusters" - "gopkg.in/yaml.v2" + "sigs.k8s.io/yaml" ) // Config is the complete configuration for kobs. type Config struct { - Clusters clusters.Config `yaml:"clusters"` - Plugins plugins.Config `yaml:"plugins"` + Clusters clusters.Config `json:"clusters"` + Plugins plugins.Config `json:"plugins"` } // Load the configuration for kobs. Most of the configuration options are available as command-line flag, but we also diff --git a/cmd/kobs/plugins/plugins.go b/cmd/kobs/plugins/plugins.go index aa8d40577..bd9743620 100644 --- a/cmd/kobs/plugins/plugins.go +++ b/cmd/kobs/plugins/plugins.go @@ -26,17 +26,17 @@ import ( // Config holds the configuration for all plugins. We have to add the configuration for all the imported plugins. type Config struct { - Applications applications.Config `yaml:"applications"` - Resources resources.Config `yaml:"resources"` - Teams teams.Config `yaml:"teams"` - Dashboards dashboards.Config `yaml:"dashboards"` - Prometheus prometheus.Config `yaml:"prometheus"` - Elasticsearch elasticsearch.Config `yaml:"elasticsearch"` - Jaeger jaeger.Config `yaml:"jaeger"` - Markdown markdown.Config `yaml:"markdown"` - Kiali kiali.Config `yaml:"kiali"` - Opsgenie opsgenie.Config `yaml:"opsgenie"` - RSS rss.Config `yaml:"rss"` + Applications applications.Config `json:"applications"` + Resources resources.Config `json:"resources"` + Teams teams.Config `json:"teams"` + Dashboards dashboards.Config `json:"dashboards"` + Prometheus prometheus.Config `json:"prometheus"` + Elasticsearch elasticsearch.Config `json:"elasticsearch"` + Jaeger jaeger.Config `json:"jaeger"` + Markdown markdown.Config `json:"markdown"` + Kiali kiali.Config `json:"kiali"` + Opsgenie opsgenie.Config `json:"opsgenie"` + RSS rss.Config `json:"rss"` } // Router implements the router for the plugins package. This only registeres one route which is used to return all the diff --git a/deploy/demo/kind-with-registry.sh b/deploy/demo/kind-with-registry.sh index ecc12114a..3291ccbc5 100755 --- a/deploy/demo/kind-with-registry.sh +++ b/deploy/demo/kind-with-registry.sh @@ -16,6 +16,8 @@ cat < = ({ request, resource, ref const [showRestart, setShowRestart] = useState(false); const [showCreateJob, setShowCreateJob] = useState(false); const [showTerminal, setShowTerminal] = useState(false); + const [showCreateEphemeralContainer, setShowCreateEphemeralContainer] = useState(false); const [showEdit, setShowEdit] = useState(false); const [showDelete, setShowDelete] = useState(false); @@ -94,6 +96,20 @@ const Actions: React.FunctionComponent = ({ request, resource, ref ); } + if (request.resource === 'pods') { + dropdownItems.push( + { + setShowDropdown(false); + setShowCreateEphemeralContainer(true); + }} + > + Create Ephemeral Container + , + ); + } + dropdownItems.push( = ({ request, resource, ref + setAlerts([...alerts, alert])} + refetch={refetch} + /> + void; + setAlert: (alert: IAlert) => void; + refetch: () => void; +} + +const CreateEphemeralContainer: React.FunctionComponent = ({ + resource, + show, + setShow, + setAlert, + refetch, +}: ICreateEphemeralContainerProps) => { + const pluginsContext = useContext(PluginsContext); + const pluginDetails = pluginsContext.getPluginDetails('resources'); + const ephemeralContainers: V1EphemeralContainer[] | undefined = + pluginDetails && pluginDetails.options && pluginDetails.options && pluginDetails.options.ephemeralContainers + ? pluginDetails.options.ephemeralContainers + : undefined; + + const [ephemeralContainer, setEphemeralContainer] = useState( + ephemeralContainers && ephemeralContainers.length > 0 ? yaml.dump(ephemeralContainers[0]) : '', + ); + + const createEphemeralContainer = async (): Promise => { + try { + const parsedEphemeralContainer: V1EphemeralContainer = yaml.load(ephemeralContainer); + const manifest: V1EphemeralContainers = { + apiVersion: 'v1', + ephemeralContainers: [parsedEphemeralContainer], + kind: 'EphemeralContainers', + metadata: { + name: resource.name.title, + namespace: resource.namespace.title, + }, + }; + + const response = await fetch( + `/api/plugins/resources/resources?cluster=${resource.cluster.title}${ + resource.namespace ? `&namespace=${resource.namespace.title}` : '' + }&name=${resource.name.title}&resource=pods&path=/api/v1&subResource=ephemeralcontainers`, + { + body: JSON.stringify(manifest), + method: 'post', + }, + ); + const json = await response.json(); + + if (response.status >= 200 && response.status < 300) { + setShow(false); + setAlert({ + title: `Ephemeral Container ${parsedEphemeralContainer.name} was created`, + variant: AlertVariant.success, + }); + refetch(); + } else { + if (json.error) { + throw new Error(json.error); + } else { + throw new Error('An unknown error occured'); + } + } + } catch (err) { + setShow(false); + setAlert({ title: err.message, variant: AlertVariant.danger }); + } + }; + + return ( + setShow(false)} + actions={[ + , + , + ]} + > + {ephemeralContainers && ephemeralContainers.length > 0 && ( + +
+ + setEphemeralContainer(value)} + id="create-ephemeral-container-form-container" + name="create-ephemeral-container-form-container" + aria-label="Template" + > + {ephemeralContainers.map((container, index) => ( + + ))} + + +
+

 

+
+ )} + + +
+ ); +}; + +export default CreateEphemeralContainer; diff --git a/plugins/resources/src/components/panel/details/actions/Terminal.tsx b/plugins/resources/src/components/panel/details/actions/Terminal.tsx index f62472d98..d1a9ca80f 100644 --- a/plugins/resources/src/components/panel/details/actions/Terminal.tsx +++ b/plugins/resources/src/components/panel/details/actions/Terminal.tsx @@ -39,6 +39,12 @@ const getContainers = (pod: V1Pod): string[] => { } } + if (pod.spec?.ephemeralContainers) { + for (const container of pod.spec?.ephemeralContainers) { + containers.push(container.name); + } + } + return containers; }; @@ -71,8 +77,6 @@ const Terminal: React.FunctionComponent = ({ request, resource, : undefined; const host = configuredWebSocketAddress || `wss://${window.location.host}`; - console.log(host); - const ws = new WebSocket( `${host}/api/plugins/resources/terminal?cluster=${resource.cluster.title}${ resource.namespace ? `&namespace=${resource.namespace.title}` : '' @@ -132,7 +136,7 @@ const Terminal: React.FunctionComponent = ({ request, resource, isOpen={show} onClose={(): void => setShow(false)} actions={[ - ,