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={[ - ,