feat(multicloud): support webapp for azure

This commit is contained in:
rainzm
2021-08-02 11:45:50 +08:00
parent f8e53027d6
commit 64f5e09439
7 changed files with 816 additions and 4 deletions
+199
View File
@@ -0,0 +1,199 @@
// Copyright 2019 Yunion
//
// 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 azure
import (
"fmt"
"net/url"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SAppServicePlan struct {
multicloud.AzureTags
region *SRegion
Properties SAppServicePlanProperties
ID string
Kind string
Location string
Name string
Type string
Sku SSkuDescription
}
type SAppServicePlanProperties struct {
FreeOfferExpirationTime string
GeoRegion string
HostingEnvironmentProfile SHostingEnvironmentProfile
HyperV bool
IsSpot bool
MaximumElasticWorkerCount int
MaximumNumberOfWorkers int
NumberOfSites int
PerSiteScaling bool
// 应用服务环境的预配状态
ProvisioningState string
Reserved bool
ResourceGroup string
SpotExpirationTime string
// Creating Pending Ready
Status string
Subscription string
TargetWorkerCount int
TargetWorkerSizeId int
WorkerTierName string
}
type SHostingEnvironmentProfile struct {
ID string
Name string
Type string
}
type SSkuDescription struct {
Capabilities []SCapability
Capacity int
Family string
locations []string
Name string
Size string
SkuCapacity SSkuCapacity
Tier string
}
type SCapability struct {
Name string
Reason string
Value string
}
type SSkuCapacity struct {
Default int
Maximum int
Minimum int
ScaleType string
}
func (r *SRegion) GetAppServicePlanWithCache(id string) (*SAppServicePlan, error) {
if r.appServicePlans == nil {
plans, err := r.GetAppServicePlans()
if err != nil {
return nil, err
}
appServicePlans := make(map[string]*SAppServicePlan, len(plans))
for i := range plans {
appServicePlans[plans[i].ID] = &plans[i]
}
r.appServicePlans = appServicePlans
}
if ret, ok := r.appServicePlans[id]; ok {
return ret, nil
}
return r.GetAppServicePlan(id)
}
func (r *SRegion) GetAppServicePlans() ([]SAppServicePlan, error) {
result := []SAppServicePlan{}
resource := "Microsoft.Web/serverfarms"
err := r.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
for i := range result {
result[i].region = r
}
return result, nil
}
func (r *SRegion) GetAppServicePlan(planId string) (*SAppServicePlan, error) {
asp := &SAppServicePlan{region: r}
params := url.Values{"api-version": []string{"2019-08-01"}}
return asp, r.get(planId, params, asp)
}
type SServerFarmSku struct {
ResourceType string
Sku SSkuSpec
Capacity SSkuCapacity
}
type SSkuSpec struct {
Name string
Tier string
}
func (asp *SAppServicePlan) GetSkus() ([]SServerFarmSku, error) {
result := []SServerFarmSku{}
resource := fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/serverfarms/%s/skus", asp.region.client._subscriptionId(), asp.Properties.ResourceGroup, asp.Name)
err := asp.region.client.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
return result, nil
}
type SAppServicePlanVnet struct {
Id string
Name string
Properties SASPVnetProperties
}
type SASPVnetProperties struct {
VnetResourceId string
}
func (asp *SAppServicePlan) GetNetworks() ([]SNetwork, error) {
vnets, err := asp.GetVnets()
if err != nil {
return nil, errors.Wrap(err, "unable to get Vnets")
}
networks := make([]SNetwork, 0, len(vnets))
for i := range vnets {
network, err := asp.region.GetNetwork(vnets[i].Properties.VnetResourceId)
if err != nil {
return nil, errors.Wrap(err, "unable to get Network")
}
networks = append(networks, *network)
}
return networks, nil
}
func (asp *SAppServicePlan) GetVnets() ([]SAppServicePlanVnet, error) {
result := []SAppServicePlanVnet{}
resource := fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/serverfarms/%s/virtualNetworkConnections", asp.region.client._subscriptionId(), asp.Properties.ResourceGroup, asp.Name)
err := asp.region.client.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
return result, nil
}
type SSkuInfos struct {
ResourceType string
Skus []SResourceSku
}
func (r *SRegion) GetAppServiceSkus() ([]SSkuInfos, error) {
result := []SSkuInfos{}
resource := "Microsoft.Web/skus"
err := r.client.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
return result, err
}
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2019 Yunion
//
// 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 azure
import (
"net/url"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SAutoscaleSettingResource struct {
multicloud.AzureTags
region *SRegion
Properties SAutoscaleSettingProperties
ID string
Location string
Name string
Type string
}
type SAutoscaleSettingProperties struct {
Enabled bool
Name string
TargetResourceUri string
Profiles []SAutoscaleProfile
}
type SAutoscaleProfile struct {
Capacity SScaleCapacity
FixedDate STimeWindow
Name string
Recurrence SRecurrence
Rule []SScaleRule
}
type SRecurrence struct {
Frequency string
Schedule SRecurrentSchedule
}
type SRecurrentSchedule struct {
Days []string
Hours []int
Minutes []int
TimeZone string
}
type STimeWindow struct {
// 采用 ISO 8601 格式的配置文件的结束时间。
End string
// 采用 ISO 8601 格式的配置文件的开始时间。
Start string
TimeZone string
}
type SScaleAction struct {
Cooldown string
Direction string
Type string
Value string
}
// 可以在此配置文件期间使用的实例数。
type SScaleCapacity struct {
Default string
Maximum string
Minimum string
}
// Decrease string
// Increase string
// None string
// 为缩放操作提供触发器和参数的规则。
type SScaleRule struct {
MetricTrigger SMetricTrigger
ScaleAction SScaleAction
}
// 维度运算符。 仅支持"Equals"和"NotEquals"。 "Equals"等于任何值。 "NotEquals"不等于所有值
type SScaleRuleMetricDimensionOperationType struct {
Equals string
NotEquals string
}
//触发缩放规则时应发生的操作类型。
// ChangeCount string
// ExactCount string
// PercentChangeCount string
type SScaleRuleMetricDimension struct {
DimensionName string
Operator string
Values []string
}
type SMetricTrigger struct {
// 维度条件的列表。 例如: [{"DimensionName": "AppName"、"Operator": "Equals"、"Values": ["App1"]}、{"DimensionName": "Deployment"、"Operator": "Equals"、"Values": ["default"]}]。
Dimensions []SScaleRuleMetricDimension
// 一个值,该值指示度量值是否应除以每个实例。
DividePerInstance bool
// 定义规则监视内容的指标的名称。
MetricName string
// 定义规则监视内容的指标的命名空间。
MetricNamespace string
// 规则监视的资源的资源标识符。
MetricResourceUri string
// 用于比较指标数据和阈值的运算符。
// Equals GreaterThan GreaterThanOrEqual LessThan LessThanOrEqual NotEquals
Operator string
// 指标统计信息类型。 来自多个实例的指标进行组合的方式。
// Average Max Min Sum
Statistic string
// 触发缩放操作的度量值的阈值。
Threshold int
// 时间聚合类型。 随着时间推移,收集的数据应如何组合。 默认值为 Average。
// Average Count Last Maximum Minimum Total
TimeAggregation string
//规则监视的度量值的粒度。 必须是从指标的指标定义返回的预定义值之一。 必须介于 12 小时和 1 分钟之间。
TimeGrain string
//收集实例数据的时间范围。 此值必须大于指标集合中的延迟,可能会因资源而异。 必须介于 12 小时和 5 分钟之间。
TimeWindow string
}
func (r *SRegion) GetAutoscaleSettingResources() ([]SAutoscaleSettingResource, error) {
result := []SAutoscaleSettingResource{}
resource := "microsoft.insights/autoscalesettings"
err := r.client.list(resource, url.Values{"api-version": []string{"2015-04-01"}}, &result)
if err != nil {
return nil, err
}
return result, nil
}
+8 -3
View File
@@ -508,10 +508,14 @@ func (self *SAzureClient) _list(resource string, params url.Values) (jsonutils.J
path = fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/%s", subId, params.Get("resourceGroups"), resource)
params.Del("resourceGroups")
default:
if len(subId) == 0 {
return nil, fmt.Errorf("no avaiable subscriptions")
if strings.HasPrefix(resource, "subscriptions/") || strings.HasPrefix(resource, "/subscriptions/") {
path = resource
} else {
if len(subId) == 0 {
return nil, fmt.Errorf("no avaiable subscriptions")
}
path = fmt.Sprintf("subscriptions/%s/providers/%s", subId, resource)
}
path = fmt.Sprintf("subscriptions/%s/providers/%s", subId, resource)
}
params.Set("api-version", self._apiVersion(resource, params))
return self.jsonRequest("GET", path, nil, params, true)
@@ -976,6 +980,7 @@ func (self *SAzureClient) GetCapabilities() []string {
cloudprovider.CLOUD_CAPABILITY_SAML_AUTH,
cloudprovider.CLOUD_CAPABILITY_WAF,
cloudprovider.CLOUD_CAPABILITY_CACHE,
cloudprovider.CLOUD_CAPABILITY_APP,
}
return caps
}
+2 -1
View File
@@ -42,7 +42,8 @@ type SRegion struct {
multicloud.SRegion
client *SAzureClient
storageCache *SStoragecache
storageCache *SStoragecache
appServicePlans map[string]*SAppServicePlan
ID string
Name string
@@ -0,0 +1,81 @@
// Copyright 2019 Yunion
//
// 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 shell
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/multicloud/azure"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type AppServicePlanListOptions struct {
}
shellutils.R(&AppServicePlanListOptions{}, "app-service-plan-list", "List app service plan", func(cli *azure.SRegion, args *AppServicePlanListOptions) error {
appServicePlans, err := cli.GetAppServicePlans()
if err != nil {
return err
}
fmt.Printf("sku: %s\n", jsonutils.Marshal(appServicePlans[0].Sku))
printList(appServicePlans, len(appServicePlans), 0, 0, []string{})
return nil
})
type AppServicePlanShowOptions struct {
ID string
}
shellutils.R(&AppServicePlanShowOptions{}, "app-service-plan-show", "Show app service plan", func(cli *azure.SRegion, args *AppServicePlanShowOptions) error {
appServicePlan, err := cli.GetAppServicePlan(args.ID)
if err != nil {
return err
}
printObject(appServicePlan)
return nil
})
shellutils.R(&AppServicePlanListOptions{}, "autoscale-setting-list", "List autoscale setting in app service plan", func(cli *azure.SRegion, args *AppServicePlanListOptions) error {
appServicePlans, err := cli.GetAutoscaleSettingResources()
if err != nil {
return err
}
printList(appServicePlans, len(appServicePlans), 0, 0, []string{})
return nil
})
shellutils.R(&AppServicePlanShowOptions{}, "app-service-sku-list", "List sku usable in app service plan", func(cli *azure.SRegion, args *AppServicePlanShowOptions) error {
appServicePlan, err := cli.GetAppServicePlan(args.ID)
if err != nil {
return err
}
skus, err := appServicePlan.GetSkus()
if err != nil {
return err
}
printList(skus, len(skus), 0, 0, []string{})
return nil
})
shellutils.R(&AppServicePlanShowOptions{}, "app-service-network-list", "List network connected with app service plan", func(cli *azure.SRegion, args *AppServicePlanShowOptions) error {
appServicePlan, err := cli.GetAppServicePlan(args.ID)
if err != nil {
return err
}
networks, err := appServicePlan.GetNetworks()
if err != nil {
return err
}
printList(networks, len(networks), 0, 0, []string{})
return nil
})
}
+68
View File
@@ -0,0 +1,68 @@
// Copyright 2019 Yunion
//
// 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 shell
import (
"yunion.io/x/onecloud/pkg/multicloud/azure"
"yunion.io/x/onecloud/pkg/util/shellutils"
)
func init() {
type AppSiteListOptions struct {
}
shellutils.R(&AppSiteListOptions{}, "app-site-list", "List app service plan", func(cli *azure.SRegion, args *AppSiteListOptions) error {
ass, err := cli.GetAppSites()
if err != nil {
return err
}
printList(ass, len(ass), 0, 0, []string{})
return nil
})
type AppSiteShowOptions struct {
ID string
}
shellutils.R(&AppSiteShowOptions{}, "app-site-show", "Show app service plan", func(cli *azure.SRegion, args *AppSiteShowOptions) error {
as, err := cli.GetAppSite(args.ID)
if err != nil {
return err
}
printObject(as)
return nil
})
shellutils.R(&AppSiteShowOptions{}, "app-site-deployment-list", "List sku usable in app service plan", func(cli *azure.SRegion, args *AppSiteShowOptions) error {
as, err := cli.GetAppSite(args.ID)
if err != nil {
return err
}
deployments, err := as.GetDeployments()
if err != nil {
return err
}
printList(deployments, len(deployments), 0, 0, []string{})
return nil
})
shellutils.R(&AppSiteShowOptions{}, "app-site-slot-list", "List slots ofr App site", func(cli *azure.SRegion, args *AppSiteShowOptions) error {
as, err := cli.GetAppSite(args.ID)
if err != nil {
return err
}
slots, err := as.GetSlots()
if err != nil {
return err
}
printList(slots, len(slots), 0, 0, []string{})
return nil
})
}
+302
View File
@@ -0,0 +1,302 @@
// Copyright 2019 Yunion
//
// 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 azure
import (
"fmt"
"net/url"
"strings"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/multicloud"
)
type SAppSite struct {
multicloud.AzureTags
region *SRegion
appServicePlan *SAppServicePlan
Properties SAppSiteProperties
Id string
Name string
Kind string
Location string
Type string
stack *string
}
type SAppSiteProperties struct {
AvailabilityState string
DefaultHostName string
Enabled bool
EnabledHostNames []string
HostNameSslStates []SHostNameSslState
HostNames []string
HttpsOnly bool
OutboundIpAddresses string
ResourceGroup string
ServerFarmId string
SiteConfig SSiteConfig
State string
}
type SSiteConfig struct {
// TODO
}
type SHostNameSslState struct {
HostType string
Name string
SslState string
Thumbprint string
ToUpdate bool
VirtualIP string
}
func (r *SRegion) GetAppSites() ([]SAppSite, error) {
result := []SAppSite{}
resource := "Microsoft.Web/sites"
err := r.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
for i := range result {
result[i].region = r
}
return result, nil
}
func (r *SRegion) GetICloudApps() ([]cloudprovider.ICloudApp, error) {
ass, err := r.GetAppSites()
if err != nil {
return nil, err
}
apps := make([]cloudprovider.ICloudApp, 0, len(ass))
for i := range ass {
apps = append(apps, &SApp{
SAppSite: ass[i],
})
}
return apps, nil
}
func (r *SRegion) GetICloudAppById(id string) (cloudprovider.ICloudApp, error) {
as, err := r.GetAppSite(id)
if err != nil {
return nil, err
}
return &SApp{
SAppSite: *as,
}, nil
}
func (as *SAppSite) GetSlots() ([]SAppSite, error) {
result := []SAppSite{}
resource := fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s/slots", as.region.client.subscriptionId, as.Properties.ResourceGroup, as.Name)
err := as.region.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
return result, nil
}
func (r *SRegion) GetAppSite(siteId string) (*SAppSite, error) {
as := &SAppSite{region: r}
params := url.Values{"api-version": []string{"2019-08-01"}}
return as, r.get(siteId, params, as)
}
func (as *SAppSite) GetAppServicePlan() (*SAppServicePlan, error) {
if as.appServicePlan != nil {
return as.appServicePlan, nil
}
plan, err := as.region.GetAppServicePlanWithCache(as.Properties.ServerFarmId)
if err != nil {
return nil, errors.Wrap(err, "unable to get AppServicePlan")
}
as.appServicePlan = plan
return plan, nil
}
type SDeployment struct {
ID string
Name string
Kind string
Type string
Properties SDeploymentProperties
}
type SDeploymentProperties struct {
Active bool
Author string
AuthorEmail string
Deployer string
Details string
EndTime string
Message string
StartTime string
Status string
}
func (as *SAppSite) GetDeployments() ([]SDeployment, error) {
result := []SDeployment{}
resource := fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s/deployments", as.region.client._subscriptionId(), as.Properties.ResourceGroup, as.Name)
err := as.region.client.list(resource, url.Values{"api-version": []string{"2019-08-01"}}, &result)
if err != nil {
return nil, err
}
return result, nil
}
type SStringDictionary struct {
Id string
Kind string
Name string
Type string
Properties map[string]string
}
func (as *SAppSite) GetStack() (string, error) {
if as.stack != nil {
return *as.stack, nil
}
// result := SStringDictionary{}
resource := fmt.Sprintf("subscriptions/%s/resourceGroups/%s/providers/Microsoft.Web/sites/%s/config/metadata", as.region.client.subscriptionId, as.Properties.ResourceGroup, as.Name)
params := url.Values{}
params.Set("api-version", "2019-08-01")
path := resource + "/list"
response, err := as.region.client.jsonRequest("POST", path, nil, params, true)
if err != nil {
return "", err
}
stack, err := response.GetString("properties", "CURRENT_STACK")
if err != nil {
return "", errors.Wrapf(err, "unable to parse response correctly, response: %s", response)
}
as.stack = &stack
return stack, nil
}
func (as *SAppSite) GetId() string {
return as.Id
}
func (as *SAppSite) GetName() string {
return as.Name
}
func (as *SAppSite) GetGlobalId() string {
return strings.ToLower(as.Id)
}
func (as *SAppSite) GetStatus() string {
return "ready"
}
func (as *SAppSite) GetProjectId() string {
return strings.ToLower(as.Properties.ResourceGroup)
}
type SApp struct {
multicloud.SResourceBase
multicloud.AzureTags
SAppSite
}
type SAppEnvironment struct {
multicloud.SResourceBase
multicloud.AzureTags
SAppSite
}
func (ae *SAppEnvironment) GetInstanceType() (string, error) {
asp, err := ae.SAppSite.GetAppServicePlan()
if err != nil {
return "", err
}
return asp.Sku.Name, nil
}
func (ae *SAppEnvironment) GetInstanceNumber() (int, error) {
asp, err := ae.SAppSite.GetAppServicePlan()
if err != nil {
return 0, err
}
return asp.Sku.Capacity, nil
}
func (a *SApp) GetEnvironments() ([]cloudprovider.ICloudAppEnvironment, error) {
sites, err := a.SAppSite.GetSlots()
if err != nil {
return nil, err
}
aes := make([]cloudprovider.ICloudAppEnvironment, 0, len(sites)+1)
aes = append(aes, &SAppEnvironment{
SAppSite: a.SAppSite,
})
for i := range sites {
sites[i].region = a.region
aes = append(aes, &SAppEnvironment{
SAppSite: sites[i],
})
}
return aes, nil
}
var techStacks = map[string]string{
"dotnet": ".NET",
"dotnetcore": ".NET Core",
"aspdotnet": "ASP.NET",
"node": "Node",
"python": "Python",
"php": "PHP",
"ruby": "Ruby",
"java": "Java",
"javacontainers": "Jave Containers",
}
func (a *SApp) GetTechStack() string {
if strings.Contains(a.SAppSite.Kind, "container") {
return "Docker container"
}
stack, err := a.GetStack()
if err != nil {
log.Errorf("unable to GetStack: %v", err)
}
if s, ok := techStacks[stack]; ok {
return s
}
return stack
}
func (a *SApp) GetType() string {
return "web"
}
func (a *SApp) GetKind() string {
return a.Kind
}
func (a *SApp) GetOsType() string {
if strings.Contains(a.Kind, "linux") {
return "Linux"
}
return "Windows"
}