From 9f8ea923761e3bde95c42aff73d4898fc82ac88e Mon Sep 17 00:00:00 2001 From: Sooyoung Kim Date: Mon, 30 Dec 2024 06:35:32 +0000 Subject: [PATCH 1/3] FEAT: [k8scluster] support k8scluster's container remote commands --- go.mod | 32 ++++- go.sum | 47 +++++++ src/api/rest/server/resource/k8scluster.go | 41 +++++- src/api/rest/server/server.go | 2 + src/core/model/k8scluster.go | 27 +++- src/core/resource/k8scluster.go | 145 +++++++++++++++++++++ 6 files changed, 282 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index d7f1761a..84eaf5fc 100644 --- a/go.mod +++ b/go.mod @@ -24,9 +24,37 @@ require ( golang.org/x/crypto v0.31.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v2 v2.4.0 + k8s.io/api v0.31.3 + k8s.io/client-go v0.31.3 xorm.io/xorm v1.3.6 ) +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/moby/spdystream v0.4.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/x448/float16 v0.8.4 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/term v0.22.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/apimachinery v0.31.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect @@ -44,7 +72,7 @@ require ( github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -98,7 +126,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect google.golang.org/grpc v1.60.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect xorm.io/builder v0.3.13 // indirect diff --git a/go.sum b/go.sum index 4f4e94e4..db30e6d4 100644 --- a/go.sum +++ b/go.sum @@ -37,17 +37,23 @@ github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5O github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= @@ -88,21 +94,31 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -221,12 +237,18 @@ github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwp github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -321,6 +343,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -338,6 +362,7 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -499,6 +524,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -507,6 +534,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= @@ -523,6 +552,18 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= +k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= +k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= +k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= @@ -560,6 +601,12 @@ modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE= modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/src/api/rest/server/resource/k8scluster.go b/src/api/rest/server/resource/k8scluster.go index 9dd817e0..effaf084 100644 --- a/src/api/rest/server/resource/k8scluster.go +++ b/src/api/rest/server/resource/k8scluster.go @@ -208,7 +208,7 @@ func RestPutK8sCluster(c echo.Context) error { // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster01) -// @Param k8sNodeGroupReq body model.TbK8sNodeGroupReq true "Details of the K8sNodeGroup object" default(nodegroup01) +// @Param k8sNodeGroupReq body model.TbK8sNodeGroupReq true "Details of the K8sNodeGroup object" default(k8snodegroup01) // @Success 200 {object} model.TbK8sClusterInfo // @Failure 404 {object} model.SimpleMsg // @Failure 500 {object} model.SimpleMsg @@ -286,7 +286,7 @@ func RestDeleteK8sNodeGroup(c echo.Context) error { // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster01) -// @Param k8sNodeGroupName path string true "K8sNodeGroup Name" default(nodegroup01) +// @Param k8sNodeGroupName path string true "K8sNodeGroup Name" default(k8snodegroup01) // @Param setK8sNodeGroupAutoscalingReq body model.TbSetK8sNodeGroupAutoscalingReq true "Details of the TbSetK8sNodeGroupAutoscalingReq object" // @Success 200 {object} model.TbSetK8sNodeGroupAutoscalingRes // @Failure 404 {object} model.SimpleMsg @@ -326,7 +326,7 @@ func RestPutSetK8sNodeGroupAutoscaling(c echo.Context) error { // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster01) -// @Param k8sNodeGroupName path string true "K8sNodeGroup Name" default(nodegroup01) +// @Param k8sNodeGroupName path string true "K8sNodeGroup Name" default(k8snodegroup01) // @Param changeK8sNodeGroupAutoscaleSizeReq body model.TbChangeK8sNodeGroupAutoscaleSizeReq true "Details of the TbChangeK8sNodeGroupAutoscaleSizeReq object" // @Success 200 {object} model.TbChangeK8sNodeGroupAutoscaleSizeRes // @Failure 404 {object} model.SimpleMsg @@ -700,3 +700,38 @@ func RestRecommendK8sNode(c echo.Context) error { content, err := infra.RecommendK8sNode(nsId, *u) return common.EndRequestWithLog(c, err, content) } + +// RestPostCmdK8sCluster godoc +// @ID PostCmdK8sCluster +// @Summary Send a command to specified Container in K8sCluster +// @Description Send a command to specified Container in K8sCluster +// @Tags [Kubernetes] Cluster's Container Remote Command +// @Accept json +// @Produce json +// @Param nsId path string true "Namespace ID" default(default) +// @Param k8sClusterId path string true "K8sCluster ID" default(k8scluster01) +// @Param k8sClusterNamespace query string true "Namespace in K8sCluster to apply the command" default(mynamespace) +// @Param k8sClusterPodName query string true "Pod Name in K8sCluster to apply the command" default(mypod) +// @Param k8sClusterContainerName query string false "Container Name in K8sCluster to apply the command" +// @Param k8sClusterContainerCmdReq body model.TbK8sClusterContainerCmdReq true "K8sCluster's Container Command Request" +// @Param x-request-id header string false "Custom request ID" +// @Success 200 {object} model.TbK8sClusterContainerCmdResult +// @Failure 404 {object} model.SimpleMsg +// @Failure 500 {object} model.SimpleMsg +// @Router /ns/{nsId}/cmd/k8sCluster/{k8sClusterId} [post] +func RestPostCmdK8sCluster(c echo.Context) error { + + nsId := c.Param("nsId") + k8sClusterId := c.Param("k8sClusterId") + k8sClusterNamespace := c.QueryParam("k8sClusterNamespace") + k8sClusterPodName := c.QueryParam("k8sClusterPodName") + k8sClusterContainerName := c.QueryParam("k8sClusterContainerName") + + req := &model.TbK8sClusterContainerCmdReq{} + if err := c.Bind(req); err != nil { + return common.EndRequestWithLog(c, err, nil) + } + + content, err := resource.RemoteCommandToK8sClusterContainer(nsId, k8sClusterId, k8sClusterNamespace, k8sClusterPodName, k8sClusterContainerName, req) + return common.EndRequestWithLog(c, err, content) +} diff --git a/src/api/rest/server/server.go b/src/api/rest/server/server.go index 00986427..0a0d71be 100644 --- a/src/api/rest/server/server.go +++ b/src/api/rest/server/server.go @@ -410,6 +410,8 @@ func RunServer() { g.POST("/:nsId/k8sCluster/:k8sClusterId/k8sNodeGroupDynamic", rest_resource.RestPostK8sNodeGroupDynamic) g.GET("/:nsId/control/k8sCluster/:k8sClusterId", rest_resource.RestGetControlK8sCluster) + g.POST("/:nsId/cmd/k8sCluster/:k8sClusterId", rest_resource.RestPostCmdK8sCluster) + // Network Load Balancer g.POST("/:nsId/mci/:mciId/mcSwNlb", rest_infra.RestPostMcNLB) g.POST("/:nsId/mci/:mciId/nlb", rest_infra.RestPostNLB) diff --git a/src/core/model/k8scluster.go b/src/core/model/k8scluster.go index 5b4a534b..5352502a 100644 --- a/src/core/model/k8scluster.go +++ b/src/core/model/k8scluster.go @@ -82,7 +82,7 @@ type TbK8sClusterReq struct { // Tumblebug Description string `json:"description" example:"My K8sCluster"` // (1) K8sCluster Info - Name string `json:"name" validate:"required" example:"k8scluster-01"` + Name string `json:"name" validate:"required" example:"k8scluster01"` Version string `json:"version" example:"1.30.1-aliyun.1"` // (2) Network Info @@ -131,7 +131,7 @@ type SpiderNodeGroupReqInfo struct { // TbK8sNodeGroupReq is a struct to handle requests related to K8sNodeGroup toward CB-Tumblebug. type TbK8sNodeGroupReq struct { - Name string `json:"name" example:"k8snodegroup-01"` + Name string `json:"name" example:"k8snodegroup01"` ImageId string `json:"imageId" example:"image-01"` SpecId string `json:"specId" example:"spec-01"` RootDiskType string `json:"rootDiskType" example:"cloud_essd" enum:"default, TYPE1, ..."` // "", "default", "TYPE1", AWS: ["standard", "gp2", "gp3"], Azure: ["PremiumSSD", "StandardSSD", "StandardHDD"], GCP: ["pd-standard", "pd-balanced", "pd-ssd", "pd-extreme"], ALIBABA: ["cloud_efficiency", "cloud", "cloud_ssd"], TENCENT: ["CLOUD_PREMIUM", "CLOUD_SSD"] @@ -305,7 +305,7 @@ type TbK8sClusterInfo struct { ResourceType string `json:"resourceType"` // Id is unique identifier for the object, same as Name - Id string `json:"id" example:"k8scluster-01"` + Id string `json:"id" example:"k8scluster01"` // Uid is universally unique identifier for the object, used for labelSelector Uid string `json:"uid,omitempty" example:"wef12awefadf1221edcf"` // CspResourceName is name assigned to the CSP resource. This name is internally used to handle the resource. @@ -314,7 +314,7 @@ type TbK8sClusterInfo struct { CspResourceId string `json:"cspResourceId,omitempty" example:"csp-06eb41e14121c550a"` // Name is human-readable string to represent the object - Name string `json:"name" example:"k8scluster-01"` + Name string `json:"name" example:"k8scluster01"` ConnectionName string `json:"connectionName" example:"alibaba-ap-northeast-2"` // ConnectionConfig shows connection info to cloud service provider @@ -491,7 +491,7 @@ type CheckNodeDynamicReqInfo struct { // TbK8sClusterDynamicReq is struct for requirements to create K8sCluster dynamically (with default resource option) type TbK8sClusterDynamicReq struct { // K8sCluster name if it is not empty. - Name string `json:"name" validate:"required" example:"k8scluster-01"` + Name string `json:"name" validate:"required" example:"k8scluster01"` // K8s Clsuter version Version string `json:"version,omitempty" example:"1.29"` @@ -502,7 +502,7 @@ type TbK8sClusterDynamicReq struct { Description string `json:"description,omitempty" example:"Description"` // NodeGroup name if it is not empty - NodeGroupName string `json:"nodeGroupName,omitempty" example:"k8snodegroup-01"` + NodeGroupName string `json:"nodeGroupName,omitempty" example:"k8snodegroup01"` // CommonSpec is field for id of a spec in common namespace CommonSpec string `json:"commonSpec" validate:"required" example:"tencent+ap-seoul+S2.MEDIUM4"` @@ -526,7 +526,7 @@ type TbK8sClusterDynamicReq struct { // TbK8sNodeGroupDynamicReq is struct for requirements to create K8sNodeGroup dynamically (with default resource option) type TbK8sNodeGroupDynamicReq struct { // K8sNodeGroup name if it is not empty. - Name string `json:"name" validate:"required" example:"k8snodegroup-01"` + Name string `json:"name" validate:"required" example:"k8snodegroup01"` // Label is for describing the object by keywords Label map[string]string `json:"label,omitempty"` @@ -547,3 +547,16 @@ type TbK8sNodeGroupDynamicReq struct { MinNodeSize string `json:"minNodeSize,omitempty" default:"1" example:"1"` MaxNodeSize string `json:"maxNodeSize,omitempty" default:"2" example:"3"` } + +// TbK8sClusterContainerCmdReq is struct for remote command +type TbK8sClusterContainerCmdReq struct { + Command []string `json:"command" validate:"required" example:"echo hello"` +} + +// TbK8sClusterContainerCmdResult is struct for K8sClusterContainerCmd Result +type TbK8sClusterContainerCmdResult struct { + Command map[int]string `json:"command"` + Stdout map[int]string `json:"stdout"` + Stderr map[int]string `json:"stderr"` + Err error `json:"err"` +} diff --git a/src/core/resource/k8scluster.go b/src/core/resource/k8scluster.go index 8ad82e73..01b41e0d 100644 --- a/src/core/resource/k8scluster.go +++ b/src/core/resource/k8scluster.go @@ -15,6 +15,7 @@ limitations under the License. package resource import ( + "bytes" "encoding/json" "fmt" "reflect" @@ -30,6 +31,12 @@ import ( validator "github.com/go-playground/validator/v10" "github.com/go-resty/resty/v2" "github.com/rs/zerolog/log" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/remotecommand" ) // TbK8sClusterReqStructLevelValidation is a function to validate 'model.TbK8sClusterReq' object. @@ -1458,3 +1465,141 @@ func validateK8sVersion(providerName, regionName, version string) error { return nil } + +// RemoteCommandToK8sClusterContainer is func to command to specified Container in K8sCluster by Kubernetes API +func RemoteCommandToK8sClusterContainer(nsId string, k8sClusterId string, k8sClusterNamespace string, k8sClusterPodName string, k8sClusterContainerName string, req *model.TbK8sClusterContainerCmdReq) (*model.TbK8sClusterContainerCmdResult, error) { + log.Info().Msg("RemoteCommandToK8sClusterContainer") + + emptyObj := &model.TbK8sClusterContainerCmdResult{} + + err := validate.Struct(req) + if err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + log.Err(err).Msgf("Failed to Run Remote Command to K8sCluster(%s)'s Pod(%s)", k8sClusterId, k8sClusterPodName) + return emptyObj, err + } + + return emptyObj, err + } + + if len(req.Command) <= 0 { + err := fmt.Errorf("empty commands") + log.Err(err).Msgf("Failed to Run Remote Command to K8sCluster(%s)'s Pod(%s)", k8sClusterId, k8sClusterPodName) + return emptyObj, err + } + + check, err := CheckK8sCluster(nsId, k8sClusterId) + if err != nil { + log.Err(err).Msgf("Failed to Run Remote Command to K8sCluster(%s)'s Pod(%s)", k8sClusterId, k8sClusterPodName) + return emptyObj, err + } + + if !check { + err = fmt.Errorf("The K8sCluster(%s) does not exist", k8sClusterId) + return emptyObj, err + } + + // Execute commands + commandMap, stdoutMap, stderrMap, err := runRemoteCommandToK8sClusterContainer(nsId, k8sClusterId, k8sClusterPodName, k8sClusterNamespace, k8sClusterContainerName, req.Command) + return &model.TbK8sClusterContainerCmdResult{ + Command: commandMap, + Stdout: stdoutMap, + Stderr: stderrMap, + Err: err, + }, nil +} + +func getKubeconfigFromK8sClusterInfo(nsId, k8sClusterId string) (string, error) { + log.Debug().Msg("[Get Kubeconfig from K8sClusterInfo] " + k8sClusterId) + + tbK8sCInfo, err := getK8sClusterInfo(nsId, k8sClusterId) + if err != nil { + err = fmt.Errorf("failed to get kubeconfig from K8sClusterInfo(%s): %v", k8sClusterId, err) + return "", err + } + + if tbK8sCInfo.CspViewK8sClusterDetail.Status != model.SpiderClusterActive { + // Check K8sCluster's Status again + newTbK8sCInfo, err := GetK8sCluster(nsId, k8sClusterId) + if err != nil { + err = fmt.Errorf("failed to get kubeconfig from K8sClusterInfo(%s): %v", k8sClusterId, err) + return "", err + } + + if newTbK8sCInfo.CspViewK8sClusterDetail.Status != model.SpiderClusterActive { + err = fmt.Errorf("failed to get kubeconfig from K8sClusterInfo(%s): K8sCluster is not active", k8sClusterId) + return "", err + } + } + + return tbK8sCInfo.CspViewK8sClusterDetail.AccessInfo.Kubeconfig, nil +} + +func runRemoteCommandToK8sClusterContainer(nsId, k8sClusterId, k8sClusterPodName, k8sClusterNamespace, k8sClusterContainerName string, commands []string) (map[int]string, map[int]string, map[int]string, error) { + commandMap := make(map[int]string) + stdoutMap := make(map[int]string) + stderrMap := make(map[int]string) + + // Check whether K8sCluster is active + kubeconfig, err := getKubeconfigFromK8sClusterInfo(nsId, k8sClusterId) + if err != nil { + log.Err(err).Msgf("failed to run remote commands To K8sCluster(%s)'s container(%s)", k8sClusterId, k8sClusterContainerName) + return commandMap, stdoutMap, stderrMap, err + } + + // Access K8sCluster via kubeconfig + config, err := clientcmd.RESTConfigFromKubeConfig([]byte(kubeconfig)) + if err != nil { + log.Err(err).Msgf("failed to run remote commands To K8sCluster(%s)'s container(%s)", k8sClusterId, k8sClusterContainerName) + return commandMap, stdoutMap, stderrMap, err + } + + cset, err := kubernetes.NewForConfig(config) + if err != nil { + log.Err(err).Msgf("failed to run remote commands To K8sCluster(%s)'s container(%s)", k8sClusterId, k8sClusterContainerName) + return commandMap, stdoutMap, stderrMap, err + } + + for i, cmd := range commands { + // Split the command string into individual arguments + cmdArgs := strings.Fields(cmd) + + podExecOptions := &corev1.PodExecOptions{ + Container: k8sClusterContainerName, + Command: cmdArgs, + Stdout: true, + Stderr: true, + } + + req := cset.CoreV1().RESTClient(). + Post(). + Namespace(k8sClusterNamespace). + Resource("pods"). + Name(k8sClusterPodName). + SubResource("exec"). + VersionedParams(podExecOptions, scheme.ParameterCodec) + + var stdout, stderr bytes.Buffer + + executor, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL()) + if err != nil { + log.Err(err).Msgf("failed to run some remote command(%s) to K8sCluster(%s)'s Container(%s)", cmd, k8sClusterId, k8sClusterContainerName) + } else { + err = executor.Stream(remotecommand.StreamOptions{ + Stdout: &stdout, + Stderr: &stderr, + Tty: false, + }) + if err != nil { + log.Err(err).Msgf("failed to run some remote command(%s) to K8sCluster(%s)'s Container(%s)", cmd, k8sClusterId, k8sClusterContainerName) + return commandMap, stdoutMap, stderrMap, err + } + } + + commandMap[i] = cmd + stdoutMap[i] = stdout.String() + stderrMap[i] = stderr.String() + } + + return commandMap, stdoutMap, stderrMap, nil +} From 9840ecc4beef484e51b88bbe12a41896956880c8 Mon Sep 17 00:00:00 2001 From: Sooyoung Kim Date: Mon, 30 Dec 2024 06:36:34 +0000 Subject: [PATCH 2/3] CHORE: update k8sclusterinfo.yaml for alibaba --- assets/k8sclusterinfo.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/k8sclusterinfo.yaml b/assets/k8sclusterinfo.yaml index 575cfca7..46359adf 100644 --- a/assets/k8sclusterinfo.yaml +++ b/assets/k8sclusterinfo.yaml @@ -105,12 +105,12 @@ k8scluster: # ap-northeast-1,ap-northeast-2,ap-southeast-1,ap-southeast-3,ap-southeast-5,us-west-1,us-east-1,eu-central-1,eu-west-1,cn-beijing,cn-hongkong,cn-shanghai,cn-huhehaote,cn-heyuan,cn-wulanchabu,cn-guangzhou - region: [common] available: + - name: 1.31 + id: 1.31.1-aliyun.1 - name: 1.30 - id: 1.30.1-aliyun.1 + id: 1.30.7-aliyun.1 - name: 1.28 - id: 1.28.9-aliyun.1 - - name: 1.26 - id: 1.26.15-aliyun.1 + id: 1.28.15-aliyun.1 rootDisk: - region: [common] type: From f400cc545dc131958a354cc3d06e2c2cb4da71f3 Mon Sep 17 00:00:00 2001 From: Sooyoung Kim Date: Fri, 3 Jan 2025 16:45:09 +0900 Subject: [PATCH 3/3] Update description for /ns/{nsId}/cmd/k8sCluster/{k8sClusterId} API Co-authored-by: Seokho Son --- src/api/rest/server/resource/k8scluster.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/rest/server/resource/k8scluster.go b/src/api/rest/server/resource/k8scluster.go index effaf084..584d4242 100644 --- a/src/api/rest/server/resource/k8scluster.go +++ b/src/api/rest/server/resource/k8scluster.go @@ -705,6 +705,9 @@ func RestRecommendK8sNode(c echo.Context) error { // @ID PostCmdK8sCluster // @Summary Send a command to specified Container in K8sCluster // @Description Send a command to specified Container in K8sCluster +// @Description [note] This feature is not intended for general use +// @Description This API is provided as an exceptional and limited function for specific purposes such as migration. +// @Description Kubernetes resource information required as input for this API is not currently provided, and its availability in the future is uncertain. // @Tags [Kubernetes] Cluster's Container Remote Command // @Accept json // @Produce json