eks.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package aws
  15. import (
  16. "fmt"
  17. "strings"
  18. "time"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/pkg/utils"
  22. api "yunion.io/x/cloudmux/pkg/apis/compute"
  23. "yunion.io/x/cloudmux/pkg/cloudprovider"
  24. "yunion.io/x/cloudmux/pkg/multicloud"
  25. )
  26. type SKubeCluster struct {
  27. multicloud.SResourceBase
  28. AwsTags
  29. region *SRegion
  30. Name string
  31. Arn string `json:"arn"`
  32. CreatedAt float64 `json:"createdAt"`
  33. Version string `json:"version"`
  34. Endpoint string `json:"endpoint"`
  35. RoleArn string `json:"roleArn"`
  36. ResourcesVpcConfig struct {
  37. SubnetIds []string `json:"subnetIds"`
  38. SecurityGroupIds []string `json:"securityGroupIds"`
  39. ClusterSecurityGroupId string `json:"clusterSecurityGroupId"`
  40. VpcId string `json:"vpcId"`
  41. EndpointPublicAccess bool `json:"endpointPublicAccess"`
  42. EndpointPrivateAccess bool `json:"endpointPrivateAccess"`
  43. PublicAccessCidrs []string `json:"publicAccessCidrs"`
  44. } `json:"resourcesVpcConfig"`
  45. KubernetesNetworkConfig struct {
  46. ServiceIpv4CIDR string `json:"serviceIpv4Cidr"`
  47. ServiceIpv6CIDR string `json:"serviceIpv6Cidr"`
  48. IPFamily string `json:"ipFamily"`
  49. } `json:"kubernetesNetworkConfig"`
  50. Logging struct {
  51. ClusterLogging []ClusterLogging `json:"clusterLogging"`
  52. } `json:"logging"`
  53. Identity string `json:"identity"`
  54. Status string `json:"status"`
  55. CertificateAuthority struct {
  56. Data string
  57. } `json:"certificateAuthority"`
  58. ClientRequestToken string `json:"clientRequestToken"`
  59. PlatformVersion string `json:"platformVersion"`
  60. EncryptionConfig string `json:"encryptionConfig"`
  61. ConnectorConfig string `json:"connectorConfig"`
  62. Id string `json:"id"`
  63. Health string `json:"health"`
  64. }
  65. type ClusterLogging struct {
  66. Types []string `json:"types"`
  67. Enabled bool `json:"enabled"`
  68. }
  69. func (self *SKubeCluster) GetName() string {
  70. return self.Name
  71. }
  72. func (self *SKubeCluster) GetId() string {
  73. return self.Name
  74. }
  75. func (self *SKubeCluster) GetGlobalId() string {
  76. return self.GetId()
  77. }
  78. func (self *SKubeCluster) GetEnabled() bool {
  79. return true
  80. }
  81. func (self *SKubeCluster) GetStatus() string {
  82. if len(self.Status) == 0 {
  83. self.Refresh()
  84. }
  85. switch self.Status {
  86. case "ACTIVE":
  87. return api.KUBE_CLUSTER_STATUS_RUNNING
  88. case "DELETING":
  89. return api.KUBE_CLUSTER_STATUS_DELETING
  90. default:
  91. return strings.ToLower(self.Status)
  92. }
  93. }
  94. func (self *SKubeCluster) GetKubeConfig(private bool, expireMinutes int) (*cloudprovider.SKubeconfig, error) {
  95. if len(self.CertificateAuthority.Data) == 0 {
  96. self.Refresh()
  97. }
  98. eksId := fmt.Sprintf("%s:%s:cluster/%s", self.region.RegionId, self.region.client.ownerId, self.Name)
  99. config := fmt.Sprintf(`apiVersion: v1
  100. clusters:
  101. - cluster:
  102. server: %s
  103. certificate-authority-data: %s
  104. name: arn:aws:eks:%s
  105. contexts:
  106. - context:
  107. cluster: arn:aws:eks:%s
  108. user: arn:aws:eks:%s
  109. name: arn:aws:eks:%s
  110. current-context: arn:aws:eks:%s
  111. kind: Config
  112. preferences: {}
  113. users:
  114. - name: arn:aws:eks:%s
  115. user:
  116. exec:
  117. apiVersion: client.authentication.k8s.io/v1beta1
  118. command: aws-iam-authenticator
  119. args:
  120. - "token"
  121. - "-i"
  122. - "%s"`, self.Endpoint, self.CertificateAuthority.Data, eksId, eksId, eksId, eksId, eksId, eksId, self.Name)
  123. return &cloudprovider.SKubeconfig{
  124. Config: config,
  125. }, nil
  126. }
  127. func (self *SKubeCluster) GetIKubeNodePools() ([]cloudprovider.ICloudKubeNodePool, error) {
  128. ret := []cloudprovider.ICloudKubeNodePool{}
  129. nextToken := ""
  130. for {
  131. part, nextToken, err := self.region.GetNodegroups(self.Name, nextToken)
  132. if err != nil {
  133. return nil, errors.Wrapf(err, "GetNodegroups")
  134. }
  135. for i := range part {
  136. ret = append(ret, &part[i])
  137. }
  138. if len(nextToken) == 0 {
  139. break
  140. }
  141. }
  142. return ret, nil
  143. }
  144. func (self *SKubeCluster) GetIKubeNodes() ([]cloudprovider.ICloudKubeNode, error) {
  145. return nil, cloudprovider.ErrNotImplemented
  146. }
  147. func (self *SKubeCluster) Delete(isRetain bool) error {
  148. return self.region.DeleteKubeCluster(self.Name)
  149. }
  150. func (self *SKubeCluster) GetCreatedAt() time.Time {
  151. return time.Unix(int64(self.CreatedAt), 0)
  152. }
  153. func (self *SKubeCluster) GetVpcId() string {
  154. if len(self.ResourcesVpcConfig.VpcId) == 0 {
  155. self.Refresh()
  156. }
  157. return self.ResourcesVpcConfig.VpcId
  158. }
  159. func (self *SKubeCluster) GetVersion() string {
  160. if len(self.Version) == 0 {
  161. self.Refresh()
  162. }
  163. return self.Version
  164. }
  165. func (self *SKubeCluster) GetNetworkIds() []string {
  166. if len(self.ResourcesVpcConfig.SubnetIds) == 0 {
  167. self.Refresh()
  168. }
  169. return self.ResourcesVpcConfig.SubnetIds
  170. }
  171. func (self *SKubeCluster) Refresh() error {
  172. cluster, err := self.region.GetKubeCluster(self.Name)
  173. if err != nil {
  174. return err
  175. }
  176. return jsonutils.Update(self, cluster)
  177. }
  178. func (self *SRegion) GetKubeClusters(nextToken string) ([]SKubeCluster, string, error) {
  179. ret := struct {
  180. Clusters []string
  181. NextToken string
  182. }{}
  183. params := map[string]interface{}{
  184. "include": "all",
  185. }
  186. if len(nextToken) > 0 {
  187. params["nextToken"] = nextToken
  188. }
  189. result := []SKubeCluster{}
  190. err := self.eksRequest("ListClusters", "/clusters", params, &ret)
  191. if err != nil {
  192. return nil, "", errors.Wrapf(err, "ListClusters")
  193. }
  194. for i := range ret.Clusters {
  195. result = append(result, SKubeCluster{
  196. region: self,
  197. Name: ret.Clusters[i],
  198. })
  199. }
  200. return result, ret.NextToken, nil
  201. }
  202. func (self *SRegion) GetKubeCluster(name string) (*SKubeCluster, error) {
  203. params := map[string]interface{}{
  204. "name": name,
  205. }
  206. ret := struct {
  207. Cluster SKubeCluster
  208. }{}
  209. err := self.eksRequest("DescribeCluster", "/clusters/{name}", params, &ret)
  210. if err != nil {
  211. return nil, errors.Wrapf(err, "DescribeCluster")
  212. }
  213. ret.Cluster.region = self
  214. return &ret.Cluster, nil
  215. }
  216. func (self *SRegion) DeleteKubeCluster(name string) error {
  217. params := map[string]interface{}{
  218. "name": name,
  219. }
  220. ret := struct {
  221. }{}
  222. return self.eksRequest("DeleteCluster", "/clusters/{name}", params, &ret)
  223. }
  224. func (self *SRegion) GetICloudKubeClusters() ([]cloudprovider.ICloudKubeCluster, error) {
  225. ret := []cloudprovider.ICloudKubeCluster{}
  226. nextToken := ""
  227. for {
  228. part, nextToken, err := self.GetKubeClusters(nextToken)
  229. if err != nil {
  230. return nil, errors.Wrapf(err, "GetKubeClusters")
  231. }
  232. for i := range part {
  233. part[i].region = self
  234. ret = append(ret, &part[i])
  235. }
  236. if len(nextToken) == 0 {
  237. break
  238. }
  239. }
  240. return ret, nil
  241. }
  242. func (self *SRegion) GetICloudKubeClusterById(id string) (cloudprovider.ICloudKubeCluster, error) {
  243. cluster, err := self.GetKubeCluster(id)
  244. if err != nil {
  245. return nil, err
  246. }
  247. return cluster, nil
  248. }
  249. func (self *SRegion) CreateIKubeCluster(opts *cloudprovider.KubeClusterCreateOptions) (cloudprovider.ICloudKubeCluster, error) {
  250. cluster, err := self.CreateKubeCluster(opts)
  251. if err != nil {
  252. return nil, err
  253. }
  254. return cluster, nil
  255. }
  256. func (self *SRegion) CreateKubeCluster(opts *cloudprovider.KubeClusterCreateOptions) (*SKubeCluster, error) {
  257. if !opts.PrivateAccess && !opts.PublicAccess { // avoid occur 'Private and public endpoint access cannot be false' error
  258. opts.PrivateAccess = true
  259. }
  260. params := map[string]interface{}{
  261. "name": opts.NAME,
  262. "clientRequestToken": utils.GenRequestId(20),
  263. "resourcesVpcConfig": map[string]interface{}{
  264. "endpointPrivateAccess": opts.PrivateAccess,
  265. "endpointPublicAccess": opts.PublicAccess,
  266. "subnetIds": opts.NetworkIds,
  267. },
  268. "tags": opts.Tags,
  269. }
  270. if len(opts.RoleName) == 0 {
  271. opts.RoleName = "eksClusterRole"
  272. }
  273. role, err := func() (*SRole, error) {
  274. role, err := self.client.GetRole(opts.RoleName)
  275. if err != nil {
  276. if errors.Cause(err) == cloudprovider.ErrNotFound {
  277. params := map[string]string{
  278. "RoleName": opts.RoleName,
  279. "Description": opts.Desc,
  280. "AssumeRolePolicyDocument": k8sRole,
  281. }
  282. role := struct {
  283. Role SRole
  284. }{}
  285. err := self.client.iamRequest("CreateRole", params, &role)
  286. if err != nil {
  287. return nil, errors.Wrapf(err, "CreateRole")
  288. }
  289. role.Role.client = self.client
  290. return &role.Role, nil
  291. }
  292. return nil, errors.Wrapf(err, "GetRole(%s)", opts.RoleName)
  293. }
  294. return role, nil
  295. }()
  296. if err != nil {
  297. return nil, err
  298. }
  299. err = self.client.AttachRolePolicy(opts.RoleName, self.client.getIamArn("AmazonEKSClusterPolicy"))
  300. if err != nil {
  301. return nil, errors.Wrapf(err, "AttachRolePolicy")
  302. }
  303. params["roleArn"] = role.Arn
  304. if len(opts.ServiceCIDR) > 0 {
  305. params["kubernetesNetworkConfig"] = map[string]interface{}{
  306. "ipFamily": "ipv4",
  307. "serviceIpv4Cidr": opts.ServiceCIDR,
  308. }
  309. }
  310. if len(opts.Version) > 0 {
  311. params["version"] = opts.Version
  312. }
  313. ret := struct {
  314. Cluster SKubeCluster
  315. }{}
  316. err = self.eksRequest("CreateCluster", "/clusters", params, &ret)
  317. if err != nil {
  318. return nil, err
  319. }
  320. ret.Cluster.region = self
  321. return &ret.Cluster, nil
  322. }
  323. func (self *SRegion) CreateNodegroup(cluster string, opts *cloudprovider.KubeNodePoolCreateOptions) (*SNodeGroup, error) {
  324. params := map[string]interface{}{
  325. "nodegroupName": opts.NAME,
  326. "clientRequestToken": utils.GenRequestId(20),
  327. "diskSize": opts.RootDiskSizeGb,
  328. "instanceTypes": opts.InstanceTypes,
  329. "tags": opts.Tags,
  330. "scalingConfig": map[string]interface{}{
  331. "desiredSize": opts.DesiredInstanceCount,
  332. "maxSize": opts.MaxInstanceCount,
  333. "minSize": opts.MinInstanceCount,
  334. },
  335. "subnets": opts.NetworkIds,
  336. }
  337. if len(opts.PublicKey) > 0 {
  338. keypairName, err := self.SyncKeypair(opts.PublicKey)
  339. if err != nil {
  340. return nil, errors.Wrapf(err, "syncKeypair")
  341. }
  342. params["remoteAccess"] = map[string]string{
  343. "ec2SshKey": keypairName,
  344. }
  345. }
  346. roleName := "AmazonEKSNodeRole"
  347. role, err := func() (*SRole, error) {
  348. role, err := self.client.GetRole(roleName)
  349. if err != nil {
  350. if errors.Cause(err) == cloudprovider.ErrNotFound {
  351. params := map[string]string{
  352. "RoleName": roleName,
  353. "Description": opts.Desc,
  354. "AssumeRolePolicyDocument": nodeRole,
  355. }
  356. role := struct {
  357. Role SRole
  358. }{}
  359. err := self.client.iamRequest("CreateRole", params, &role)
  360. if err != nil {
  361. return nil, errors.Wrapf(err, "CreateRole")
  362. }
  363. role.Role.client = self.client
  364. return &role.Role, nil
  365. }
  366. return nil, errors.Wrapf(err, "GetRole(%s)", roleName)
  367. }
  368. return role, nil
  369. }()
  370. if err != nil {
  371. return nil, errors.Wrapf(err, "Create role")
  372. }
  373. for _, policy := range []string{"AmazonEKSWorkerNodePolicy", "AmazonEC2ContainerRegistryReadOnly"} {
  374. err = self.client.AttachRolePolicy(roleName, self.client.getIamArn(policy))
  375. if err != nil {
  376. return nil, errors.Wrapf(err, "AttachRolePolicy %s", policy)
  377. }
  378. }
  379. params["nodeRole"] = role.Arn
  380. ret := struct {
  381. Nodegroup SNodeGroup
  382. }{}
  383. err = self.eksRequest("CreateNodegroup", fmt.Sprintf("/clusters/%s/node-groups", cluster), params, &ret)
  384. if err != nil {
  385. return nil, err
  386. }
  387. ret.Nodegroup.region = self
  388. return &ret.Nodegroup, nil
  389. }
  390. func (self *SKubeCluster) CreateIKubeNodePool(opts *cloudprovider.KubeNodePoolCreateOptions) (cloudprovider.ICloudKubeNodePool, error) {
  391. nodegroup, err := self.region.CreateNodegroup(self.Name, opts)
  392. if err != nil {
  393. return nil, errors.Wrapf(err, "CreateNodegroup")
  394. }
  395. return nodegroup, nil
  396. }
  397. func (self *SKubeCluster) GetDescription() string {
  398. return ""
  399. }