Skip to content

Commit

Permalink
improve authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
daimaxiaxie committed Dec 26, 2024
1 parent ad48b60 commit b4a238b
Show file tree
Hide file tree
Showing 39 changed files with 284 additions and 4,845 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.4
controller-gen.kubebuilder.io/version: v0.16.5
name: ecsnodeclasses.karpenter.k8s.alibabacloud
spec:
group: karpenter.k8s.alibabacloud
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
github.com/alibabacloud-go/tea v1.2.2
github.com/alibabacloud-go/tea-utils/v2 v2.0.6
github.com/alibabacloud-go/vpc-20160428/v6 v6.10.4
github.com/aliyun/aliyun-cli v0.0.0-20240925084117-158a70e275f0
github.com/awslabs/operatorpkg v0.0.0-20240805231134-67d0acfb6306
github.com/cloudpilot-ai/priceserver v0.0.0-20241011010411-15ac0e19a857
github.com/mitchellh/hashstructure/v2 v2.0.2
Expand Down Expand Up @@ -42,7 +41,7 @@ require (
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
github.com/alibabacloud-go/tea-utils v1.3.1 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
github.com/aliyun/credentials-go v1.3.10 // indirect
github.com/aliyun/credentials-go v1.3.10
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzY
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/alibabacloud-go/vpc-20160428/v6 v6.10.4 h1:6OXPOw1WcEjoSCOPFtKKgFFSlSSapns0uoOML+hzn8M=
github.com/alibabacloud-go/vpc-20160428/v6 v6.10.4/go.mod h1:6516WWE4Y9lzscVSfaev84DM+TQSvBEGX1oeMvDL5xk=
github.com/aliyun/aliyun-cli v0.0.0-20240925084117-158a70e275f0 h1:GyWC5h0inFp+vmJVbbRGh34KTj/HeG/tyqJwR+pQbko=
github.com/aliyun/aliyun-cli v0.0.0-20240925084117-158a70e275f0/go.mod h1:LRvAoBmigy35079C5FYKseCCHG4oCCC0dEgyuSsAVeo=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
Expand Down
2 changes: 1 addition & 1 deletion pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type Operator struct {
}

func NewOperator(ctx context.Context, operator *operator.Operator) (context.Context, *Operator) {
clientConfig, err := client.NewClientConfig()
clientConfig, err := client.NewClientConfig(ctx)
if err != nil {
log.FromContext(ctx).Error(err, "Failed to create client config")
os.Exit(1)
Expand Down
36 changes: 24 additions & 12 deletions pkg/utils/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,42 @@ limitations under the License.
package client

import (
"errors"
"context"
"fmt"
"os"

openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
aliyunconfig "github.com/aliyun/aliyun-cli/config"
"github.com/aliyun/credentials-go/credentials"
"sigs.k8s.io/controller-runtime/pkg/log"

"github.com/cloudpilot-ai/karpenter-provider-alibabacloud/pkg/utils/client/metadata"
)

func NewClientConfig() (*openapi.Config, error) {
profile, err := aliyunconfig.LoadCurrentProfile()
func NewClientConfig(ctx context.Context) (*openapi.Config, error) {
// Load in the following order: 1. AK/SK, 2. RRSA, 3. config.json, 4. RAMRole
// https://www.alibabacloud.com/help/zh/sdk/developer-reference/v2-manage-go-access-credentials#3ca299f04bw3c
credential, err := credentials.NewCredential(nil)
if err != nil {
return nil, err
}

if profile.RegionId == "" {
return nil, errors.New("regionId must be set in the config file")
if cred, err := credential.GetCredential(); err == nil && cred != nil {
log.FromContext(ctx).Info(fmt.Sprintf("Using credential type: %s, AccessKeyID: %s", tea.StringValue(cred.Type), tea.StringValue(cred.AccessKeyId)))
} else {
return nil, fmt.Errorf("failed get credential, error: %s", err)

Check failure on line 42 in pkg/utils/client/client.go

View workflow job for this annotation

GitHub Actions / presubmit

non-wrapping format verb for fmt.Errorf. Use `%w` to format errors (errorlint)
}

credentialClient, err := profile.GetCredential(nil, nil)
if err != nil {
return nil, err
region := os.Getenv("REGION_ID")
if region == "" {
region, err = metadata.NewMetaData(nil).Region()
if err != nil {
return nil, err
}
}

return &openapi.Config{
RegionId: tea.String(profile.RegionId),
Credential: credentialClient,
RegionId: tea.String(region),
Credential: credential,
Network: tea.String(os.Getenv("ALIBABA_CLOUD_NETWORK")), // 1. public, 2. vpc, default is public
}, nil
}
257 changes: 257 additions & 0 deletions pkg/utils/client/metadata/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*
Copyright 2024 The CloudPilot AI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package metadata

import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"reflect"
"strings"
"time"
)

const (
Endpoint = "http://100.100.100.200"
hostname = "hostname"
instanceID = "instance-id"
regionID = "region-id"
ramSecurity = "ram/security-credentials"
)

// MetaData wrap http client
type MetaData struct {
// mock for unit test.
mock requestMock
client *http.Client
}

// NewMetaData returns MetaData
func NewMetaData(client *http.Client) *MetaData {
if client == nil {
client = &http.Client{}
}
return &MetaData{
client: client,
}
}

// NewMockMetaData returns mock MetaData
func NewMockMetaData(client *http.Client, sendRequest requestMock) *MetaData {
if client == nil {
client = &http.Client{}
}
return &MetaData{
client: client,
mock: sendRequest,
}
}

// New returns MetaDataRequest
func (m *MetaData) New() *MetaDataRequest {
return &MetaDataRequest{
client: m.client,
sendRequest: m.mock,
}
}

// HostName returns host name
func (m *MetaData) HostName() (string, error) {
var name ResultList
err := m.New().Resource(hostname).Do(&name)
if err != nil {
return "", err
}
return name.result[0], nil
}

// InstanceID returns instance Id
func (m *MetaData) InstanceID() (string, error) {
var instanceid ResultList
err := m.New().Resource(instanceID).Do(&instanceid)
if err != nil {
return "", err
}
return instanceid.result[0], err
}

// Region returns region
func (m *MetaData) Region() (string, error) {
var region ResultList
err := m.New().Resource(regionID).Do(&region)
if err != nil {
return "", err
}
return region.result[0], nil
}

// RoleName returns role name
func (m *MetaData) RoleName() (string, error) {
var roleName ResultList
err := m.New().Resource("ram/security-credentials/").Do(&roleName)
if err != nil {
return "", err
}
return roleName.result[0], nil
}

// RamRoleToken returns ram role token
func (m *MetaData) RAMRoleToken(role string) (RoleAuth, error) {
var roleauth RoleAuth
err := m.New().Resource(ramSecurity).SubResource(role).Do(&roleauth)
if err != nil {
return RoleAuth{}, err
}
return roleauth, nil
}

type requestMock func(resource string) (string, error)

// ResultList struct
type ResultList struct {
result []string
}

// nolint: stylecheck
// RoleAuth struct
type RoleAuth struct {
AccessKeyId string
AccessKeySecret string
Expiration time.Time
SecurityToken string
LastUpdated time.Time
Code string
}

// MetaDataRequest struct
type MetaDataRequest struct {
version string
resourceType string
resource string
subResource string
client *http.Client

sendRequest requestMock
}

// Version sets version
func (r *MetaDataRequest) Version(version string) *MetaDataRequest {
r.version = version
return r
}

// ResourceType sets resource type
func (r *MetaDataRequest) ResourceType(rtype string) *MetaDataRequest {
r.resourceType = rtype
return r
}

// Resource sets resource
func (r *MetaDataRequest) Resource(resource string) *MetaDataRequest {
r.resource = resource
return r
}

// SubResource set sub resource
func (r *MetaDataRequest) SubResource(sub string) *MetaDataRequest {
r.subResource = sub
return r
}

// URL returns url
func (r *MetaDataRequest) URL() (string, error) {
if r.version == "" {
r.version = "latest"
}
if r.resourceType == "" {
r.resourceType = "meta-data"
}
if r.resource == "" {
return "", errors.New("the resource you want to visit must not be nil")
}
endpoint := os.Getenv("METADATA_ENDPOINT")
if endpoint == "" {
endpoint = Endpoint
}
url := fmt.Sprintf("%s/%s/%s/%s", endpoint, r.version, r.resourceType, r.resource)
if r.subResource == "" {
return url, nil
}
return fmt.Sprintf("%s/%s", url, r.subResource), nil
}

// Do try to do MetaDataRequest
func (r *MetaDataRequest) Do(api interface{}) (err error) {
res := ""

if r.sendRequest != nil {
res, err = r.sendRequest(r.resource)
} else {
res, err = r.send()
}

if err != nil {
return err
}
return r.Decode(res, api)
}

// Decode returns decoded content
func (r *MetaDataRequest) Decode(data string, api interface{}) error {
if data == "" {
url, _ := r.URL()
return fmt.Errorf("metadata: alivpc decode data must not be nil. url=[%s]", url)
}
switch api := api.(type) {
case *ResultList:
api.result = strings.Split(data, "\n")
return nil
case *RoleAuth:
return json.Unmarshal([]byte(data), api)
default:
return fmt.Errorf("metadata: unknow type to decode, type=%s", reflect.TypeOf(api))
}
}

func (r *MetaDataRequest) send() (string, error) {
url, err := r.URL()
if err != nil {
return "", err
}
req, err := http.NewRequest(http.MethodGet, url, nil)

if err != nil {
return "", err
}
resp, err := r.client.Do(req)
if err != nil {
return "", err
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("aliyun Metadata API Error: Status Code: %d", resp.StatusCode)
}
defer resp.Body.Close()

data, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(data), nil
}
Loading

0 comments on commit b4a238b

Please sign in to comment.