From bb7c4aaf7eea48db663b6dbc841d19680f7c769e Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 12 Sep 2018 16:32:04 +0200 Subject: [PATCH 01/10] Fix tracing duplicated headers --- middlewares/tracing/carrier.go | 25 +++++++++++++++++++++++++ middlewares/tracing/entrypoint.go | 2 +- middlewares/tracing/tracing.go | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 middlewares/tracing/carrier.go diff --git a/middlewares/tracing/carrier.go b/middlewares/tracing/carrier.go new file mode 100644 index 000000000..57f54865d --- /dev/null +++ b/middlewares/tracing/carrier.go @@ -0,0 +1,25 @@ +package tracing + +import "net/http" + +// HTTPHeadersCarrier custom implementation to fix duplicated headers +// It has been fixed in https://github.com/opentracing/opentracing-go/pull/191 +type HTTPHeadersCarrier http.Header + +// Set conforms to the TextMapWriter interface. +func (c HTTPHeadersCarrier) Set(key, val string) { + h := http.Header(c) + h.Set(key, val) +} + +// ForeachKey conforms to the TextMapReader interface. +func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error { + for k, vals := range c { + for _, v := range vals { + if err := handler(k, v); err != nil { + return err + } + } + } + return nil +} diff --git a/middlewares/tracing/entrypoint.go b/middlewares/tracing/entrypoint.go index a35bcea92..bf2a0879e 100644 --- a/middlewares/tracing/entrypoint.go +++ b/middlewares/tracing/entrypoint.go @@ -24,7 +24,7 @@ func (t *Tracing) NewEntryPoint(name string) negroni.Handler { func (e *entryPointMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { opNameFunc := generateEntryPointSpanName - ctx, _ := e.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) + ctx, _ := e.Extract(opentracing.HTTPHeaders, HTTPHeadersCarrier(r.Header)) span := e.StartSpan(opNameFunc(r, e.entryPoint, e.SpanNameLimit), ext.RPCServerOption(ctx)) ext.Component.Set(span, e.ServiceName) LogRequest(span, r) diff --git a/middlewares/tracing/tracing.go b/middlewares/tracing/tracing.go index a18c81806..f52039856 100644 --- a/middlewares/tracing/tracing.go +++ b/middlewares/tracing/tracing.go @@ -125,7 +125,7 @@ func InjectRequestHeaders(r *http.Request) { err := opentracing.GlobalTracer().Inject( span.Context(), opentracing.HTTPHeaders, - opentracing.HTTPHeadersCarrier(r.Header)) + HTTPHeadersCarrier(r.Header)) if err != nil { log.Error(err) } From 36966da701dea0562f0e7098311fc8687cfcbdfb Mon Sep 17 00:00:00 2001 From: Nathanael Marchand Date: Fri, 14 Sep 2018 09:00:03 +0200 Subject: [PATCH 02/10] Add missing quotes around backendName in kv template --- autogen/gentemplates/gen.go | 6 +++--- templates/kv.tmpl | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index 5fd14d24d..81a1a22d5 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -1476,14 +1476,14 @@ var _templatesKvTmpl = []byte(`[backends] {{ $healthCheck := getHealthCheck $backend }} {{if $healthCheck }} - [backends.{{ $backendName }}.healthCheck] + [backends."{{ $backendName }}".healthCheck] scheme = "{{ $healthCheck.Scheme }}" path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" hostname = "{{ $healthCheck.Hostname }}" {{if $healthCheck.Headers }} - [backends.{{ $backendName }}.healthCheck.headers] + [backends."{{ $backendName }}".healthCheck.headers] {{range $k, $v := $healthCheck.Headers }} {{$k}} = "{{$v}}" {{end}} @@ -1492,7 +1492,7 @@ var _templatesKvTmpl = []byte(`[backends] {{ $buffering := getBuffering $backend }} {{if $buffering }} - [backends.{{ $backendName }}.buffering] + [backends."{{ $backendName }}".buffering] maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }} memRequestBodyBytes = {{ $buffering.MemRequestBodyBytes }} maxResponseBodyBytes = {{ $buffering.MaxResponseBodyBytes }} diff --git a/templates/kv.tmpl b/templates/kv.tmpl index f4315cf04..dacd6f358 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -28,14 +28,14 @@ {{ $healthCheck := getHealthCheck $backend }} {{if $healthCheck }} - [backends.{{ $backendName }}.healthCheck] + [backends."{{ $backendName }}".healthCheck] scheme = "{{ $healthCheck.Scheme }}" path = "{{ $healthCheck.Path }}" port = {{ $healthCheck.Port }} interval = "{{ $healthCheck.Interval }}" hostname = "{{ $healthCheck.Hostname }}" {{if $healthCheck.Headers }} - [backends.{{ $backendName }}.healthCheck.headers] + [backends."{{ $backendName }}".healthCheck.headers] {{range $k, $v := $healthCheck.Headers }} {{$k}} = "{{$v}}" {{end}} @@ -44,7 +44,7 @@ {{ $buffering := getBuffering $backend }} {{if $buffering }} - [backends.{{ $backendName }}.buffering] + [backends."{{ $backendName }}".buffering] maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }} memRequestBodyBytes = {{ $buffering.MemRequestBodyBytes }} maxResponseBodyBytes = {{ $buffering.MaxResponseBodyBytes }} From 253060b4f3dbdbf469a91510106536ec86e0af9d Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 14 Sep 2018 10:06:03 +0200 Subject: [PATCH 03/10] Update Lego --- Gopkg.lock | 39 +- docs/configuration/acme.md | 3 + .../aliyun/alibaba-cloud-sdk-go/LICENSE | 201 +++++ .../sdk/auth/credential.go | 18 + .../auth/credentials/access_key_credential.go | 34 + .../sdk/auth/credentials/ecs_ram_role.go | 29 + .../credentials/rsa_key_pair_credential.go | 15 + .../sdk/auth/credentials/sts_credential.go | 15 + .../credentials/sts_role_arn_credential.go | 49 ++ .../sdk/auth/roa_signature_composer.go | 121 +++ .../sdk/auth/rpc_signature_composer.go | 96 +++ .../alibaba-cloud-sdk-go/sdk/auth/signer.go | 95 +++ .../sdk/auth/signers/algorithms.go | 63 ++ .../sdk/auth/signers/credential_updater.go | 53 ++ .../sdk/auth/signers/session_credential.go | 7 + .../sdk/auth/signers/signer_access_key.go | 58 ++ .../sdk/auth/signers/signer_ecs_ram_role.go | 175 ++++ .../sdk/auth/signers/signer_key_pair.go | 148 ++++ .../sdk/auth/signers/signer_ram_role_arn.go | 174 ++++ .../sdk/auth/signers/signer_sts_token.go | 58 ++ .../sdk/auth/signers/signer_v2.go | 58 ++ .../aliyun/alibaba-cloud-sdk-go/sdk/client.go | 419 +++++++++ .../aliyun/alibaba-cloud-sdk-go/sdk/config.go | 85 ++ .../sdk/endpoints/endpoints_config.go | 505 +++++++++++ .../sdk/endpoints/local_global_resolver.go | 37 + .../sdk/endpoints/local_regional_resolver.go | 41 + .../sdk/endpoints/location_resolver.go | 139 +++ .../sdk/endpoints/mapping_resolver.go | 39 + .../sdk/endpoints/resolver.go | 80 ++ .../sdk/endpoints/simple_host_resolver.go | 25 + .../sdk/errors/client_error.go | 92 ++ .../alibaba-cloud-sdk-go/sdk/errors/error.go | 23 + .../sdk/errors/server_error.go | 122 +++ .../signature_does_not_match_wrapper.go | 29 + .../sdk/requests/acs_reqeust.go | 309 +++++++ .../sdk/requests/common_request.go | 128 +++ .../sdk/requests/roa_request.go | 146 ++++ .../sdk/requests/rpc_request.go | 81 ++ .../sdk/requests/types.go | 53 ++ .../sdk/responses/json_parser.go | 341 ++++++++ .../sdk/responses/response.go | 142 ++++ .../alibaba-cloud-sdk-go/sdk/utils/utils.go | 117 +++ .../alidns/add_batch_domain_records.go | 106 +++ .../services/alidns/add_domain.go | 112 +++ .../services/alidns/add_domain_group.go | 107 +++ .../services/alidns/add_domain_record.go | 112 +++ .../alidns/apply_for_retrieval_domain_name.go | 106 +++ .../services/alidns/change_domain_group.go | 108 +++ .../alidns/change_domain_of_dns_product.go | 108 +++ .../services/alidns/check_domain_record.go | 109 +++ .../services/alidns/client.go | 81 ++ .../alidns/delete_batch_domain_records.go | 106 +++ .../services/alidns/delete_batch_domains.go | 106 +++ .../services/alidns/delete_domain.go | 106 +++ .../services/alidns/delete_domain_group.go | 106 +++ .../services/alidns/delete_domain_record.go | 106 +++ .../alidns/delete_sub_domain_records.go | 109 +++ .../services/alidns/describe_batch_result.go | 110 +++ .../alidns/describe_dns_product_instance.go | 134 +++ .../alidns/describe_dns_product_instances.go | 111 +++ .../alidns/describe_dnsslb_sub_domains.go | 111 +++ .../services/alidns/describe_domain_groups.go | 111 +++ .../services/alidns/describe_domain_info.go | 123 +++ .../services/alidns/describe_domain_logs.go | 114 +++ .../services/alidns/describe_domain_ns.go | 109 +++ .../alidns/describe_domain_record_info.go | 119 +++ .../alidns/describe_domain_records.go | 117 +++ .../alidns/describe_domain_whois_info.go | 113 +++ .../services/alidns/describe_domains.go | 112 +++ .../services/alidns/describe_record_logs.go | 114 +++ .../alidns/describe_sub_domain_records.go | 112 +++ .../services/alidns/describe_support_lines.go | 105 +++ .../services/alidns/get_main_domain_name.go | 108 +++ .../alidns/modify_hichina_domain_dns.go | 107 +++ .../services/alidns/retrieval_domain_name.go | 107 +++ .../services/alidns/set_dnsslb_status.go | 108 +++ .../alidns/set_domain_record_status.go | 108 +++ .../services/alidns/struct_available_ttls.go | 21 + .../services/alidns/struct_dns_product.go | 48 ++ .../services/alidns/struct_dns_products.go | 21 + .../struct_dns_servers_in_add_domain.go | 21 + ...ervers_in_describe_dns_product_instance.go | 21 + ...uct_dns_servers_in_describe_domain_info.go | 21 + ...truct_dns_servers_in_describe_domain_ns.go | 21 + ...s_servers_in_describe_domain_whois_info.go | 21 + .../struct_dns_servers_in_describe_domains.go | 21 + .../services/alidns/struct_domain.go | 35 + .../services/alidns/struct_domain_group.go | 23 + .../services/alidns/struct_domain_groups.go | 21 + .../services/alidns/struct_domain_log.go | 26 + .../services/alidns/struct_domain_logs.go | 21 + ...main_records_in_describe_domain_records.go | 21 + ..._records_in_describe_sub_domain_records.go | 21 + .../services/alidns/struct_domains.go | 21 + .../alidns/struct_expect_dns_servers.go | 21 + .../services/alidns/struct_fail_result.go | 22 + .../services/alidns/struct_fail_results.go | 21 + .../services/alidns/struct_new_dns_servers.go | 21 + .../alidns/struct_original_dns_servers.go | 21 + .../services/alidns/struct_record.go | 32 + .../services/alidns/struct_record_line.go | 24 + ...ct_record_lines_in_describe_domain_info.go | 21 + ..._record_lines_in_describe_support_lines.go | 21 + .../services/alidns/struct_record_log.go | 25 + .../services/alidns/struct_record_logs.go | 21 + .../services/alidns/struct_slb_sub_domain.go | 24 + .../services/alidns/struct_slb_sub_domains.go | 21 + .../services/alidns/struct_status_list.go | 21 + .../alidns/update_batch_domain_records.go | 106 +++ .../services/alidns/update_dnsslb_weight.go | 108 +++ .../services/alidns/update_domain_group.go | 108 +++ .../services/alidns/update_domain_record.go | 112 +++ .../services/push/push_notice_to_android.go | 110 +++ .../services/push/push_notice_toi_os.go | 111 +++ .../github.com/exoscale/egoscale/accounts.go | 213 +++-- .../exoscale/egoscale/accounttype_string.go | 16 + .../github.com/exoscale/egoscale/addresses.go | 259 +++--- .../exoscale/egoscale/affinity_groups.go | 222 ++--- vendor/github.com/exoscale/egoscale/apis.go | 49 +- .../exoscale/egoscale/async_jobs.go | 103 ++- vendor/github.com/exoscale/egoscale/cidr.go | 62 ++ vendor/github.com/exoscale/egoscale/client.go | 385 +++++++++ vendor/github.com/exoscale/egoscale/copier.go | 37 + .../exoscale/egoscale/cserrorcode_string.go | 47 ++ vendor/github.com/exoscale/egoscale/dns.go | 208 ++++- vendor/github.com/exoscale/egoscale/doc.go | 178 ++-- .../exoscale/egoscale/errorcode_string.go | 37 + vendor/github.com/exoscale/egoscale/events.go | 83 +- vendor/github.com/exoscale/egoscale/hosts.go | 99 +++ vendor/github.com/exoscale/egoscale/init.go | 22 - .../exoscale/egoscale/jobstatustype_string.go | 16 + .../github.com/exoscale/egoscale/keypairs.go | 171 ---- vendor/github.com/exoscale/egoscale/limits.go | 143 ++-- .../exoscale/egoscale/macaddress.go | 63 ++ .../exoscale/egoscale/network_offerings.go | 115 +-- .../github.com/exoscale/egoscale/networks.go | 340 ++++---- vendor/github.com/exoscale/egoscale/nics.go | 184 ++-- .../exoscale/egoscale/record_string.go | 16 + .../github.com/exoscale/egoscale/request.go | 781 +++++++---------- .../exoscale/egoscale/request_type.go | 184 ++++ .../exoscale/egoscale/resource_metadata.go | 36 + .../exoscale/egoscale/reversedns.go | 77 ++ .../exoscale/egoscale/security_groups.go | 368 ++++---- .../exoscale/egoscale/serialization.go | 328 ++++++++ .../exoscale/egoscale/service_offerings.go | 142 ++-- .../github.com/exoscale/egoscale/snapshots.go | 165 ++-- .../exoscale/egoscale/snapshotstate_string.go | 16 + .../exoscale/egoscale/ssh_keypairs.go | 138 +++ vendor/github.com/exoscale/egoscale/tags.go | 98 +-- .../github.com/exoscale/egoscale/templates.go | 315 +++++-- .../github.com/exoscale/egoscale/topology.go | 208 ----- vendor/github.com/exoscale/egoscale/types.go | 23 - vendor/github.com/exoscale/egoscale/users.go | 132 ++- vendor/github.com/exoscale/egoscale/uuid.go | 55 ++ .../github.com/exoscale/egoscale/version.go | 4 + .../exoscale/egoscale/virtual_machines.go | 796 ++++++++++-------- .../github.com/exoscale/egoscale/vm_groups.go | 108 +-- .../github.com/exoscale/egoscale/volumes.go | 205 +++-- vendor/github.com/exoscale/egoscale/zones.go | 118 ++- vendor/github.com/iij/doapi/api.go | 170 ++++ vendor/github.com/iij/doapi/parse.go | 267 ++++++ .../github.com/iij/doapi/protocol/Commit.go | 44 + .../iij/doapi/protocol/RecordAdd.go | 51 ++ .../iij/doapi/protocol/RecordDelete.go | 47 ++ .../iij/doapi/protocol/RecordGet.go | 48 ++ .../iij/doapi/protocol/RecordListGet.go | 47 ++ vendor/github.com/iij/doapi/protocol/Reset.go | 45 + .../iij/doapi/protocol/ZoneListGet.go | 45 + .../github.com/iij/doapi/protocol/common.go | 53 ++ vendor/github.com/satori/go.uuid/LICENSE | 2 +- vendor/github.com/satori/go.uuid/codec.go | 206 +++++ vendor/github.com/satori/go.uuid/generator.go | 239 ++++++ vendor/github.com/satori/go.uuid/sql.go | 78 ++ vendor/github.com/satori/go.uuid/uuid.go | 441 ++-------- vendor/github.com/xenolf/lego/acme/client.go | 81 +- .../xenolf/lego/acme/dns_challenge.go | 33 +- .../xenolf/lego/platform/config/env/env.go | 12 + .../lego/providers/dns/alidns/alidns.go | 166 ++++ .../lego/providers/dns/dns_providers.go | 9 + .../lego/providers/dns/exoscale/exoscale.go | 23 +- .../xenolf/lego/providers/dns/iij/iij.go | 211 +++++ .../lego/providers/dns/netcup/client.go | 327 +++++++ .../lego/providers/dns/netcup/netcup.go | 116 +++ .../xenolf/lego/providers/dns/ovh/ovh.go | 47 +- .../lego/providers/dns/route53/route53.go | 11 +- 185 files changed, 16653 insertions(+), 3210 deletions(-) create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/rsa_key_pair_credential.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_credential.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_role_arn_credential.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/roa_signature_composer.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/rpc_signature_composer.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signer.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/algorithms.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/credential_updater.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/session_credential.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_access_key.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ecs_ram_role.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_key_pair.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ram_role_arn.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_sts_token.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_v2.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/endpoints_config.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_global_resolver.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_regional_resolver.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/location_resolver.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/mapping_resolver.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/resolver.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/simple_host_resolver.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/error.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/signature_does_not_match_wrapper.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/acs_reqeust.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/common_request.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/roa_request.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/rpc_request.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/types.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils/utils.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_batch_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_group.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_record.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/apply_for_retrieval_domain_name.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_group.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_of_dns_product.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/check_domain_record.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/client.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domains.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_group.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_record.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_sub_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_batch_result.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instance.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instances.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dnsslb_sub_domains.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_groups.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_info.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_logs.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_ns.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_record_info.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_whois_info.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domains.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_record_logs.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_sub_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_support_lines.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/get_main_domain_name.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/modify_hichina_domain_dns.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/retrieval_domain_name.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_dnsslb_status.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_domain_record_status.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_available_ttls.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_product.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_products.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_add_domain.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_dns_product_instance.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_info.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_ns.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_whois_info.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domains.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_group.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_groups.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_log.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_logs.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_sub_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domains.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_expect_dns_servers.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_result.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_results.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_new_dns_servers.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_original_dns_servers.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_line.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_domain_info.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_support_lines.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_log.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_logs.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domain.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domains.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_status_list.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_batch_domain_records.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_dnsslb_weight.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_group.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_record.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_to_android.go create mode 100644 vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_toi_os.go create mode 100644 vendor/github.com/exoscale/egoscale/accounttype_string.go create mode 100644 vendor/github.com/exoscale/egoscale/cidr.go create mode 100644 vendor/github.com/exoscale/egoscale/client.go create mode 100644 vendor/github.com/exoscale/egoscale/copier.go create mode 100644 vendor/github.com/exoscale/egoscale/cserrorcode_string.go create mode 100644 vendor/github.com/exoscale/egoscale/errorcode_string.go create mode 100644 vendor/github.com/exoscale/egoscale/hosts.go delete mode 100644 vendor/github.com/exoscale/egoscale/init.go create mode 100644 vendor/github.com/exoscale/egoscale/jobstatustype_string.go delete mode 100644 vendor/github.com/exoscale/egoscale/keypairs.go create mode 100644 vendor/github.com/exoscale/egoscale/macaddress.go create mode 100644 vendor/github.com/exoscale/egoscale/record_string.go create mode 100644 vendor/github.com/exoscale/egoscale/request_type.go create mode 100644 vendor/github.com/exoscale/egoscale/resource_metadata.go create mode 100644 vendor/github.com/exoscale/egoscale/reversedns.go create mode 100644 vendor/github.com/exoscale/egoscale/serialization.go create mode 100644 vendor/github.com/exoscale/egoscale/snapshotstate_string.go create mode 100644 vendor/github.com/exoscale/egoscale/ssh_keypairs.go delete mode 100644 vendor/github.com/exoscale/egoscale/topology.go delete mode 100644 vendor/github.com/exoscale/egoscale/types.go create mode 100644 vendor/github.com/exoscale/egoscale/uuid.go create mode 100644 vendor/github.com/exoscale/egoscale/version.go create mode 100644 vendor/github.com/iij/doapi/api.go create mode 100644 vendor/github.com/iij/doapi/parse.go create mode 100644 vendor/github.com/iij/doapi/protocol/Commit.go create mode 100644 vendor/github.com/iij/doapi/protocol/RecordAdd.go create mode 100644 vendor/github.com/iij/doapi/protocol/RecordDelete.go create mode 100644 vendor/github.com/iij/doapi/protocol/RecordGet.go create mode 100644 vendor/github.com/iij/doapi/protocol/RecordListGet.go create mode 100644 vendor/github.com/iij/doapi/protocol/Reset.go create mode 100644 vendor/github.com/iij/doapi/protocol/ZoneListGet.go create mode 100644 vendor/github.com/iij/doapi/protocol/common.go create mode 100644 vendor/github.com/satori/go.uuid/codec.go create mode 100644 vendor/github.com/satori/go.uuid/generator.go create mode 100644 vendor/github.com/satori/go.uuid/sql.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/iij/iij.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/netcup/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go diff --git a/Gopkg.lock b/Gopkg.lock index e0b9792e7..1cf5788a7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -169,6 +169,23 @@ revision = "a494eba1efa1f38338393727dff63389a6a66534" version = "v0.6.0" +[[projects]] + name = "github.com/aliyun/alibaba-cloud-sdk-go" + packages = [ + "sdk", + "sdk/auth", + "sdk/auth/credentials", + "sdk/auth/signers", + "sdk/endpoints", + "sdk/errors", + "sdk/requests", + "sdk/responses", + "sdk/utils", + "services/alidns" + ] + revision = "cad214d7d71fba7883fcf3b7e550ba782c15b400" + version = "1.27.7" + [[projects]] name = "github.com/aokoli/goutils" packages = ["."] @@ -540,8 +557,8 @@ [[projects]] name = "github.com/exoscale/egoscale" packages = ["."] - revision = "e4fedc381fbddb7fef4d7060388a726c6de37c88" - version = "v0.9.7" + revision = "d8dfca6802ad5c1a5300e52fa68067e791322035" + version = "v0.11.4" [[projects]] name = "github.com/fatih/color" @@ -764,6 +781,15 @@ packages = ["."] revision = "3959339b333561bf62a38b424fd41517c2c90f40" +[[projects]] + branch = "master" + name = "github.com/iij/doapi" + packages = [ + ".", + "protocol" + ] + revision = "8803795a9b7b938fa88ddbd63a77893beee14cd8" + [[projects]] name = "github.com/imdario/mergo" packages = ["."] @@ -1141,8 +1167,8 @@ [[projects]] name = "github.com/satori/go.uuid" packages = ["."] - revision = "879c5887cd475cd7864858769793b2ceb0d44feb" - version = "v1.1.0" + revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" + version = "v1.2.0" [[projects]] name = "github.com/sirupsen/logrus" @@ -1306,6 +1332,7 @@ "platform/config/env", "providers/dns", "providers/dns/acmedns", + "providers/dns/alidns", "providers/dns/auroradns", "providers/dns/azure", "providers/dns/bluecat", @@ -1325,10 +1352,12 @@ "providers/dns/gcloud", "providers/dns/glesys", "providers/dns/godaddy", + "providers/dns/iij", "providers/dns/lightsail", "providers/dns/linode", "providers/dns/namecheap", "providers/dns/namedotcom", + "providers/dns/netcup", "providers/dns/nifcloud", "providers/dns/ns1", "providers/dns/otc", @@ -1341,7 +1370,7 @@ "providers/dns/vegadns", "providers/dns/vultr" ] - revision = "8b6701514cc0a6285a327908f3f9ce05bcacbffd" + revision = "ad34a85dada244c4e4a50d71963a90ea70736033" [[projects]] branch = "master" diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md index 7cb75f1da..d510c87dc 100644 --- a/docs/configuration/acme.md +++ b/docs/configuration/acme.md @@ -253,6 +253,7 @@ Here is a list of supported `provider`s, that can automate the DNS verification, | Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support | |--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------| +| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet | | [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet | | [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | Not tested yet | | [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet | @@ -272,11 +273,13 @@ Here is a list of supported `provider`s, that can automate the DNS verification, | [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet | | [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet | | [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES | +| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet | | [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet | | [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet | | manual | - | none, but you need to run Træfik interactively, turn on `acmeLogging` to see instructions and press Enter. | YES | | [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES | | [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet | +| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet | | [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet | | [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet | | [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet | diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential.go new file mode 100644 index 000000000..7f20b7a40 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential.go @@ -0,0 +1,18 @@ +/* + * 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 auth + +type Credential interface { +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential.go new file mode 100644 index 000000000..68f822633 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential.go @@ -0,0 +1,34 @@ +package credentials + +// Deprecated: Use AccessKeyCredential in this package instead. +type BaseCredential struct { + AccessKeyId string + AccessKeySecret string +} + +type AccessKeyCredential struct { + AccessKeyId string + AccessKeySecret string +} + +// Deprecated: Use NewAccessKeyCredential in this package instead. +func NewBaseCredential(accessKeyId, accessKeySecret string) *BaseCredential { + return &BaseCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + } +} + +func (baseCred *BaseCredential) ToAccessKeyCredential() *AccessKeyCredential { + return &AccessKeyCredential{ + AccessKeyId: baseCred.AccessKeyId, + AccessKeySecret: baseCred.AccessKeySecret, + } +} + +func NewAccessKeyCredential(accessKeyId, accessKeySecret string) *AccessKeyCredential { + return &AccessKeyCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role.go new file mode 100644 index 000000000..1e1f73ae4 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role.go @@ -0,0 +1,29 @@ +package credentials + +// Deprecated: Use EcsRamRoleCredential in this package instead. +type StsRoleNameOnEcsCredential struct { + RoleName string +} + +// Deprecated: Use NewEcsRamRoleCredential in this package instead. +func NewStsRoleNameOnEcsCredential(roleName string) *StsRoleNameOnEcsCredential { + return &StsRoleNameOnEcsCredential{ + RoleName: roleName, + } +} + +func (oldCred *StsRoleNameOnEcsCredential) ToEcsRamRoleCredential() *EcsRamRoleCredential { + return &EcsRamRoleCredential{ + RoleName: oldCred.RoleName, + } +} + +type EcsRamRoleCredential struct { + RoleName string +} + +func NewEcsRamRoleCredential(roleName string) *EcsRamRoleCredential { + return &EcsRamRoleCredential{ + RoleName: roleName, + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/rsa_key_pair_credential.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/rsa_key_pair_credential.go new file mode 100644 index 000000000..00d688eb8 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/rsa_key_pair_credential.go @@ -0,0 +1,15 @@ +package credentials + +type RsaKeyPairCredential struct { + PrivateKey string + PublicKeyId string + SessionExpiration int +} + +func NewRsaKeyPairCredential(privateKey, publicKeyId string, sessionExpiration int) *RsaKeyPairCredential { + return &RsaKeyPairCredential{ + PrivateKey: privateKey, + PublicKeyId: publicKeyId, + SessionExpiration: sessionExpiration, + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_credential.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_credential.go new file mode 100644 index 000000000..554431ff0 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_credential.go @@ -0,0 +1,15 @@ +package credentials + +type StsTokenCredential struct { + AccessKeyId string + AccessKeySecret string + AccessKeyStsToken string +} + +func NewStsTokenCredential(accessKeyId, accessKeySecret, accessKeyStsToken string) *StsTokenCredential { + return &StsTokenCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + AccessKeyStsToken: accessKeyStsToken, + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_role_arn_credential.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_role_arn_credential.go new file mode 100644 index 000000000..7a9db75d2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/sts_role_arn_credential.go @@ -0,0 +1,49 @@ +package credentials + +// Deprecated: Use RamRoleArnCredential in this package instead. +type StsRoleArnCredential struct { + AccessKeyId string + AccessKeySecret string + RoleArn string + RoleSessionName string + RoleSessionExpiration int +} + +type RamRoleArnCredential struct { + AccessKeyId string + AccessKeySecret string + RoleArn string + RoleSessionName string + RoleSessionExpiration int +} + +// Deprecated: Use RamRoleArnCredential in this package instead. +func NewStsRoleArnCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName string, roleSessionExpiration int) *StsRoleArnCredential { + return &StsRoleArnCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + RoleArn: roleArn, + RoleSessionName: roleSessionName, + RoleSessionExpiration: roleSessionExpiration, + } +} + +func (oldCred *StsRoleArnCredential) ToRamRoleArnCredential() *RamRoleArnCredential { + return &RamRoleArnCredential{ + AccessKeyId: oldCred.AccessKeyId, + AccessKeySecret: oldCred.AccessKeySecret, + RoleArn: oldCred.RoleArn, + RoleSessionName: oldCred.RoleSessionName, + RoleSessionExpiration: oldCred.RoleSessionExpiration, + } +} + +func NewRamRoleArnCredential(accessKeyId, accessKeySecret, roleArn, roleSessionName string, roleSessionExpiration int) *RamRoleArnCredential { + return &RamRoleArnCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + RoleArn: roleArn, + RoleSessionName: roleSessionName, + RoleSessionExpiration: roleSessionExpiration, + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/roa_signature_composer.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/roa_signature_composer.go new file mode 100644 index 000000000..8666dd064 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/roa_signature_composer.go @@ -0,0 +1,121 @@ +/* + * 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 auth + +import ( + "bytes" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils" + "sort" + "strings" +) + +func signRoaRequest(request requests.AcsRequest, signer Signer, regionId string) (err error) { + completeROASignParams(request, signer, regionId) + stringToSign := buildRoaStringToSign(request) + request.SetStringToSign(stringToSign) + signature := signer.Sign(stringToSign, "") + accessKeyId, err := signer.GetAccessKeyId() + if err != nil { + return nil + } + + request.GetHeaders()["Authorization"] = "acs " + accessKeyId + ":" + signature + + return +} + +func completeROASignParams(request requests.AcsRequest, signer Signer, regionId string) { + headerParams := request.GetHeaders() + + // complete query params + queryParams := request.GetQueryParams() + //if _, ok := queryParams["RegionId"]; !ok { + // queryParams["RegionId"] = regionId + //} + if extraParam := signer.GetExtraParam(); extraParam != nil { + for key, value := range extraParam { + if key == "SecurityToken" { + headerParams["x-acs-security-token"] = value + continue + } + + queryParams[key] = value + } + } + + // complete header params + headerParams["Date"] = utils.GetTimeInFormatRFC2616() + headerParams["x-acs-signature-method"] = signer.GetName() + headerParams["x-acs-signature-version"] = signer.GetVersion() + if request.GetFormParams() != nil && len(request.GetFormParams()) > 0 { + formString := utils.GetUrlFormedMap(request.GetFormParams()) + request.SetContent([]byte(formString)) + headerParams["Content-Type"] = requests.Form + } + contentMD5 := utils.GetMD5Base64(request.GetContent()) + headerParams["Content-MD5"] = contentMD5 + if _, contains := headerParams["Content-Type"]; !contains { + headerParams["Content-Type"] = requests.Raw + } + switch format := request.GetAcceptFormat(); format { + case "JSON": + headerParams["Accept"] = requests.Json + case "XML": + headerParams["Accept"] = requests.Xml + default: + headerParams["Accept"] = requests.Raw + } +} + +func buildRoaStringToSign(request requests.AcsRequest) (stringToSign string) { + + headers := request.GetHeaders() + + stringToSignBuilder := bytes.Buffer{} + stringToSignBuilder.WriteString(request.GetMethod()) + stringToSignBuilder.WriteString(requests.HeaderSeparator) + + // append header keys for sign + appendIfContain(headers, &stringToSignBuilder, "Accept", requests.HeaderSeparator) + appendIfContain(headers, &stringToSignBuilder, "Content-MD5", requests.HeaderSeparator) + appendIfContain(headers, &stringToSignBuilder, "Content-Type", requests.HeaderSeparator) + appendIfContain(headers, &stringToSignBuilder, "Date", requests.HeaderSeparator) + + // sort and append headers witch starts with 'x-acs-' + var acsHeaders []string + for key := range headers { + if strings.HasPrefix(key, "x-acs-") { + acsHeaders = append(acsHeaders, key) + } + } + sort.Strings(acsHeaders) + for _, key := range acsHeaders { + stringToSignBuilder.WriteString(key + ":" + headers[key]) + stringToSignBuilder.WriteString(requests.HeaderSeparator) + } + + // append query params + stringToSignBuilder.WriteString(request.BuildQueries()) + stringToSign = stringToSignBuilder.String() + return +} + +func appendIfContain(sourceMap map[string]string, target *bytes.Buffer, key, separator string) { + if value, contain := sourceMap[key]; contain && len(value) > 0 { + target.WriteString(sourceMap[key]) + target.WriteString(separator) + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/rpc_signature_composer.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/rpc_signature_composer.go new file mode 100644 index 000000000..0c6f6d111 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/rpc_signature_composer.go @@ -0,0 +1,96 @@ +/* + * 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 auth + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils" + "net/url" + "sort" + "strings" +) + +func signRpcRequest(request requests.AcsRequest, signer Signer, regionId string) (err error) { + err = completeRpcSignParams(request, signer, regionId) + if err != nil { + return + } + // remove while retry + if _, containsSign := request.GetQueryParams()["Signature"]; containsSign { + delete(request.GetQueryParams(), "Signature") + } + stringToSign := buildRpcStringToSign(request) + request.SetStringToSign(stringToSign) + signature := signer.Sign(stringToSign, "&") + request.GetQueryParams()["Signature"] = signature + + return +} + +func completeRpcSignParams(request requests.AcsRequest, signer Signer, regionId string) (err error) { + queryParams := request.GetQueryParams() + queryParams["Version"] = request.GetVersion() + queryParams["Action"] = request.GetActionName() + queryParams["Format"] = request.GetAcceptFormat() + queryParams["Timestamp"] = utils.GetTimeInFormatISO8601() + queryParams["SignatureMethod"] = signer.GetName() + queryParams["SignatureType"] = signer.GetType() + queryParams["SignatureVersion"] = signer.GetVersion() + queryParams["SignatureNonce"] = utils.GetUUIDV4() + queryParams["AccessKeyId"], err = signer.GetAccessKeyId() + + if err != nil { + return + } + + if _, contains := queryParams["RegionId"]; !contains { + queryParams["RegionId"] = regionId + } + if extraParam := signer.GetExtraParam(); extraParam != nil { + for key, value := range extraParam { + queryParams[key] = value + } + } + + request.GetHeaders()["Content-Type"] = requests.Form + formString := utils.GetUrlFormedMap(request.GetFormParams()) + request.SetContent([]byte(formString)) + + return +} + +func buildRpcStringToSign(request requests.AcsRequest) (stringToSign string) { + signParams := make(map[string]string) + for key, value := range request.GetQueryParams() { + signParams[key] = value + } + for key, value := range request.GetFormParams() { + signParams[key] = value + } + + // sort params by key + var paramKeySlice []string + for key := range signParams { + paramKeySlice = append(paramKeySlice, key) + } + sort.Strings(paramKeySlice) + stringToSign = utils.GetUrlFormedMap(signParams) + stringToSign = strings.Replace(stringToSign, "+", "%20", -1) + stringToSign = strings.Replace(stringToSign, "*", "%2A", -1) + stringToSign = strings.Replace(stringToSign, "%7E", "~", -1) + stringToSign = url.QueryEscape(stringToSign) + stringToSign = request.GetMethod() + "&%2F&" + stringToSign + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signer.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signer.go new file mode 100644 index 000000000..ba946d0fd --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signer.go @@ -0,0 +1,95 @@ +/* + * 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 auth + +import ( + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "reflect" +) + +type Signer interface { + GetName() string + GetType() string + GetVersion() string + GetAccessKeyId() (string, error) + GetExtraParam() map[string]string + Sign(stringToSign, secretSuffix string) string + Shutdown() +} + +func NewSignerWithCredential(credential Credential, commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error)) (signer Signer, err error) { + switch instance := credential.(type) { + case *credentials.AccessKeyCredential: + { + signer, err = signers.NewAccessKeySigner(instance) + } + case *credentials.StsTokenCredential: + { + signer, err = signers.NewStsTokenSigner(instance) + } + + case *credentials.RamRoleArnCredential: + { + signer, err = signers.NewRamRoleArnSigner(instance, commonApi) + } + case *credentials.RsaKeyPairCredential: + { + signer, err = signers.NewSignerKeyPair(instance, commonApi) + } + case *credentials.EcsRamRoleCredential: + { + signer, err = signers.NewEcsRamRoleSigner(instance, commonApi) + } + case *credentials.BaseCredential: // deprecated user interface + { + signer, err = signers.NewAccessKeySigner(instance.ToAccessKeyCredential()) + } + case *credentials.StsRoleArnCredential: // deprecated user interface + { + signer, err = signers.NewRamRoleArnSigner(instance.ToRamRoleArnCredential(), commonApi) + } + case *credentials.StsRoleNameOnEcsCredential: // deprecated user interface + { + signer, err = signers.NewEcsRamRoleSigner(instance.ToEcsRamRoleCredential(), commonApi) + } + default: + message := fmt.Sprintf(errors.UnsupportedCredentialErrorMessage, reflect.TypeOf(credential)) + err = errors.NewClientError(errors.UnsupportedCredentialErrorCode, message, nil) + } + return +} + +func Sign(request requests.AcsRequest, signer Signer, regionId string) (err error) { + switch request.GetStyle() { + case requests.ROA: + { + signRoaRequest(request, signer, regionId) + } + case requests.RPC: + { + err = signRpcRequest(request, signer, regionId) + } + default: + message := fmt.Sprintf(errors.UnknownRequestTypeErrorMessage, reflect.TypeOf(request)) + err = errors.NewClientError(errors.UnknownRequestTypeErrorCode, message, nil) + } + + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/algorithms.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/algorithms.go new file mode 100644 index 000000000..975e985b9 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/algorithms.go @@ -0,0 +1,63 @@ +/* + * 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 signers + +import ( + "crypto" + "crypto/hmac" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/base64" + "fmt" + /*"encoding/pem" + "io/ioutil" + "os/user" + "crypto/sha256"*/) + +func ShaHmac1(source, secret string) string { + key := []byte(secret) + hmac := hmac.New(sha1.New, key) + hmac.Write([]byte(source)) + signedBytes := hmac.Sum(nil) + signedString := base64.StdEncoding.EncodeToString(signedBytes) + return signedString +} + +func Sha256WithRsa(source, secret string) string { + decodeString, err := base64.StdEncoding.DecodeString(secret) + if err != nil { + fmt.Println("DecodeString err", err) + } + private, err := x509.ParsePKCS8PrivateKey(decodeString) + if err != nil { + fmt.Println("ParsePKCS8PrivateKey err", err) + } + + h := crypto.Hash.New(crypto.SHA256) + h.Write([]byte(source)) + hashed := h.Sum(nil) + signature, err := rsa.SignPKCS1v15(rand.Reader, private.(*rsa.PrivateKey), + crypto.SHA256, hashed) + if err != nil { + fmt.Println("Error from signing:", err) + return "" + } + + signedString := base64.StdEncoding.EncodeToString(signature) + //fmt.Printf("Encoded: %v\n", signedString) + return signedString +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/credential_updater.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/credential_updater.go new file mode 100644 index 000000000..bb73d2441 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/credential_updater.go @@ -0,0 +1,53 @@ +/* + * 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 signers + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "time" +) + +const defaultInAdvanceScale = 0.8 + +type credentialUpdater struct { + credentialExpiration int + lastUpdateTimestamp int64 + inAdvanceScale float64 + buildRequestMethod func() (*requests.CommonRequest, error) + responseCallBack func(response *responses.CommonResponse) error + refreshApi func(request *requests.CommonRequest) (response *responses.CommonResponse, err error) +} + +func (updater *credentialUpdater) needUpdateCredential() (result bool) { + if updater.inAdvanceScale == 0 { + updater.inAdvanceScale = defaultInAdvanceScale + } + return time.Now().Unix()-updater.lastUpdateTimestamp >= int64(float64(updater.credentialExpiration)*updater.inAdvanceScale) +} + +func (updater *credentialUpdater) updateCredential() (err error) { + request, err := updater.buildRequestMethod() + if err != nil { + return + } + response, err := updater.refreshApi(request) + if err != nil { + return + } + updater.lastUpdateTimestamp = time.Now().Unix() + err = updater.responseCallBack(response) + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/session_credential.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/session_credential.go new file mode 100644 index 000000000..99c624c88 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/session_credential.go @@ -0,0 +1,7 @@ +package signers + +type SessionCredential struct { + AccessKeyId string + AccessKeySecret string + StsToken string +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_access_key.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_access_key.go new file mode 100644 index 000000000..92466ea55 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_access_key.go @@ -0,0 +1,58 @@ +/* + * 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 signers + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" +) + +type AccessKeySigner struct { + credential *credentials.AccessKeyCredential +} + +func (signer *AccessKeySigner) GetExtraParam() map[string]string { + return nil +} + +func NewAccessKeySigner(credential *credentials.AccessKeyCredential) (*AccessKeySigner, error) { + return &AccessKeySigner{ + credential: credential, + }, nil +} + +func (*AccessKeySigner) GetName() string { + return "HMAC-SHA1" +} + +func (*AccessKeySigner) GetType() string { + return "" +} + +func (*AccessKeySigner) GetVersion() string { + return "1.0" +} + +func (signer *AccessKeySigner) GetAccessKeyId() (accessKeyId string, err error) { + return signer.credential.AccessKeyId, nil +} + +func (signer *AccessKeySigner) Sign(stringToSign, secretSuffix string) string { + secret := signer.credential.AccessKeySecret + secretSuffix + return ShaHmac1(stringToSign, secret) +} + +func (signer *AccessKeySigner) Shutdown() { + +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ecs_ram_role.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ecs_ram_role.go new file mode 100644 index 000000000..6cacc140d --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ecs_ram_role.go @@ -0,0 +1,175 @@ +/* + * 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 signers + +import ( + "encoding/json" + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "github.com/jmespath/go-jmespath" + "net/http" + "strings" + "time" +) + +type EcsRamRoleSigner struct { + *credentialUpdater + sessionCredential *SessionCredential + credential *credentials.EcsRamRoleCredential + commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error) +} + +func NewEcsRamRoleSigner(credential *credentials.EcsRamRoleCredential, commonApi func(*requests.CommonRequest, interface{}) (response *responses.CommonResponse, err error)) (signer *EcsRamRoleSigner, err error) { + signer = &EcsRamRoleSigner{ + credential: credential, + commonApi: commonApi, + } + + signer.credentialUpdater = &credentialUpdater{ + credentialExpiration: defaultDurationSeconds / 60, + buildRequestMethod: signer.buildCommonRequest, + responseCallBack: signer.refreshCredential, + refreshApi: signer.refreshApi, + } + + return +} + +func (*EcsRamRoleSigner) GetName() string { + return "HMAC-SHA1" +} + +func (*EcsRamRoleSigner) GetType() string { + return "" +} + +func (*EcsRamRoleSigner) GetVersion() string { + return "1.0" +} + +func (signer *EcsRamRoleSigner) GetAccessKeyId() (accessKeyId string, err error) { + if signer.sessionCredential == nil || signer.needUpdateCredential() { + err = signer.updateCredential() + } + if err != nil && (signer.sessionCredential == nil || len(signer.sessionCredential.AccessKeyId) <= 0) { + return "", err + } + return signer.sessionCredential.AccessKeyId, nil +} + +func (signer *EcsRamRoleSigner) GetExtraParam() map[string]string { + if signer.sessionCredential == nil { + return make(map[string]string) + } + if len(signer.sessionCredential.StsToken) <= 0 { + return make(map[string]string) + } + return map[string]string{"SecurityToken": signer.sessionCredential.StsToken} +} + +func (signer *EcsRamRoleSigner) Sign(stringToSign, secretSuffix string) string { + secret := signer.sessionCredential.AccessKeyId + secretSuffix + return ShaHmac1(stringToSign, secret) +} + +func (signer *EcsRamRoleSigner) buildCommonRequest() (request *requests.CommonRequest, err error) { + request = requests.NewCommonRequest() + return +} + +func (signer *EcsRamRoleSigner) refreshApi(request *requests.CommonRequest) (response *responses.CommonResponse, err error) { + requestUrl := "http://100.100.100.200/latest/meta-data/ram/security-credentials/" + signer.credential.RoleName + httpRequest, err := http.NewRequest(requests.GET, requestUrl, strings.NewReader("")) + if err != nil { + fmt.Println("refresh Ecs sts token err", err) + return + } + httpClient := &http.Client{} + httpResponse, err := httpClient.Do(httpRequest) + if err != nil { + fmt.Println("refresh Ecs sts token err", err) + return + } + + response = responses.NewCommonResponse() + err = responses.Unmarshal(response, httpResponse, "") + + return +} + +func (signer *EcsRamRoleSigner) refreshCredential(response *responses.CommonResponse) (err error) { + if response.GetHttpStatus() != http.StatusOK { + fmt.Println("refresh Ecs sts token err, httpStatus: " + string(response.GetHttpStatus()) + ", message = " + response.GetHttpContentString()) + return + } + var data interface{} + err = json.Unmarshal(response.GetHttpContentBytes(), &data) + if err != nil { + fmt.Println("refresh Ecs sts token err, json.Unmarshal fail", err) + return + } + code, err := jmespath.Search("Code", data) + if err != nil { + fmt.Println("refresh Ecs sts token err, fail to get Code", err) + return + } + if code.(string) != "Success" { + fmt.Println("refresh Ecs sts token err, Code is not Success", err) + return + } + accessKeyId, err := jmespath.Search("AccessKeyId", data) + if err != nil { + fmt.Println("refresh Ecs sts token err, fail to get AccessKeyId", err) + return + } + accessKeySecret, err := jmespath.Search("AccessKeySecret", data) + if err != nil { + fmt.Println("refresh Ecs sts token err, fail to get AccessKeySecret", err) + return + } + securityToken, err := jmespath.Search("SecurityToken", data) + if err != nil { + fmt.Println("refresh Ecs sts token err, fail to get SecurityToken", err) + return + } + expiration, err := jmespath.Search("Expiration", data) + if err != nil { + fmt.Println("refresh Ecs sts token err, fail to get Expiration", err) + return + } + if accessKeyId == nil || accessKeySecret == nil || securityToken == nil { + return + } + + expirationTime, err := time.Parse("2006-01-02T15:04:05Z", expiration.(string)) + signer.credentialExpiration = int(expirationTime.Unix() - time.Now().Unix()) + signer.sessionCredential = &SessionCredential{ + AccessKeyId: accessKeyId.(string), + AccessKeySecret: accessKeySecret.(string), + StsToken: securityToken.(string), + } + + return +} + +func (signer *EcsRamRoleSigner) GetSessionCredential() *SessionCredential { + return signer.sessionCredential +} + +func (signer *EcsRamRoleSigner) Shutdown() { + +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_key_pair.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_key_pair.go new file mode 100644 index 000000000..c5fe39456 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_key_pair.go @@ -0,0 +1,148 @@ +/* + * 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 signers + +import ( + "encoding/json" + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "github.com/jmespath/go-jmespath" + "net/http" + "strconv" +) + +type SignerKeyPair struct { + *credentialUpdater + sessionCredential *SessionCredential + credential *credentials.RsaKeyPairCredential + commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error) +} + +func NewSignerKeyPair(credential *credentials.RsaKeyPairCredential, commonApi func(*requests.CommonRequest, interface{}) (response *responses.CommonResponse, err error)) (signer *SignerKeyPair, err error) { + signer = &SignerKeyPair{ + credential: credential, + commonApi: commonApi, + } + + signer.credentialUpdater = &credentialUpdater{ + credentialExpiration: credential.SessionExpiration, + buildRequestMethod: signer.buildCommonRequest, + responseCallBack: signer.refreshCredential, + refreshApi: signer.refreshApi, + } + + if credential.SessionExpiration > 0 { + if credential.SessionExpiration >= 900 && credential.SessionExpiration <= 3600 { + signer.credentialExpiration = credential.SessionExpiration + } else { + err = errors.NewClientError(errors.InvalidParamErrorCode, "Key Pair session duration should be in the range of 15min - 1Hr", nil) + } + } else { + signer.credentialExpiration = defaultDurationSeconds + } + return +} + +func (*SignerKeyPair) GetName() string { + return "HMAC-SHA1" +} + +func (*SignerKeyPair) GetType() string { + return "" +} + +func (*SignerKeyPair) GetVersion() string { + return "1.0" +} + +func (signer *SignerKeyPair) GetAccessKeyId() (accessKeyId string, err error) { + if signer.sessionCredential == nil || signer.needUpdateCredential() { + err = signer.updateCredential() + } + if err != nil && (signer.sessionCredential == nil || len(signer.sessionCredential.AccessKeyId) <= 0) { + return "", err + } + return signer.sessionCredential.AccessKeyId, err +} + +func (signer *SignerKeyPair) GetExtraParam() map[string]string { + if signer.sessionCredential == nil || signer.needUpdateCredential() { + signer.updateCredential() + } + if signer.sessionCredential == nil || len(signer.sessionCredential.AccessKeyId) <= 0 { + return make(map[string]string) + } + return make(map[string]string) +} + +func (signer *SignerKeyPair) Sign(stringToSign, secretSuffix string) string { + secret := signer.sessionCredential.AccessKeyId + secretSuffix + return ShaHmac1(stringToSign, secret) +} + +func (signer *SignerKeyPair) buildCommonRequest() (request *requests.CommonRequest, err error) { + request = requests.NewCommonRequest() + request.Product = "Sts" + request.Version = "2015-04-01" + request.ApiName = "GenerateSessionAccessKey" + request.Scheme = requests.HTTPS + request.QueryParams["PublicKeyId"] = signer.credential.PublicKeyId + request.QueryParams["DurationSeconds"] = strconv.Itoa(signer.credentialExpiration) + return +} + +func (signerKeyPair *SignerKeyPair) refreshApi(request *requests.CommonRequest) (response *responses.CommonResponse, err error) { + signerV2, err := NewSignerV2(signerKeyPair.credential) + return signerKeyPair.commonApi(request, signerV2) +} + +func (signer *SignerKeyPair) refreshCredential(response *responses.CommonResponse) (err error) { + if response.GetHttpStatus() != http.StatusOK { + message := "refresh session AccessKey failed" + err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), message) + return + } + var data interface{} + err = json.Unmarshal(response.GetHttpContentBytes(), &data) + if err != nil { + fmt.Println("refresh KeyPair err, json.Unmarshal fail", err) + return + } + accessKeyId, err := jmespath.Search("SessionAccessKey.SessionAccessKeyId", data) + if err != nil { + fmt.Println("refresh KeyPair err, fail to get SessionAccessKeyId", err) + return + } + accessKeySecret, err := jmespath.Search("SessionAccessKey.SessionAccessKeySecret", data) + if err != nil { + fmt.Println("refresh KeyPair err, fail to get SessionAccessKeySecret", err) + return + } + if accessKeyId == nil || accessKeySecret == nil { + return + } + signer.sessionCredential = &SessionCredential{ + AccessKeyId: accessKeyId.(string), + AccessKeySecret: accessKeySecret.(string), + } + return +} + +func (signer *SignerKeyPair) Shutdown() { + +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ram_role_arn.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ram_role_arn.go new file mode 100644 index 000000000..bfcaa9573 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_ram_role_arn.go @@ -0,0 +1,174 @@ +/* + * 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 signers + +import ( + "encoding/json" + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "github.com/jmespath/go-jmespath" + "net/http" + "strconv" + "time" +) + +const ( + defaultDurationSeconds = 3600 +) + +type RamRoleArnSigner struct { + *credentialUpdater + roleSessionName string + sessionCredential *SessionCredential + credential *credentials.RamRoleArnCredential + commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error) +} + +func NewRamRoleArnSigner(credential *credentials.RamRoleArnCredential, commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error)) (signer *RamRoleArnSigner, err error) { + signer = &RamRoleArnSigner{ + credential: credential, + commonApi: commonApi, + } + + signer.credentialUpdater = &credentialUpdater{ + credentialExpiration: credential.RoleSessionExpiration, + buildRequestMethod: signer.buildCommonRequest, + responseCallBack: signer.refreshCredential, + refreshApi: signer.refreshApi, + } + + if len(credential.RoleSessionName) > 0 { + signer.roleSessionName = credential.RoleSessionName + } else { + signer.roleSessionName = "aliyun-go-sdk-" + strconv.FormatInt(time.Now().UnixNano()/1000, 10) + } + if credential.RoleSessionExpiration > 0 { + if credential.RoleSessionExpiration >= 900 && credential.RoleSessionExpiration <= 3600 { + signer.credentialExpiration = credential.RoleSessionExpiration + } else { + err = errors.NewClientError(errors.InvalidParamErrorCode, "Assume Role session duration should be in the range of 15min - 1Hr", nil) + } + } else { + signer.credentialExpiration = defaultDurationSeconds + } + return +} + +func (*RamRoleArnSigner) GetName() string { + return "HMAC-SHA1" +} + +func (*RamRoleArnSigner) GetType() string { + return "" +} + +func (*RamRoleArnSigner) GetVersion() string { + return "1.0" +} + +func (signer *RamRoleArnSigner) GetAccessKeyId() (accessKeyId string, err error) { + if signer.sessionCredential == nil || signer.needUpdateCredential() { + err = signer.updateCredential() + } + if err != nil && (signer.sessionCredential == nil || len(signer.sessionCredential.AccessKeyId) <= 0) { + return "", err + } + return signer.sessionCredential.AccessKeyId, nil +} + +func (signer *RamRoleArnSigner) GetExtraParam() map[string]string { + if signer.sessionCredential == nil || signer.needUpdateCredential() { + signer.updateCredential() + } + if signer.sessionCredential == nil || len(signer.sessionCredential.StsToken) <= 0 { + return make(map[string]string) + } + return map[string]string{"SecurityToken": signer.sessionCredential.StsToken} +} + +func (signer *RamRoleArnSigner) Sign(stringToSign, secretSuffix string) string { + secret := signer.sessionCredential.AccessKeySecret + secretSuffix + return ShaHmac1(stringToSign, secret) +} + +func (signer *RamRoleArnSigner) buildCommonRequest() (request *requests.CommonRequest, err error) { + request = requests.NewCommonRequest() + request.Product = "Sts" + request.Version = "2015-04-01" + request.ApiName = "AssumeRole" + request.Scheme = requests.HTTPS + request.QueryParams["RoleArn"] = signer.credential.RoleArn + request.QueryParams["RoleSessionName"] = signer.credential.RoleSessionName + request.QueryParams["DurationSeconds"] = strconv.Itoa(signer.credentialExpiration) + return +} + +func (signer *RamRoleArnSigner) refreshApi(request *requests.CommonRequest) (response *responses.CommonResponse, err error) { + credential := &credentials.AccessKeyCredential{ + AccessKeyId: signer.credential.AccessKeyId, + AccessKeySecret: signer.credential.AccessKeySecret, + } + signerV1, err := NewAccessKeySigner(credential) + return signer.commonApi(request, signerV1) +} + +func (signer *RamRoleArnSigner) refreshCredential(response *responses.CommonResponse) (err error) { + if response.GetHttpStatus() != http.StatusOK { + message := "refresh session token failed" + err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), message) + return + } + var data interface{} + err = json.Unmarshal(response.GetHttpContentBytes(), &data) + if err != nil { + fmt.Println("refresh RoleArn sts token err, json.Unmarshal fail", err) + return + } + accessKeyId, err := jmespath.Search("Credentials.AccessKeyId", data) + if err != nil { + fmt.Println("refresh RoleArn sts token err, fail to get AccessKeyId", err) + return + } + accessKeySecret, err := jmespath.Search("Credentials.AccessKeySecret", data) + if err != nil { + fmt.Println("refresh RoleArn sts token err, fail to get AccessKeySecret", err) + return + } + securityToken, err := jmespath.Search("Credentials.SecurityToken", data) + if err != nil { + fmt.Println("refresh RoleArn sts token err, fail to get SecurityToken", err) + return + } + if accessKeyId == nil || accessKeySecret == nil || securityToken == nil { + return + } + signer.sessionCredential = &SessionCredential{ + AccessKeyId: accessKeyId.(string), + AccessKeySecret: accessKeySecret.(string), + StsToken: securityToken.(string), + } + return +} + +func (signer *RamRoleArnSigner) GetSessionCredential() *SessionCredential { + return signer.sessionCredential +} + +func (signer *RamRoleArnSigner) Shutdown() { + +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_sts_token.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_sts_token.go new file mode 100644 index 000000000..9e178d0fb --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_sts_token.go @@ -0,0 +1,58 @@ +/* + * 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 signers + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" +) + +type StsTokenSigner struct { + credential *credentials.StsTokenCredential +} + +func NewStsTokenSigner(credential *credentials.StsTokenCredential) (*StsTokenSigner, error) { + return &StsTokenSigner{ + credential: credential, + }, nil +} + +func (*StsTokenSigner) GetName() string { + return "HMAC-SHA1" +} + +func (*StsTokenSigner) GetType() string { + return "" +} + +func (*StsTokenSigner) GetVersion() string { + return "1.0" +} + +func (signer *StsTokenSigner) GetAccessKeyId() (accessKeyId string, err error) { + return signer.credential.AccessKeyId, nil +} + +func (signer *StsTokenSigner) GetExtraParam() map[string]string { + return map[string]string{"SecurityToken": signer.credential.AccessKeyStsToken} +} + +func (signer *StsTokenSigner) Sign(stringToSign, secretSuffix string) string { + secret := signer.credential.AccessKeySecret + secretSuffix + return ShaHmac1(stringToSign, secret) +} + +func (signer *StsTokenSigner) Shutdown() { + +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_v2.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_v2.go new file mode 100644 index 000000000..1814fe7bd --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/signers/signer_v2.go @@ -0,0 +1,58 @@ +/* + * 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 signers + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" +) + +type SignerV2 struct { + credential *credentials.RsaKeyPairCredential +} + +func (signer *SignerV2) GetExtraParam() map[string]string { + return nil +} + +func NewSignerV2(credential *credentials.RsaKeyPairCredential) (*SignerV2, error) { + return &SignerV2{ + credential: credential, + }, nil +} + +func (*SignerV2) GetName() string { + return "SHA256withRSA" +} + +func (*SignerV2) GetType() string { + return "PRIVATEKEY" +} + +func (*SignerV2) GetVersion() string { + return "1.0" +} + +func (signer *SignerV2) GetAccessKeyId() (accessKeyId string, err error) { + return signer.credential.PublicKeyId, err +} + +func (signer *SignerV2) Sign(stringToSign, secretSuffix string) string { + secret := signer.credential.PrivateKey + return Sha256WithRsa(stringToSign, secret) +} + +func (signer *SignerV2) Shutdown() { + +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go new file mode 100644 index 000000000..e204676ba --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/client.go @@ -0,0 +1,419 @@ +/* + * 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 sdk + +import ( + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "net" + "net/http" + "strconv" + "sync" +) + +// this value will be replaced while build: -ldflags="-X sdk.version=x.x.x" +var Version = "0.0.1" + +type Client struct { + regionId string + config *Config + signer auth.Signer + httpClient *http.Client + asyncTaskQueue chan func() + + debug bool + isRunning bool + // void "panic(write to close channel)" cause of addAsync() after Shutdown() + asyncChanLock *sync.RWMutex +} + +func (client *Client) Init() (err error) { + panic("not support yet") +} + +func (client *Client) InitWithOptions(regionId string, config *Config, credential auth.Credential) (err error) { + client.isRunning = true + client.asyncChanLock = new(sync.RWMutex) + client.regionId = regionId + client.config = config + if err != nil { + return + } + client.httpClient = &http.Client{} + + if config.HttpTransport != nil { + client.httpClient.Transport = config.HttpTransport + } + + if config.Timeout > 0 { + client.httpClient.Timeout = config.Timeout + } + + if config.EnableAsync { + client.EnableAsync(config.GoRoutinePoolSize, config.MaxTaskQueueSize) + } + + client.signer, err = auth.NewSignerWithCredential(credential, client.ProcessCommonRequestWithSigner) + + return +} + +func (client *Client) EnableAsync(routinePoolSize, maxTaskQueueSize int) { + client.asyncTaskQueue = make(chan func(), maxTaskQueueSize) + for i := 0; i < routinePoolSize; i++ { + go func() { + for client.isRunning { + select { + case task, notClosed := <-client.asyncTaskQueue: + if notClosed { + task() + } + } + } + }() + } +} + +func (client *Client) InitWithAccessKey(regionId, accessKeyId, accessKeySecret string) (err error) { + config := client.InitClientConfig() + credential := &credentials.BaseCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + } + return client.InitWithOptions(regionId, config, credential) +} + +func (client *Client) InitWithStsToken(regionId, accessKeyId, accessKeySecret, securityToken string) (err error) { + config := client.InitClientConfig() + credential := &credentials.StsTokenCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + AccessKeyStsToken: securityToken, + } + return client.InitWithOptions(regionId, config, credential) +} + +func (client *Client) InitWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (err error) { + config := client.InitClientConfig() + credential := &credentials.RamRoleArnCredential{ + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + RoleArn: roleArn, + RoleSessionName: roleSessionName, + } + return client.InitWithOptions(regionId, config, credential) +} + +func (client *Client) InitWithRsaKeyPair(regionId, publicKeyId, privateKey string, sessionExpiration int) (err error) { + config := client.InitClientConfig() + credential := &credentials.RsaKeyPairCredential{ + PrivateKey: privateKey, + PublicKeyId: publicKeyId, + SessionExpiration: sessionExpiration, + } + return client.InitWithOptions(regionId, config, credential) +} + +func (client *Client) InitWithEcsRamRole(regionId, roleName string) (err error) { + config := client.InitClientConfig() + credential := &credentials.EcsRamRoleCredential{ + RoleName: roleName, + } + return client.InitWithOptions(regionId, config, credential) +} + +func (client *Client) InitClientConfig() (config *Config) { + if client.config != nil { + return client.config + } else { + return NewConfig() + } +} + +func (client *Client) DoAction(request requests.AcsRequest, response responses.AcsResponse) (err error) { + return client.DoActionWithSigner(request, response, nil) +} + +func (client *Client) BuildRequestWithSigner(request requests.AcsRequest, signer auth.Signer) (err error) { + // add clientVersion + request.GetHeaders()["x-sdk-core-version"] = Version + + regionId := client.regionId + if len(request.GetRegionId()) > 0 { + regionId = request.GetRegionId() + } + + // resolve endpoint + resolveParam := &endpoints.ResolveParam{ + Domain: request.GetDomain(), + Product: request.GetProduct(), + RegionId: regionId, + LocationProduct: request.GetLocationServiceCode(), + LocationEndpointType: request.GetLocationEndpointType(), + CommonApi: client.ProcessCommonRequest, + } + endpoint, err := endpoints.Resolve(resolveParam) + if err != nil { + return + } + request.SetDomain(endpoint) + + // init request params + err = requests.InitParams(request) + if err != nil { + return + } + + // signature + var finalSigner auth.Signer + if signer != nil { + finalSigner = signer + } else { + finalSigner = client.signer + } + httpRequest, err := buildHttpRequest(request, finalSigner, regionId) + if client.config.UserAgent != "" { + httpRequest.Header.Set("User-Agent", client.config.UserAgent) + } + return err +} + +func (client *Client) DoActionWithSigner(request requests.AcsRequest, response responses.AcsResponse, signer auth.Signer) (err error) { + + // add clientVersion + request.GetHeaders()["x-sdk-core-version"] = Version + + regionId := client.regionId + if len(request.GetRegionId()) > 0 { + regionId = request.GetRegionId() + } + + // resolve endpoint + resolveParam := &endpoints.ResolveParam{ + Domain: request.GetDomain(), + Product: request.GetProduct(), + RegionId: regionId, + LocationProduct: request.GetLocationServiceCode(), + LocationEndpointType: request.GetLocationEndpointType(), + CommonApi: client.ProcessCommonRequest, + } + endpoint, err := endpoints.Resolve(resolveParam) + if err != nil { + return + } + request.SetDomain(endpoint) + + if request.GetScheme() == "" { + request.SetScheme(client.config.Scheme) + } + // init request params + err = requests.InitParams(request) + if err != nil { + return + } + + // signature + var finalSigner auth.Signer + if signer != nil { + finalSigner = signer + } else { + finalSigner = client.signer + } + httpRequest, err := buildHttpRequest(request, finalSigner, regionId) + if client.config.UserAgent != "" { + httpRequest.Header.Set("User-Agent", client.config.UserAgent) + } + if err != nil { + return + } + var httpResponse *http.Response + for retryTimes := 0; retryTimes <= client.config.MaxRetryTime; retryTimes++ { + httpResponse, err = client.httpClient.Do(httpRequest) + + var timeout bool + // receive error + if err != nil { + if !client.config.AutoRetry { + return + } else if timeout = isTimeout(err); !timeout { + // if not timeout error, return + return + } else if retryTimes >= client.config.MaxRetryTime { + // timeout but reached the max retry times, return + timeoutErrorMsg := fmt.Sprintf(errors.TimeoutErrorMessage, strconv.Itoa(retryTimes+1), strconv.Itoa(retryTimes+1)) + err = errors.NewClientError(errors.TimeoutErrorCode, timeoutErrorMsg, err) + return + } + } + // if status code >= 500 or timeout, will trigger retry + if client.config.AutoRetry && (timeout || isServerError(httpResponse)) { + // rewrite signatureNonce and signature + httpRequest, err = buildHttpRequest(request, finalSigner, regionId) + if err != nil { + return + } + continue + } + break + } + err = responses.Unmarshal(response, httpResponse, request.GetAcceptFormat()) + // wrap server errors + if serverErr, ok := err.(*errors.ServerError); ok { + var wrapInfo = map[string]string{} + wrapInfo["StringToSign"] = request.GetStringToSign() + err = errors.WrapServerError(serverErr, wrapInfo) + } + return +} + +func buildHttpRequest(request requests.AcsRequest, singer auth.Signer, regionId string) (httpRequest *http.Request, err error) { + err = auth.Sign(request, singer, regionId) + if err != nil { + return + } + requestMethod := request.GetMethod() + requestUrl := request.BuildUrl() + body := request.GetBodyReader() + httpRequest, err = http.NewRequest(requestMethod, requestUrl, body) + if err != nil { + return + } + for key, value := range request.GetHeaders() { + httpRequest.Header[key] = []string{value} + } + // host is a special case + if host, containsHost := request.GetHeaders()["Host"]; containsHost { + httpRequest.Host = host + } + return +} + +func isTimeout(err error) bool { + if err == nil { + return false + } + netErr, isNetError := err.(net.Error) + return isNetError && netErr.Timeout() +} + +func isServerError(httpResponse *http.Response) bool { + return httpResponse.StatusCode >= http.StatusInternalServerError +} + +/** +only block when any one of the following occurs: +1. the asyncTaskQueue is full, increase the queue size to avoid this +2. Shutdown() in progressing, the client is being closed +**/ +func (client *Client) AddAsyncTask(task func()) (err error) { + if client.asyncTaskQueue != nil { + client.asyncChanLock.RLock() + defer client.asyncChanLock.RUnlock() + if client.isRunning { + client.asyncTaskQueue <- task + } + } else { + err = errors.NewClientError(errors.AsyncFunctionNotEnabledCode, errors.AsyncFunctionNotEnabledMessage, nil) + } + return +} + +func (client *Client) GetConfig() *Config { + return client.config +} + +func NewClient() (client *Client, err error) { + client = &Client{} + err = client.Init() + return +} + +func NewClientWithOptions(regionId string, config *Config, credential auth.Credential) (client *Client, err error) { + client = &Client{} + err = client.InitWithOptions(regionId, config, credential) + return +} + +func NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret string) (client *Client, err error) { + client = &Client{} + err = client.InitWithAccessKey(regionId, accessKeyId, accessKeySecret) + return +} + +func NewClientWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken string) (client *Client, err error) { + client = &Client{} + err = client.InitWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken) + return +} + +func NewClientWithRamRoleArn(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (client *Client, err error) { + client = &Client{} + err = client.InitWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName) + return +} + +func NewClientWithEcsRamRole(regionId string, roleName string) (client *Client, err error) { + client = &Client{} + err = client.InitWithEcsRamRole(regionId, roleName) + return +} + +func NewClientWithRsaKeyPair(regionId string, publicKeyId, privateKey string, sessionExpiration int) (client *Client, err error) { + client = &Client{} + err = client.InitWithRsaKeyPair(regionId, publicKeyId, privateKey, sessionExpiration) + return +} + +// Deprecated: Use NewClientWithRamRoleArn in this package instead. +func NewClientWithStsRoleArn(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (client *Client, err error) { + return NewClientWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName) +} + +// Deprecated: Use NewClientWithEcsRamRole in this package instead. +func NewClientWithStsRoleNameOnEcs(regionId string, roleName string) (client *Client, err error) { + return NewClientWithEcsRamRole(regionId, roleName) +} + +func (client *Client) ProcessCommonRequest(request *requests.CommonRequest) (response *responses.CommonResponse, err error) { + request.TransToAcsRequest() + response = responses.NewCommonResponse() + err = client.DoAction(request, response) + return +} + +func (client *Client) ProcessCommonRequestWithSigner(request *requests.CommonRequest, signerInterface interface{}) (response *responses.CommonResponse, err error) { + if signer, isSigner := signerInterface.(auth.Signer); isSigner { + request.TransToAcsRequest() + response = responses.NewCommonResponse() + err = client.DoActionWithSigner(request, response, signer) + return + } else { + panic("should not be here") + } +} + +func (client *Client) Shutdown() { + client.signer.Shutdown() + // lock the addAsync() + client.asyncChanLock.Lock() + defer client.asyncChanLock.Unlock() + client.isRunning = false + close(client.asyncTaskQueue) +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go new file mode 100644 index 000000000..c67c2ad09 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/config.go @@ -0,0 +1,85 @@ +/* + * 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 sdk + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils" + "net/http" + "time" +) + +type Config struct { + AutoRetry bool `default:"true"` + MaxRetryTime int `default:"3"` + UserAgent string `default:""` + Debug bool `default:"false"` + Timeout time.Duration `default:"10000000000"` + HttpTransport *http.Transport `default:""` + EnableAsync bool `default:"false"` + MaxTaskQueueSize int `default:"1000"` + GoRoutinePoolSize int `default:"5"` + Scheme string `default:"HTTP"` +} + +func NewConfig() (config *Config) { + config = &Config{} + utils.InitStructWithDefaultTag(config) + return +} + +func (c *Config) WithTimeout(timeout time.Duration) *Config { + c.Timeout = timeout + return c +} + +func (c *Config) WithAutoRetry(isAutoRetry bool) *Config { + c.AutoRetry = isAutoRetry + return c +} + +func (c *Config) WithMaxRetryTime(maxRetryTime int) *Config { + c.MaxRetryTime = maxRetryTime + return c +} + +func (c *Config) WithUserAgent(userAgent string) *Config { + c.UserAgent = userAgent + return c +} + +func (c *Config) WithHttpTransport(httpTransport *http.Transport) *Config { + c.HttpTransport = httpTransport + return c +} + +func (c *Config) WithEnableAsync(isEnableAsync bool) *Config { + c.EnableAsync = isEnableAsync + return c +} + +func (c *Config) WithMaxTaskQueueSize(maxTaskQueueSize int) *Config { + c.MaxTaskQueueSize = maxTaskQueueSize + return c +} + +func (c *Config) WithGoRoutinePoolSize(goRoutinePoolSize int) *Config { + c.GoRoutinePoolSize = goRoutinePoolSize + return c +} + +func (c *Config) WithDebug(isDebug bool) *Config { + c.Debug = isDebug + return c +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/endpoints_config.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/endpoints_config.go new file mode 100644 index 000000000..ec0d56f60 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/endpoints_config.go @@ -0,0 +1,505 @@ +package endpoints + +import ( + "encoding/json" + "fmt" + "sync" +) + +const endpointsJson = "{" + + " \"products\":[" + + " {" + + " \"code\": \"aegis\"," + + " \"document_id\": \"28449\"," + + " \"location_service_code\": \"vipaegis\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"aegis.cn-hangzhou.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"alidns\"," + + " \"document_id\": \"29739\"," + + " \"location_service_code\": \"alidns\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"alidns.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"arms\"," + + " \"document_id\": \"42924\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"ap-southeast-1\"," + + " \"endpoint\": \"arms.ap-southeast-1.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-beijing\"," + + " \"endpoint\": \"arms.cn-beijing.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-hangzhou\"," + + " \"endpoint\": \"arms.cn-hangzhou.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-hongkong\"," + + " \"endpoint\": \"arms.cn-hongkong.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-qingdao\"," + + " \"endpoint\": \"arms.cn-qingdao.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shanghai\"," + + " \"endpoint\": \"arms.cn-shanghai.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shenzhen\"," + + " \"endpoint\": \"arms.cn-shenzhen.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"arms.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"batchcompute\"," + + " \"document_id\": \"44717\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"ap-southeast-1\"," + + " \"endpoint\": \"batchcompute.ap-southeast-1.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-beijing\"," + + " \"endpoint\": \"batchcompute.cn-beijing.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-hangzhou\"," + + " \"endpoint\": \"batchcompute.cn-hangzhou.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-huhehaote\"," + + " \"endpoint\": \"batchcompute.cn-huhehaote.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-qingdao\"," + + " \"endpoint\": \"batchcompute.cn-qingdao.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shanghai\"," + + " \"endpoint\": \"batchcompute.cn-shanghai.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shenzhen\"," + + " \"endpoint\": \"batchcompute.cn-shenzhen.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-zhangjiakou\"," + + " \"endpoint\": \"batchcompute.cn-zhangjiakou.aliyuncs.com\"" + + " }, {" + + " \"region\": \"us-west-1\"," + + " \"endpoint\": \"batchcompute.us-west-1.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"batchcompute.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"ccc\"," + + " \"document_id\": \"63027\"," + + " \"location_service_code\": \"ccc\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"cn-hangzhou\"," + + " \"endpoint\": \"ccc.cn-hangzhou.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shanghai\"," + + " \"endpoint\": \"ccc.cn-shanghai.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"ccc.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"cdn\"," + + " \"document_id\": \"27148\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cdn.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"cds\"," + + " \"document_id\": \"62887\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cds.cn-beijing.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"chatbot\"," + + " \"document_id\": \"60760\"," + + " \"location_service_code\": \"beebot\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"chatbot.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"cloudapi\"," + + " \"document_id\": \"43590\"," + + " \"location_service_code\": \"apigateway\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"ap-northeast-1\"," + + " \"endpoint\": \"apigateway.ap-northeast-1.aliyuncs.com\"" + + " }, {" + + " \"region\": \"us-west-1\"," + + " \"endpoint\": \"apigateway.us-west-1.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"apigateway.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"cloudauth\"," + + " \"document_id\": \"60687\"," + + " \"location_service_code\": \"cloudauth\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cloudauth.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"cloudphoto\"," + + " \"document_id\": \"59902\"," + + " \"location_service_code\": \"cloudphoto\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"cloudphoto.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"cloudwf\"," + + " \"document_id\": \"58111\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cloudwf.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"cms\"," + + " \"document_id\": \"28615\"," + + " \"location_service_code\": \"cms\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"cr\"," + + " \"document_id\": \"60716\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cr.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"cs\"," + + " \"document_id\": \"26043\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cs.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"csb\"," + + " \"document_id\": \"64837\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"cn-beijing\"," + + " \"endpoint\": \"csb.cn-beijing.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-hangzhou\"," + + " \"endpoint\": \"csb.cn-hangzhou.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"csb.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"dds\"," + + " \"document_id\": \"61715\"," + + " \"location_service_code\": \"dds\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"mongodb.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"mongodb.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"dm\"," + + " \"document_id\": \"29434\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"ap-southeast-1\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"ap-southeast-2\"," + + " \"endpoint\": \"dm.ap-southeast-2.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-beijing\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-hangzhou\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-hongkong\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-qingdao\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shanghai\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"cn-shenzhen\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"us-east-1\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }, {" + + " \"region\": \"us-west-1\"," + + " \"endpoint\": \"dm.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"dm.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"dm.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"domain\"," + + " \"document_id\": \"42875\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"domain.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"domain.aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"domain-intl\"," + + " \"document_id\": \"\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"domain-intl.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"domain-intl.aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"drds\"," + + " \"document_id\": \"51111\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"drds.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"drds.aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"ecs\"," + + " \"document_id\": \"25484\"," + + " \"location_service_code\": \"ecs\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"emr\"," + + " \"document_id\": \"28140\"," + + " \"location_service_code\": \"emr\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"emr.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"ess\"," + + " \"document_id\": \"25925\"," + + " \"location_service_code\": \"ess\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"ess.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"green\"," + + " \"document_id\": \"28427\"," + + " \"location_service_code\": \"green\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"green.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"hpc\"," + + " \"document_id\": \"35201\"," + + " \"location_service_code\": \"hpc\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"hpc.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"httpdns\"," + + " \"document_id\": \"52679\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"httpdns-api.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"iot\"," + + " \"document_id\": \"30557\"," + + " \"location_service_code\": \"iot\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"iot.[RegionId].aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"itaas\"," + + " \"document_id\": \"55759\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"itaas.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"jaq\"," + + " \"document_id\": \"35037\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"jaq.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"live\"," + + " \"document_id\": \"48207\"," + + " \"location_service_code\": \"live\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"live.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"mts\"," + + " \"document_id\": \"29212\"," + + " \"location_service_code\": \"mts\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"nas\"," + + " \"document_id\": \"62598\"," + + " \"location_service_code\": \"nas\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"ons\"," + + " \"document_id\": \"44416\"," + + " \"location_service_code\": \"ons\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"polardb\"," + + " \"document_id\": \"58764\"," + + " \"location_service_code\": \"polardb\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"ap-south-1\"," + + " \"endpoint\": \"polardb.ap-south-1.aliyuncs.com\"" + + " }, {" + + " \"region\": \"ap-southeast-5\"," + + " \"endpoint\": \"polardb.ap-southeast-5.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"polardb.aliyuncs.com\"" + + " }," + + " {" + + " \"code\": \"push\"," + + " \"document_id\": \"30074\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"cloudpush.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"qualitycheck\"," + + " \"document_id\": \"50807\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": [ {" + + " \"region\": \"cn-hangzhou\"," + + " \"endpoint\": \"qualitycheck.cn-hangzhou.aliyuncs.com\"" + + " }]," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"r-kvstore\"," + + " \"document_id\": \"60831\"," + + " \"location_service_code\": \"redisa\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"ram\"," + + " \"document_id\": \"28672\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"ram.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"rds\"," + + " \"document_id\": \"26223\"," + + " \"location_service_code\": \"rds\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"ros\"," + + " \"document_id\": \"28899\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"ros.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"sas-api\"," + + " \"document_id\": \"28498\"," + + " \"location_service_code\": \"sas\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"slb\"," + + " \"document_id\": \"27565\"," + + " \"location_service_code\": \"slb\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"sts\"," + + " \"document_id\": \"28756\"," + + " \"location_service_code\": \"\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"sts.aliyuncs.com\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"vod\"," + + " \"document_id\": \"60574\"," + + " \"location_service_code\": \"vod\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"vpc\"," + + " \"document_id\": \"34962\"," + + " \"location_service_code\": \"vpc\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }," + + " {" + + " \"code\": \"waf\"," + + " \"document_id\": \"62847\"," + + " \"location_service_code\": \"waf\"," + + " \"regional_endpoints\": []," + + " \"global_endpoint\": \"\"," + + " \"regional_endpoint_pattern\": \"\"" + + " }]" + + "}" + +var initOnce sync.Once +var data interface{} + +func getEndpointConfigData() interface{} { + initOnce.Do(func() { + err := json.Unmarshal([]byte(endpointsJson), &data) + if err != nil { + fmt.Println("init endpoint config data failed.", err) + } + }) + return data +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_global_resolver.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_global_resolver.go new file mode 100644 index 000000000..864b7cc4c --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_global_resolver.go @@ -0,0 +1,37 @@ +/* + * 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 endpoints + +import ( + "fmt" + "github.com/jmespath/go-jmespath" + "strings" +) + +type LocalGlobalResolver struct { +} + +func (resolver *LocalGlobalResolver) TryResolve(param *ResolveParam) (endpoint string, support bool, err error) { + // get the global endpoints configs + endpointExpression := fmt.Sprintf("products[?code=='%s'].global_endpoint", strings.ToLower(param.Product)) + endpointData, err := jmespath.Search(endpointExpression, getEndpointConfigData()) + if err == nil && endpointData != nil && len(endpointData.([]interface{})) > 0 { + endpoint = endpointData.([]interface{})[0].(string) + support = len(endpoint) > 0 + return endpoint, support, nil + } + support = false + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_regional_resolver.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_regional_resolver.go new file mode 100644 index 000000000..f8aa0f92a --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/local_regional_resolver.go @@ -0,0 +1,41 @@ +/* + * 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 endpoints + +import ( + "fmt" + "github.com/jmespath/go-jmespath" + "strings" +) + +type LocalRegionalResolver struct { +} + +func (resolver *LocalRegionalResolver) TryResolve(param *ResolveParam) (endpoint string, support bool, err error) { + // get the regional endpoints configs + regionalExpression := fmt.Sprintf("products[?code=='%s'].regional_endpoints", strings.ToLower(param.Product)) + regionalData, err := jmespath.Search(regionalExpression, getEndpointConfigData()) + if err == nil && regionalData != nil && len(regionalData.([]interface{})) > 0 { + endpointExpression := fmt.Sprintf("[0][?region=='%s'].endpoint", strings.ToLower(param.RegionId)) + endpointData, err := jmespath.Search(endpointExpression, regionalData) + if err == nil && endpointData != nil && len(endpointData.([]interface{})) > 0 { + endpoint = endpointData.([]interface{})[0].(string) + support = len(endpoint) > 0 + return endpoint, support, nil + } + } + support = false + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/location_resolver.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/location_resolver.go new file mode 100644 index 000000000..803bc57d5 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/location_resolver.go @@ -0,0 +1,139 @@ +/* + * 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 endpoints + +import ( + "encoding/json" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "sync" + "time" +) + +const ( + EndpointCacheExpireTime = 3600 //Seconds +) + +var lastClearTimePerProduct = struct { + sync.RWMutex + cache map[string]int64 +}{cache: make(map[string]int64)} + +var endpointCache = struct { + sync.RWMutex + cache map[string]string +}{cache: make(map[string]string)} + +type LocationResolver struct { +} + +func (resolver *LocationResolver) TryResolve(param *ResolveParam) (endpoint string, support bool, err error) { + if len(param.LocationProduct) <= 0 { + support = false + return + } + + //get from cache + cacheKey := param.Product + "#" + param.RegionId + if endpointCache.cache != nil && len(endpointCache.cache[cacheKey]) > 0 && !CheckCacheIsExpire(cacheKey) { + endpoint = endpointCache.cache[cacheKey] + support = true + return + } + + //get from remote + getEndpointRequest := requests.NewCommonRequest() + + getEndpointRequest.Product = "Location" + getEndpointRequest.Version = "2015-06-12" + getEndpointRequest.ApiName = "DescribeEndpoints" + getEndpointRequest.Domain = "location.aliyuncs.com" + getEndpointRequest.Method = "GET" + getEndpointRequest.Scheme = requests.HTTPS + + getEndpointRequest.QueryParams["Id"] = param.RegionId + getEndpointRequest.QueryParams["ServiceCode"] = param.LocationProduct + if len(param.LocationEndpointType) > 0 { + getEndpointRequest.QueryParams["Type"] = param.LocationEndpointType + } else { + getEndpointRequest.QueryParams["Type"] = "openAPI" + } + + response, err := param.CommonApi(getEndpointRequest) + var getEndpointResponse GetEndpointResponse + if !response.IsSuccess() { + support = false + return + } + + json.Unmarshal([]byte(response.GetHttpContentString()), &getEndpointResponse) + if !getEndpointResponse.Success || getEndpointResponse.Endpoints == nil { + support = false + return + } + if len(getEndpointResponse.Endpoints.Endpoint) <= 0 { + support = false + return + } + if len(getEndpointResponse.Endpoints.Endpoint[0].Endpoint) > 0 { + endpoint = getEndpointResponse.Endpoints.Endpoint[0].Endpoint + endpointCache.Lock() + endpointCache.cache[cacheKey] = endpoint + endpointCache.Unlock() + lastClearTimePerProduct.Lock() + lastClearTimePerProduct.cache[cacheKey] = time.Now().Unix() + lastClearTimePerProduct.Unlock() + support = true + return + } + + support = false + return +} + +func CheckCacheIsExpire(cacheKey string) bool { + lastClearTime := lastClearTimePerProduct.cache[cacheKey] + if lastClearTime <= 0 { + lastClearTime = time.Now().Unix() + lastClearTimePerProduct.Lock() + lastClearTimePerProduct.cache[cacheKey] = lastClearTime + lastClearTimePerProduct.Unlock() + } + + now := time.Now().Unix() + elapsedTime := now - lastClearTime + if elapsedTime > EndpointCacheExpireTime { + return true + } + + return false +} + +type GetEndpointResponse struct { + Endpoints *EndpointsObj + RequestId string + Success bool +} + +type EndpointsObj struct { + Endpoint []EndpointObj +} + +type EndpointObj struct { + Protocols map[string]string + Type string + Namespace string + Id string + SerivceCode string + Endpoint string +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/mapping_resolver.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/mapping_resolver.go new file mode 100644 index 000000000..f7b5a1aa2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/mapping_resolver.go @@ -0,0 +1,39 @@ +/* + * 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 endpoints + +import ( + "fmt" + "strings" +) + +const keyFormatter = "%s::%s" + +var endpointMapping = make(map[string]string) + +func AddEndpointMapping(regionId, productId, endpoint string) (err error) { + key := fmt.Sprintf(keyFormatter, strings.ToLower(regionId), strings.ToLower(productId)) + endpointMapping[key] = endpoint + return nil +} + +type MappingResolver struct { +} + +func (resolver *MappingResolver) TryResolve(param *ResolveParam) (endpoint string, support bool, err error) { + key := fmt.Sprintf(keyFormatter, strings.ToLower(param.RegionId), strings.ToLower(param.Product)) + endpoint, contains := endpointMapping[key] + return endpoint, contains, nil +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/resolver.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/resolver.go new file mode 100644 index 000000000..e58cfbf28 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/resolver.go @@ -0,0 +1,80 @@ +/* + * 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 endpoints + +import ( + "encoding/json" + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" + "sync" +) + +const ( + ResolveEndpointUserGuideLink = "" +) + +var once sync.Once +var resolvers []Resolver + +type Resolver interface { + TryResolve(param *ResolveParam) (endpoint string, support bool, err error) +} + +func Resolve(param *ResolveParam) (endpoint string, err error) { + supportedResolvers := getAllResolvers() + for _, resolver := range supportedResolvers { + endpoint, supported, err := resolver.TryResolve(param) + if supported { + return endpoint, err + } + } + + // not support + errorMsg := fmt.Sprintf(errors.CanNotResolveEndpointErrorMessage, param, ResolveEndpointUserGuideLink) + err = errors.NewClientError(errors.CanNotResolveEndpointErrorCode, errorMsg, nil) + return +} + +func getAllResolvers() []Resolver { + once.Do(func() { + resolvers = []Resolver{ + &SimpleHostResolver{}, + &MappingResolver{}, + &LocationResolver{}, + &LocalRegionalResolver{}, + &LocalGlobalResolver{}, + } + }) + return resolvers +} + +type ResolveParam struct { + Domain string + Product string + RegionId string + LocationProduct string + LocationEndpointType string + CommonApi func(request *requests.CommonRequest) (response *responses.CommonResponse, err error) `json:"-"` +} + +func (param *ResolveParam) String() string { + jsonBytes, err := json.Marshal(param) + if err != nil { + return fmt.Sprint("ResolveParam.String() process error:", err) + } + return string(jsonBytes) +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/simple_host_resolver.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/simple_host_resolver.go new file mode 100644 index 000000000..3e2e731ea --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints/simple_host_resolver.go @@ -0,0 +1,25 @@ +/* + * 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 endpoints + +type SimpleHostResolver struct { +} + +func (resolver *SimpleHostResolver) TryResolve(param *ResolveParam) (endpoint string, support bool, err error) { + if support = len(param.Domain) > 0; support { + endpoint = param.Domain + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go new file mode 100644 index 000000000..3d4048b4e --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/client_error.go @@ -0,0 +1,92 @@ +/* + * 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 errors + +import "fmt" + +const ( + DefaultClientErrorStatus = 400 + DefaultClientErrorCode = "SDK.ClientError" + + UnsupportedCredentialErrorCode = "SDK.UnsupportedCredential" + UnsupportedCredentialErrorMessage = "Specified credential (type = %s) is not supported, please check" + + CanNotResolveEndpointErrorCode = "SDK.CanNotResolveEndpoint" + CanNotResolveEndpointErrorMessage = "Can not resolve endpoint(param = %s), please check your accessKey with secret, and read the user guide\n %s" + + UnsupportedParamPositionErrorCode = "SDK.UnsupportedParamPosition" + UnsupportedParamPositionErrorMessage = "Specified param position (%s) is not supported, please upgrade sdk and retry" + + AsyncFunctionNotEnabledCode = "SDK.AsyncFunctionNotEnabled" + AsyncFunctionNotEnabledMessage = "Async function is not enabled in client, please invoke 'client.EnableAsync' function" + + UnknownRequestTypeErrorCode = "SDK.UnknownRequestType" + UnknownRequestTypeErrorMessage = "Unknown Request Type: %s" + + MissingParamErrorCode = "SDK.MissingParam" + InvalidParamErrorCode = "SDK.InvalidParam" + + JsonUnmarshalErrorCode = "SDK.JsonUnmarshalError" + JsonUnmarshalErrorMessage = "Failed to unmarshal response, but you can get the data via response.GetHttpStatusCode() and response.GetHttpContentString()" + + TimeoutErrorCode = "SDK.TimeoutError" + TimeoutErrorMessage = "The request timed out %s times(%s for retry), perhaps we should have the threshold raised a little?" +) + +type ClientError struct { + errorCode string + message string + originError error +} + +func NewClientError(errorCode, message string, originErr error) Error { + return &ClientError{ + errorCode: errorCode, + message: message, + originError: originErr, + } +} + +func (err *ClientError) Error() string { + clientErrMsg := fmt.Sprintf("[%s] %s", err.errorCode, err.message) + if err.originError != nil { + return clientErrMsg + "\ncaused by:\n" + err.originError.Error() + } + return clientErrMsg +} + +func (err *ClientError) OriginError() error { + return err.originError +} + +func (*ClientError) HttpStatus() int { + return DefaultClientErrorStatus +} + +func (err *ClientError) ErrorCode() string { + if err.errorCode == "" { + return DefaultClientErrorCode + } else { + return err.errorCode + } +} + +func (err *ClientError) Message() string { + return err.message +} + +func (err *ClientError) String() string { + return err.Error() +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/error.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/error.go new file mode 100644 index 000000000..49962f3b5 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/error.go @@ -0,0 +1,23 @@ +/* + * 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 errors + +type Error interface { + error + HttpStatus() int + ErrorCode() string + Message() string + OriginError() error +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go new file mode 100644 index 000000000..d90638c0a --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/server_error.go @@ -0,0 +1,122 @@ +/* + * 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 errors + +import ( + "encoding/json" + "fmt" + "github.com/jmespath/go-jmespath" +) + +var wrapperList = []ServerErrorWrapper{ + &SignatureDostNotMatchWrapper{}, +} + +type ServerError struct { + httpStatus int + requestId string + hostId string + errorCode string + recommend string + message string + comment string +} + +type ServerErrorWrapper interface { + tryWrap(error *ServerError, wrapInfo map[string]string) (bool, *ServerError) +} + +func (err *ServerError) Error() string { + return fmt.Sprintf("SDK.ServerError\nErrorCode: %s\nRecommend: %s\nRequestId: %s\nMessage: %s", + err.errorCode, err.comment+err.recommend, err.requestId, err.message) +} + +func NewServerError(httpStatus int, responseContent, comment string) Error { + result := &ServerError{ + httpStatus: httpStatus, + message: responseContent, + comment: comment, + } + + var data interface{} + err := json.Unmarshal([]byte(responseContent), &data) + if err == nil { + requestId, _ := jmespath.Search("RequestId", data) + hostId, _ := jmespath.Search("HostId", data) + errorCode, _ := jmespath.Search("Code", data) + recommend, _ := jmespath.Search("Recommend", data) + message, _ := jmespath.Search("Message", data) + + if requestId != nil { + result.requestId = requestId.(string) + } + if hostId != nil { + result.hostId = hostId.(string) + } + if errorCode != nil { + result.errorCode = errorCode.(string) + } + if recommend != nil { + result.recommend = recommend.(string) + } + if message != nil { + result.message = message.(string) + } + } + + return result +} + +func WrapServerError(originError *ServerError, wrapInfo map[string]string) *ServerError { + for _, wrapper := range wrapperList { + ok, newError := wrapper.tryWrap(originError, wrapInfo) + if ok { + return newError + } + } + return originError +} + +func (err *ServerError) HttpStatus() int { + return err.httpStatus +} + +func (err *ServerError) ErrorCode() string { + return err.errorCode +} + +func (err *ServerError) Message() string { + return err.message +} + +func (err *ServerError) OriginError() error { + return nil +} + +func (err *ServerError) HostId() string { + return err.hostId +} + +func (err *ServerError) RequestId() string { + return err.requestId +} + +func (err *ServerError) Recommend() string { + return err.recommend +} + +func (err *ServerError) Comment() string { + return err.comment +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/signature_does_not_match_wrapper.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/signature_does_not_match_wrapper.go new file mode 100644 index 000000000..33b3e4c44 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors/signature_does_not_match_wrapper.go @@ -0,0 +1,29 @@ +package errors + +import "strings" + +const SignatureDostNotMatchErrorCode = "SignatureDoesNotMatch" +const MessagePrefix = "Specified signature is not matched with our calculation. server string to sign is:" + +type SignatureDostNotMatchWrapper struct { +} + +func (*SignatureDostNotMatchWrapper) tryWrap(error *ServerError, wrapInfo map[string]string) (bool, *ServerError) { + clientStringToSign := wrapInfo["StringToSign"] + if error.errorCode == SignatureDostNotMatchErrorCode && clientStringToSign != "" { + message := error.message + if strings.HasPrefix(message, MessagePrefix) { + serverStringToSign := message[len(MessagePrefix):] + if clientStringToSign == serverStringToSign { + // user secret is error + error.recommend = "Please check you AccessKeySecret" + } else { + error.recommend = "This may be a bug with the SDK and we hope you can submit this question in the " + + "github issue(https://github.com/aliyun/alibaba-cloud-sdk-go/issues), thanks very much" + } + } + return true, error + } else { + return false, nil + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/acs_reqeust.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/acs_reqeust.go new file mode 100644 index 000000000..5f4a42bb4 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/acs_reqeust.go @@ -0,0 +1,309 @@ +/* + * 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 requests + +import ( + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "io" + "reflect" + "strconv" +) + +const ( + RPC = "RPC" + ROA = "ROA" + + HTTP = "HTTP" + HTTPS = "HTTPS" + + DefaultHttpPort = "80" + + GET = "GET" + PUT = "PUT" + POST = "POST" + DELETE = "DELETE" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + + Json = "application/json" + Xml = "application/xml" + Raw = "application/octet-stream" + Form = "application/x-www-form-urlencoded" + + Header = "Header" + Query = "Query" + Body = "Body" + Path = "Path" + + HeaderSeparator = "\n" +) + +// interface +type AcsRequest interface { + GetScheme() string + GetMethod() string + GetDomain() string + GetPort() string + GetRegionId() string + GetUrl() string + GetQueries() string + GetHeaders() map[string]string + GetQueryParams() map[string]string + GetFormParams() map[string]string + GetContent() []byte + GetBodyReader() io.Reader + GetStyle() string + GetProduct() string + GetVersion() string + GetActionName() string + GetAcceptFormat() string + GetLocationServiceCode() string + GetLocationEndpointType() string + + SetStringToSign(stringToSign string) + GetStringToSign() string + + SetDomain(domain string) + SetContent(content []byte) + SetScheme(scheme string) + BuildUrl() string + BuildQueries() string + + addHeaderParam(key, value string) + addQueryParam(key, value string) + addFormParam(key, value string) + addPathParam(key, value string) +} + +// base class +type baseRequest struct { + Scheme string + Method string + Domain string + Port string + RegionId string + + product string + version string + + actionName string + + AcceptFormat string + + QueryParams map[string]string + Headers map[string]string + FormParams map[string]string + Content []byte + + locationServiceCode string + locationEndpointType string + + queries string + + stringToSign string +} + +func (request *baseRequest) GetQueryParams() map[string]string { + return request.QueryParams +} + +func (request *baseRequest) GetFormParams() map[string]string { + return request.FormParams +} + +func (request *baseRequest) GetContent() []byte { + return request.Content +} + +func (request *baseRequest) GetVersion() string { + return request.version +} + +func (request *baseRequest) GetActionName() string { + return request.actionName +} + +func (request *baseRequest) SetContent(content []byte) { + request.Content = content +} + +func (request *baseRequest) addHeaderParam(key, value string) { + request.Headers[key] = value +} + +func (request *baseRequest) addQueryParam(key, value string) { + request.QueryParams[key] = value +} + +func (request *baseRequest) addFormParam(key, value string) { + request.FormParams[key] = value +} + +func (request *baseRequest) GetAcceptFormat() string { + return request.AcceptFormat +} + +func (request *baseRequest) GetLocationServiceCode() string { + return request.locationServiceCode +} + +func (request *baseRequest) GetLocationEndpointType() string { + return request.locationEndpointType +} + +func (request *baseRequest) GetProduct() string { + return request.product +} + +func (request *baseRequest) GetScheme() string { + return request.Scheme +} + +func (request *baseRequest) SetScheme(scheme string) { + request.Scheme = scheme +} + +func (request *baseRequest) GetMethod() string { + return request.Method +} + +func (request *baseRequest) GetDomain() string { + return request.Domain +} + +func (request *baseRequest) SetDomain(host string) { + request.Domain = host +} + +func (request *baseRequest) GetPort() string { + return request.Port +} + +func (request *baseRequest) GetRegionId() string { + return request.RegionId +} + +func (request *baseRequest) GetHeaders() map[string]string { + return request.Headers +} + +func (request *baseRequest) SetContentType(contentType string) { + request.Headers["Content-Type"] = contentType +} + +func (request *baseRequest) GetContentType() (contentType string, contains bool) { + contentType, contains = request.Headers["Content-Type"] + return +} + +func (request *baseRequest) SetStringToSign(stringToSign string) { + request.stringToSign = stringToSign +} + +func (request *baseRequest) GetStringToSign() string { + return request.stringToSign +} + +func defaultBaseRequest() (request *baseRequest) { + request = &baseRequest{ + Scheme: "", + AcceptFormat: "JSON", + Method: GET, + QueryParams: make(map[string]string), + Headers: map[string]string{ + "x-sdk-client": "golang/1.0.0", + "x-sdk-invoke-type": "normal", + "Accept-Encoding": "identity", + }, + FormParams: make(map[string]string), + } + return +} + +func InitParams(request AcsRequest) (err error) { + requestValue := reflect.ValueOf(request).Elem() + err = flatRepeatedList(requestValue, request, "", "") + return +} + +func flatRepeatedList(dataValue reflect.Value, request AcsRequest, position, prefix string) (err error) { + dataType := dataValue.Type() + for i := 0; i < dataType.NumField(); i++ { + field := dataType.Field(i) + name, containsNameTag := field.Tag.Lookup("name") + fieldPosition := position + if fieldPosition == "" { + fieldPosition, _ = field.Tag.Lookup("position") + } + typeTag, containsTypeTag := field.Tag.Lookup("type") + if containsNameTag { + if !containsTypeTag { + // simple param + key := prefix + name + value := dataValue.Field(i).String() + err = addParam(request, fieldPosition, key, value) + if err != nil { + return + } + } else if typeTag == "Repeated" { + // repeated param + repeatedFieldValue := dataValue.Field(i) + if repeatedFieldValue.Kind() != reflect.Slice { + // possible value: {"[]string", "*[]struct"}, we must call Elem() in the last condition + repeatedFieldValue = repeatedFieldValue.Elem() + } + if repeatedFieldValue.IsValid() && !repeatedFieldValue.IsNil() { + for m := 0; m < repeatedFieldValue.Len(); m++ { + elementValue := repeatedFieldValue.Index(m) + key := prefix + name + "." + strconv.Itoa(m+1) + if elementValue.Type().String() == "string" { + value := elementValue.String() + err = addParam(request, fieldPosition, key, value) + if err != nil { + return + } + } else { + err = flatRepeatedList(elementValue, request, fieldPosition, key+".") + if err != nil { + return + } + } + } + } + } + } + } + return +} + +func addParam(request AcsRequest, position, name, value string) (err error) { + if len(value) > 0 { + switch position { + case Header: + request.addHeaderParam(name, value) + case Query: + request.addQueryParam(name, value) + case Path: + request.addPathParam(name, value) + case Body: + request.addFormParam(name, value) + default: + errMsg := fmt.Sprintf(errors.UnsupportedParamPositionErrorMessage, position) + err = errors.NewClientError(errors.UnsupportedParamPositionErrorCode, errMsg, nil) + } + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/common_request.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/common_request.go new file mode 100644 index 000000000..d5d841b42 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/common_request.go @@ -0,0 +1,128 @@ +package requests + +import ( + "bytes" + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "io" + "strings" +) + +type CommonRequest struct { + *baseRequest + + Version string + ApiName string + Product string + + // roa params + PathPattern string + PathParams map[string]string + + Ontology AcsRequest +} + +func NewCommonRequest() (request *CommonRequest) { + request = &CommonRequest{ + baseRequest: defaultBaseRequest(), + } + request.Headers["x-sdk-invoke-type"] = "common" + request.PathParams = make(map[string]string) + return +} + +func (request *CommonRequest) String() string { + request.TransToAcsRequest() + request.BuildQueries() + request.BuildUrl() + + resultBuilder := bytes.Buffer{} + + mapOutput := func(m map[string]string) { + if len(m) > 0 { + for key, value := range m { + resultBuilder.WriteString(key + ": " + value + "\n") + } + } + } + + // Request Line + resultBuilder.WriteString("\n") + resultBuilder.WriteString(fmt.Sprintf("%s %s %s/1.1\n", request.Method, request.GetQueries(), strings.ToUpper(request.Scheme))) + + // Headers + resultBuilder.WriteString("Host" + ": " + request.Domain + "\n") + mapOutput(request.Headers) + + resultBuilder.WriteString("\n") + // Body + if len(request.Content) > 0 { + resultBuilder.WriteString(string(request.Content) + "\n") + } else { + mapOutput(request.FormParams) + } + + return resultBuilder.String() +} + +func (request *CommonRequest) TransToAcsRequest() { + if len(request.Version) == 0 { + errors.NewClientError(errors.MissingParamErrorCode, "Common request [version] is required", nil) + } + if len(request.ApiName) == 0 && len(request.PathPattern) == 0 { + errors.NewClientError(errors.MissingParamErrorCode, "At least one of [ApiName] and [PathPattern] should has a value", nil) + } + if len(request.Domain) == 0 && len(request.Product) == 0 { + errors.NewClientError(errors.MissingParamErrorCode, "At least one of [Domain] and [Product] should has a value", nil) + } + + if len(request.PathPattern) > 0 { + roaRequest := &RoaRequest{} + roaRequest.initWithCommonRequest(request) + request.Ontology = roaRequest + } else { + rpcRequest := &RpcRequest{} + rpcRequest.baseRequest = request.baseRequest + rpcRequest.product = request.Product + rpcRequest.version = request.Version + rpcRequest.actionName = request.ApiName + request.Ontology = rpcRequest + } + +} + +func (request *CommonRequest) BuildUrl() string { + if len(request.Port) > 0 { + return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.BuildQueries() + } + + return strings.ToLower(request.Scheme) + "://" + request.Domain + request.BuildQueries() +} + +func (request *CommonRequest) BuildQueries() string { + return request.Ontology.BuildQueries() +} + +func (request *CommonRequest) GetUrl() string { + if len(request.Port) > 0 { + return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.GetQueries() + } + + return strings.ToLower(request.Scheme) + "://" + request.Domain + request.GetQueries() +} + +func (request *CommonRequest) GetQueries() string { + return request.Ontology.GetQueries() +} + +func (request *CommonRequest) GetBodyReader() io.Reader { + return request.Ontology.GetBodyReader() +} + +func (request *CommonRequest) GetStyle() string { + return request.Ontology.GetStyle() +} + +func (request *CommonRequest) addPathParam(key, value string) { + request.PathParams[key] = value +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/roa_request.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/roa_request.go new file mode 100644 index 000000000..cd1ab178f --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/roa_request.go @@ -0,0 +1,146 @@ +/* + * 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 requests + +import ( + "bytes" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils" + "io" + "net/url" + "sort" + "strings" +) + +type RoaRequest struct { + *baseRequest + pathPattern string + PathParams map[string]string +} + +func (*RoaRequest) GetStyle() string { + return ROA +} + +func (request *RoaRequest) GetBodyReader() io.Reader { + if request.FormParams != nil && len(request.FormParams) > 0 { + formString := utils.GetUrlFormedMap(request.FormParams) + return strings.NewReader(formString) + } else if len(request.Content) > 0 { + return bytes.NewReader(request.Content) + } else { + return nil + } +} + +func (request *RoaRequest) GetQueries() string { + return request.queries +} + +// for sign method, need not url encoded +func (request *RoaRequest) BuildQueries() string { + return request.buildQueries(false) +} + +func (request *RoaRequest) buildQueries(needParamEncode bool) string { + // replace path params with value + path := request.pathPattern + for key, value := range request.PathParams { + path = strings.Replace(path, "["+key+"]", value, 1) + } + + queryParams := request.QueryParams + // check if path contains params + splitArray := strings.Split(path, "?") + path = splitArray[0] + if len(splitArray) > 1 && len(splitArray[1]) > 0 { + queryParams[splitArray[1]] = "" + } + // sort QueryParams by key + var queryKeys []string + for key := range queryParams { + queryKeys = append(queryKeys, key) + } + sort.Strings(queryKeys) + + // append urlBuilder + urlBuilder := bytes.Buffer{} + urlBuilder.WriteString(path) + if len(queryKeys) > 0 { + urlBuilder.WriteString("?") + } + for i := 0; i < len(queryKeys); i++ { + queryKey := queryKeys[i] + urlBuilder.WriteString(queryKey) + if value := queryParams[queryKey]; len(value) > 0 { + urlBuilder.WriteString("=") + if needParamEncode { + urlBuilder.WriteString(url.QueryEscape(value)) + } else { + urlBuilder.WriteString(value) + } + } + if i < len(queryKeys)-1 { + urlBuilder.WriteString("&") + } + } + result := urlBuilder.String() + result = popStandardUrlencode(result) + request.queries = result + return request.queries +} + +func popStandardUrlencode(stringToSign string) (result string) { + result = strings.Replace(stringToSign, "+", "%20", -1) + result = strings.Replace(result, "*", "%2A", -1) + result = strings.Replace(result, "%7E", "~", -1) + return +} + +func (request *RoaRequest) GetUrl() string { + return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.GetQueries() +} + +func (request *RoaRequest) BuildUrl() string { + // for network trans, need url encoded + return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.buildQueries(true) +} + +func (request *RoaRequest) addPathParam(key, value string) { + request.PathParams[key] = value +} + +func (request *RoaRequest) InitWithApiInfo(product, version, action, uriPattern, serviceCode, endpointType string) { + request.baseRequest = defaultBaseRequest() + request.PathParams = make(map[string]string) + request.Headers["x-acs-version"] = version + request.pathPattern = uriPattern + request.locationServiceCode = serviceCode + request.locationEndpointType = endpointType + //request.product = product + //request.version = version + //request.actionName = action +} + +func (request *RoaRequest) initWithCommonRequest(commonRequest *CommonRequest) { + request.baseRequest = commonRequest.baseRequest + request.PathParams = commonRequest.PathParams + //request.product = commonRequest.Product + //request.version = commonRequest.Version + request.Headers["x-acs-version"] = commonRequest.Version + //request.actionName = commonRequest.ApiName + request.pathPattern = commonRequest.PathPattern + request.locationServiceCode = "" + request.locationEndpointType = "" +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/rpc_request.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/rpc_request.go new file mode 100644 index 000000000..7a61c19f4 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/rpc_request.go @@ -0,0 +1,81 @@ +/* + * 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 requests + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils" + "io" + "strings" +) + +type RpcRequest struct { + *baseRequest +} + +func (request *RpcRequest) init() { + request.baseRequest = defaultBaseRequest() + request.Method = POST +} + +func (*RpcRequest) GetStyle() string { + return RPC +} + +func (request *RpcRequest) GetBodyReader() io.Reader { + if request.FormParams != nil && len(request.FormParams) > 0 { + formString := utils.GetUrlFormedMap(request.FormParams) + return strings.NewReader(formString) + } else { + return strings.NewReader("") + } +} + +func (request *RpcRequest) BuildQueries() string { + request.queries = "/?" + utils.GetUrlFormedMap(request.QueryParams) + return request.queries +} + +func (request *RpcRequest) GetQueries() string { + return request.queries +} + +func (request *RpcRequest) BuildUrl() string { + return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.BuildQueries() +} + +func (request *RpcRequest) GetUrl() string { + return strings.ToLower(request.Scheme) + "://" + request.Domain + request.GetQueries() +} + +func (request *RpcRequest) GetVersion() string { + return request.version +} + +func (request *RpcRequest) GetActionName() string { + return request.actionName +} + +func (request *RpcRequest) addPathParam(key, value string) { + panic("not support") +} + +func (request *RpcRequest) InitWithApiInfo(product, version, action, serviceCode, endpointType string) { + request.init() + request.product = product + request.version = version + request.actionName = action + request.locationServiceCode = serviceCode + request.locationEndpointType = endpointType +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/types.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/types.go new file mode 100644 index 000000000..28af63ea1 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests/types.go @@ -0,0 +1,53 @@ +package requests + +import "strconv" + +type Integer string + +func NewInteger(integer int) Integer { + return Integer(strconv.Itoa(integer)) +} + +func (integer Integer) HasValue() bool { + return integer != "" +} + +func (integer Integer) GetValue() (int, error) { + return strconv.Atoi(string(integer)) +} + +func NewInteger64(integer int64) Integer { + return Integer(strconv.FormatInt(integer, 10)) +} + +func (integer Integer) GetValue64() (int64, error) { + return strconv.ParseInt(string(integer), 10, 0) +} + +type Boolean string + +func NewBoolean(bool bool) Boolean { + return Boolean(strconv.FormatBool(bool)) +} + +func (boolean Boolean) HasValue() bool { + return boolean != "" +} + +func (boolean Boolean) GetValue() (bool, error) { + return strconv.ParseBool(string(boolean)) +} + +type Float string + +func NewFloat(f float64) Float { + return Float(strconv.FormatFloat(f, 'f', 6, 64)) +} + +func (float Float) HasValue() bool { + return float != "" +} + +func (float Float) GetValue() (float64, error) { + return strconv.ParseFloat(string(float), 64) +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go new file mode 100644 index 000000000..43ba6fc26 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/json_parser.go @@ -0,0 +1,341 @@ +package responses + +import ( + "encoding/json" + "github.com/json-iterator/go" + "io" + "math" + "strconv" + "strings" + "sync" + "unsafe" +) + +const maxUint = ^uint(0) +const maxInt = int(maxUint >> 1) +const minInt = -maxInt - 1 + +var jsonParser jsoniter.API +var initJson = &sync.Once{} + +func initJsonParserOnce() { + initJson.Do(func() { + registerBetterFuzzyDecoder() + jsonParser = jsoniter.ConfigCompatibleWithStandardLibrary + }) +} + +func registerBetterFuzzyDecoder() { + jsoniter.RegisterTypeDecoder("string", &nullableFuzzyStringDecoder{}) + jsoniter.RegisterTypeDecoder("bool", &fuzzyBoolDecoder{}) + jsoniter.RegisterTypeDecoder("float32", &nullableFuzzyFloat32Decoder{}) + jsoniter.RegisterTypeDecoder("float64", &nullableFuzzyFloat64Decoder{}) + jsoniter.RegisterTypeDecoder("int", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(maxInt) || val < float64(minInt) { + iter.ReportError("fuzzy decode int", "exceed range") + return + } + *((*int)(ptr)) = int(val) + } else { + *((*int)(ptr)) = iter.ReadInt() + } + }}) + jsoniter.RegisterTypeDecoder("uint", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(maxUint) || val < 0 { + iter.ReportError("fuzzy decode uint", "exceed range") + return + } + *((*uint)(ptr)) = uint(val) + } else { + *((*uint)(ptr)) = iter.ReadUint() + } + }}) + jsoniter.RegisterTypeDecoder("int8", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt8) || val < float64(math.MinInt8) { + iter.ReportError("fuzzy decode int8", "exceed range") + return + } + *((*int8)(ptr)) = int8(val) + } else { + *((*int8)(ptr)) = iter.ReadInt8() + } + }}) + jsoniter.RegisterTypeDecoder("uint8", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint8) || val < 0 { + iter.ReportError("fuzzy decode uint8", "exceed range") + return + } + *((*uint8)(ptr)) = uint8(val) + } else { + *((*uint8)(ptr)) = iter.ReadUint8() + } + }}) + jsoniter.RegisterTypeDecoder("int16", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt16) || val < float64(math.MinInt16) { + iter.ReportError("fuzzy decode int16", "exceed range") + return + } + *((*int16)(ptr)) = int16(val) + } else { + *((*int16)(ptr)) = iter.ReadInt16() + } + }}) + jsoniter.RegisterTypeDecoder("uint16", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint16) || val < 0 { + iter.ReportError("fuzzy decode uint16", "exceed range") + return + } + *((*uint16)(ptr)) = uint16(val) + } else { + *((*uint16)(ptr)) = iter.ReadUint16() + } + }}) + jsoniter.RegisterTypeDecoder("int32", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt32) || val < float64(math.MinInt32) { + iter.ReportError("fuzzy decode int32", "exceed range") + return + } + *((*int32)(ptr)) = int32(val) + } else { + *((*int32)(ptr)) = iter.ReadInt32() + } + }}) + jsoniter.RegisterTypeDecoder("uint32", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint32) || val < 0 { + iter.ReportError("fuzzy decode uint32", "exceed range") + return + } + *((*uint32)(ptr)) = uint32(val) + } else { + *((*uint32)(ptr)) = iter.ReadUint32() + } + }}) + jsoniter.RegisterTypeDecoder("int64", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxInt64) || val < float64(math.MinInt64) { + iter.ReportError("fuzzy decode int64", "exceed range") + return + } + *((*int64)(ptr)) = int64(val) + } else { + *((*int64)(ptr)) = iter.ReadInt64() + } + }}) + jsoniter.RegisterTypeDecoder("uint64", &nullableFuzzyIntegerDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if isFloat { + val := iter.ReadFloat64() + if val > float64(math.MaxUint64) || val < 0 { + iter.ReportError("fuzzy decode uint64", "exceed range") + return + } + *((*uint64)(ptr)) = uint64(val) + } else { + *((*uint64)(ptr)) = iter.ReadUint64() + } + }}) +} + +type nullableFuzzyStringDecoder struct { +} + +func (decoder *nullableFuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + switch valueType { + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + *((*string)(ptr)) = string(number) + case jsoniter.StringValue: + *((*string)(ptr)) = iter.ReadString() + case jsoniter.BoolValue: + *((*string)(ptr)) = strconv.FormatBool(iter.ReadBool()) + case jsoniter.NilValue: + iter.ReadNil() + *((*string)(ptr)) = "" + default: + iter.ReportError("fuzzyStringDecoder", "not number or string or bool") + } +} + +type fuzzyBoolDecoder struct { +} + +func (decoder *fuzzyBoolDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + switch valueType { + case jsoniter.BoolValue: + *((*bool)(ptr)) = iter.ReadBool() + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + num, err := number.Int64() + if err != nil { + iter.ReportError("fuzzyBoolDecoder", "get value from json.number failed") + } + if num == 0 { + *((*bool)(ptr)) = false + } else { + *((*bool)(ptr)) = true + } + case jsoniter.StringValue: + strValue := strings.ToLower(iter.ReadString()) + if strValue == "true" { + *((*bool)(ptr)) = true + } else if strValue == "false" || strValue == "" { + *((*bool)(ptr)) = false + } else { + iter.ReportError("fuzzyBoolDecoder", "unsupported bool value: "+strValue) + } + case jsoniter.NilValue: + iter.ReadNil() + *((*bool)(ptr)) = false + default: + iter.ReportError("fuzzyBoolDecoder", "not number or string or nil") + } +} + +type tolerateEmptyArrayDecoder struct { + valDecoder jsoniter.ValDecoder +} + +func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + if iter.WhatIsNext() == jsoniter.ArrayValue { + iter.Skip() + newIter := iter.Pool().BorrowIterator([]byte("{}")) + defer iter.Pool().ReturnIterator(newIter) + decoder.valDecoder.Decode(ptr, newIter) + } else { + decoder.valDecoder.Decode(ptr, iter) + } +} + +type nullableFuzzyIntegerDecoder struct { + fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator) +} + +func (decoder *nullableFuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + var number json.Number + iter.ReadVal(&number) + str = string(number) + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == "" { + str = "0" + } + case jsoniter.BoolValue: + if iter.ReadBool() { + str = "1" + } else { + str = "0" + } + case jsoniter.NilValue: + iter.ReadNil() + str = "0" + default: + iter.ReportError("fuzzyIntegerDecoder", "not number or string") + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + isFloat := strings.IndexByte(str, '.') != -1 + decoder.fun(isFloat, ptr, newIter) + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } +} + +type nullableFuzzyFloat32Decoder struct { +} + +func (decoder *nullableFuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + *((*float32)(ptr)) = iter.ReadFloat32() + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == "" { + *((*float32)(ptr)) = 0 + return + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + *((*float32)(ptr)) = newIter.ReadFloat32() + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } + case jsoniter.BoolValue: + // support bool to float32 + if iter.ReadBool() { + *((*float32)(ptr)) = 1 + } else { + *((*float32)(ptr)) = 0 + } + case jsoniter.NilValue: + iter.ReadNil() + *((*float32)(ptr)) = 0 + default: + iter.ReportError("nullableFuzzyFloat32Decoder", "not number or string") + } +} + +type nullableFuzzyFloat64Decoder struct { +} + +func (decoder *nullableFuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + valueType := iter.WhatIsNext() + var str string + switch valueType { + case jsoniter.NumberValue: + *((*float64)(ptr)) = iter.ReadFloat64() + case jsoniter.StringValue: + str = iter.ReadString() + // support empty string + if str == "" { + *((*float64)(ptr)) = 0 + return + } + newIter := iter.Pool().BorrowIterator([]byte(str)) + defer iter.Pool().ReturnIterator(newIter) + *((*float64)(ptr)) = newIter.ReadFloat64() + if newIter.Error != nil && newIter.Error != io.EOF { + iter.Error = newIter.Error + } + case jsoniter.BoolValue: + // support bool to float64 + if iter.ReadBool() { + *((*float64)(ptr)) = 1 + } else { + *((*float64)(ptr)) = 0 + } + case jsoniter.NilValue: + // support empty string + iter.ReadNil() + *((*float64)(ptr)) = 0 + default: + iter.ReportError("nullableFuzzyFloat32Decoder", "not number or string") + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go new file mode 100644 index 000000000..8780f26f2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses/response.go @@ -0,0 +1,142 @@ +/* + * 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 responses + +import ( + "bytes" + "encoding/xml" + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" + "io/ioutil" + "net/http" + "strings" +) + +type AcsResponse interface { + IsSuccess() bool + GetHttpStatus() int + GetHttpHeaders() map[string][]string + GetHttpContentString() string + GetHttpContentBytes() []byte + GetOriginHttpResponse() *http.Response + parseFromHttpResponse(httpResponse *http.Response) error +} + +func Unmarshal(response AcsResponse, httpResponse *http.Response, format string) (err error) { + err = response.parseFromHttpResponse(httpResponse) + if err != nil { + return + } + if !response.IsSuccess() { + err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), "") + return + } + if _, isCommonResponse := response.(CommonResponse); isCommonResponse { + // common response need not unmarshal + return + } + + if len(response.GetHttpContentBytes()) == 0 { + return + } + + if strings.ToUpper(format) == "JSON" { + initJsonParserOnce() + err = jsonParser.Unmarshal(response.GetHttpContentBytes(), response) + if err != nil { + err = errors.NewClientError(errors.JsonUnmarshalErrorCode, errors.JsonUnmarshalErrorMessage, err) + } + } else if strings.ToUpper(format) == "XML" { + err = xml.Unmarshal(response.GetHttpContentBytes(), response) + } + return +} + +type BaseResponse struct { + httpStatus int + httpHeaders map[string][]string + httpContentString string + httpContentBytes []byte + originHttpResponse *http.Response +} + +func (baseResponse *BaseResponse) GetHttpStatus() int { + return baseResponse.httpStatus +} + +func (baseResponse *BaseResponse) GetHttpHeaders() map[string][]string { + return baseResponse.httpHeaders +} + +func (baseResponse *BaseResponse) GetHttpContentString() string { + return baseResponse.httpContentString +} + +func (baseResponse *BaseResponse) GetHttpContentBytes() []byte { + return baseResponse.httpContentBytes +} + +func (baseResponse *BaseResponse) GetOriginHttpResponse() *http.Response { + return baseResponse.originHttpResponse +} + +func (baseResponse *BaseResponse) IsSuccess() bool { + if baseResponse.GetHttpStatus() >= 200 && baseResponse.GetHttpStatus() < 300 { + return true + } + + return false +} + +func (baseResponse *BaseResponse) parseFromHttpResponse(httpResponse *http.Response) (err error) { + defer httpResponse.Body.Close() + body, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + return + } + baseResponse.httpStatus = httpResponse.StatusCode + baseResponse.httpHeaders = httpResponse.Header + baseResponse.httpContentBytes = body + baseResponse.httpContentString = string(body) + baseResponse.originHttpResponse = httpResponse + return +} + +func (baseResponse *BaseResponse) String() string { + resultBuilder := bytes.Buffer{} + // statusCode + resultBuilder.WriteString("\n") + resultBuilder.WriteString(fmt.Sprintf("%s %s\n", baseResponse.originHttpResponse.Proto, baseResponse.originHttpResponse.Status)) + // httpHeaders + //resultBuilder.WriteString("Headers:\n") + for key, value := range baseResponse.httpHeaders { + resultBuilder.WriteString(key + ": " + strings.Join(value, ";") + "\n") + } + resultBuilder.WriteString("\n") + // content + //resultBuilder.WriteString("Content:\n") + resultBuilder.WriteString(baseResponse.httpContentString + "\n") + return resultBuilder.String() +} + +type CommonResponse struct { + *BaseResponse +} + +func NewCommonResponse() (response *CommonResponse) { + return &CommonResponse{ + BaseResponse: &BaseResponse{}, + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils/utils.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils/utils.go new file mode 100644 index 000000000..a00c775c2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/sdk/utils/utils.go @@ -0,0 +1,117 @@ +/* + * 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 utils + +import ( + "crypto/md5" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/satori/go.uuid" + "net/url" + "reflect" + "strconv" + "time" +) + +// if you use go 1.10 or higher, you can hack this util by these to avoid "TimeZone.zip not found" on Windows +var LoadLocationFromTZData func(name string, data []byte) (*time.Location, error) = nil +var TZData []byte = nil + +func GetUUIDV4() (uuidHex string) { + uuidV4 := uuid.NewV4() + uuidHex = hex.EncodeToString(uuidV4.Bytes()) + return +} + +func GetMD5Base64(bytes []byte) (base64Value string) { + md5Ctx := md5.New() + md5Ctx.Write(bytes) + md5Value := md5Ctx.Sum(nil) + base64Value = base64.StdEncoding.EncodeToString(md5Value) + return +} + +func GetGMTLocation() (*time.Location, error) { + if LoadLocationFromTZData != nil && TZData != nil { + return LoadLocationFromTZData("GMT", TZData) + } else { + return time.LoadLocation("GMT") + } +} + +func GetTimeInFormatISO8601() (timeStr string) { + gmt, err := GetGMTLocation() + + if err != nil { + panic(err) + } + return time.Now().In(gmt).Format("2006-01-02T15:04:05Z") +} + +func GetTimeInFormatRFC2616() (timeStr string) { + gmt, err := GetGMTLocation() + + if err != nil { + panic(err) + } + return time.Now().In(gmt).Format("Mon, 02 Jan 2006 15:04:05 GMT") +} + +func GetUrlFormedMap(source map[string]string) (urlEncoded string) { + urlEncoder := url.Values{} + for key, value := range source { + urlEncoder.Add(key, value) + } + urlEncoded = urlEncoder.Encode() + return +} + +func GetFromJsonString(jsonString, key string) (result string, err error) { + var responseMap map[string]*json.RawMessage + err = json.Unmarshal([]byte(jsonString), &responseMap) + if err != nil { + return + } + fmt.Println(string(*responseMap[key])) + err = json.Unmarshal(*responseMap[key], &result) + return +} + +func InitStructWithDefaultTag(bean interface{}) { + configType := reflect.TypeOf(bean) + for i := 0; i < configType.Elem().NumField(); i++ { + field := configType.Elem().Field(i) + defaultValue := field.Tag.Get("default") + if defaultValue == "" { + continue + } + setter := reflect.ValueOf(bean).Elem().Field(i) + switch field.Type.String() { + case "int": + intValue, _ := strconv.ParseInt(defaultValue, 10, 64) + setter.SetInt(intValue) + case "time.Duration": + intValue, _ := strconv.ParseInt(defaultValue, 10, 64) + setter.SetInt(intValue) + case "string": + setter.SetString(defaultValue) + case "bool": + boolValue, _ := strconv.ParseBool(defaultValue) + setter.SetBool(boolValue) + } + } +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_batch_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_batch_domain_records.go new file mode 100644 index 000000000..30e1dea47 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_batch_domain_records.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// AddBatchDomainRecords invokes the alidns.AddBatchDomainRecords API synchronously +// api document: https://help.aliyun.com/api/alidns/addbatchdomainrecords.html +func (client *Client) AddBatchDomainRecords(request *AddBatchDomainRecordsRequest) (response *AddBatchDomainRecordsResponse, err error) { + response = CreateAddBatchDomainRecordsResponse() + err = client.DoAction(request, response) + return +} + +// AddBatchDomainRecordsWithChan invokes the alidns.AddBatchDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/addbatchdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddBatchDomainRecordsWithChan(request *AddBatchDomainRecordsRequest) (<-chan *AddBatchDomainRecordsResponse, <-chan error) { + responseChan := make(chan *AddBatchDomainRecordsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.AddBatchDomainRecords(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// AddBatchDomainRecordsWithCallback invokes the alidns.AddBatchDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/addbatchdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddBatchDomainRecordsWithCallback(request *AddBatchDomainRecordsRequest, callback func(response *AddBatchDomainRecordsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *AddBatchDomainRecordsResponse + var err error + defer close(result) + response, err = client.AddBatchDomainRecords(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// AddBatchDomainRecordsRequest is the request struct for api AddBatchDomainRecords +type AddBatchDomainRecordsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + Records string `position:"Query" name:"Records"` +} + +// AddBatchDomainRecordsResponse is the response struct for api AddBatchDomainRecords +type AddBatchDomainRecordsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TraceId string `json:"TraceId" xml:"TraceId"` +} + +// CreateAddBatchDomainRecordsRequest creates a request to invoke AddBatchDomainRecords API +func CreateAddBatchDomainRecordsRequest() (request *AddBatchDomainRecordsRequest) { + request = &AddBatchDomainRecordsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "AddBatchDomainRecords", "", "") + return +} + +// CreateAddBatchDomainRecordsResponse creates a response to parse from AddBatchDomainRecords response +func CreateAddBatchDomainRecordsResponse() (response *AddBatchDomainRecordsResponse) { + response = &AddBatchDomainRecordsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain.go new file mode 100644 index 000000000..1b2249e29 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain.go @@ -0,0 +1,112 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// AddDomain invokes the alidns.AddDomain API synchronously +// api document: https://help.aliyun.com/api/alidns/adddomain.html +func (client *Client) AddDomain(request *AddDomainRequest) (response *AddDomainResponse, err error) { + response = CreateAddDomainResponse() + err = client.DoAction(request, response) + return +} + +// AddDomainWithChan invokes the alidns.AddDomain API asynchronously +// api document: https://help.aliyun.com/api/alidns/adddomain.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddDomainWithChan(request *AddDomainRequest) (<-chan *AddDomainResponse, <-chan error) { + responseChan := make(chan *AddDomainResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.AddDomain(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// AddDomainWithCallback invokes the alidns.AddDomain API asynchronously +// api document: https://help.aliyun.com/api/alidns/adddomain.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddDomainWithCallback(request *AddDomainRequest, callback func(response *AddDomainResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *AddDomainResponse + var err error + defer close(result) + response, err = client.AddDomain(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// AddDomainRequest is the request struct for api AddDomain +type AddDomainRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + GroupId string `position:"Query" name:"GroupId"` +} + +// AddDomainResponse is the response struct for api AddDomain +type AddDomainResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainId string `json:"DomainId" xml:"DomainId"` + DomainName string `json:"DomainName" xml:"DomainName"` + PunyCode string `json:"PunyCode" xml:"PunyCode"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` + DnsServers DnsServersInAddDomain `json:"DnsServers" xml:"DnsServers"` +} + +// CreateAddDomainRequest creates a request to invoke AddDomain API +func CreateAddDomainRequest() (request *AddDomainRequest) { + request = &AddDomainRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "AddDomain", "", "") + return +} + +// CreateAddDomainResponse creates a response to parse from AddDomain response +func CreateAddDomainResponse() (response *AddDomainResponse) { + response = &AddDomainResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_group.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_group.go new file mode 100644 index 000000000..a1c7cd4c0 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_group.go @@ -0,0 +1,107 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// AddDomainGroup invokes the alidns.AddDomainGroup API synchronously +// api document: https://help.aliyun.com/api/alidns/adddomaingroup.html +func (client *Client) AddDomainGroup(request *AddDomainGroupRequest) (response *AddDomainGroupResponse, err error) { + response = CreateAddDomainGroupResponse() + err = client.DoAction(request, response) + return +} + +// AddDomainGroupWithChan invokes the alidns.AddDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/adddomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddDomainGroupWithChan(request *AddDomainGroupRequest) (<-chan *AddDomainGroupResponse, <-chan error) { + responseChan := make(chan *AddDomainGroupResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.AddDomainGroup(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// AddDomainGroupWithCallback invokes the alidns.AddDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/adddomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddDomainGroupWithCallback(request *AddDomainGroupRequest, callback func(response *AddDomainGroupResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *AddDomainGroupResponse + var err error + defer close(result) + response, err = client.AddDomainGroup(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// AddDomainGroupRequest is the request struct for api AddDomainGroup +type AddDomainGroupRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + GroupName string `position:"Query" name:"GroupName"` +} + +// AddDomainGroupResponse is the response struct for api AddDomainGroup +type AddDomainGroupResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` +} + +// CreateAddDomainGroupRequest creates a request to invoke AddDomainGroup API +func CreateAddDomainGroupRequest() (request *AddDomainGroupRequest) { + request = &AddDomainGroupRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "AddDomainGroup", "", "") + return +} + +// CreateAddDomainGroupResponse creates a response to parse from AddDomainGroup response +func CreateAddDomainGroupResponse() (response *AddDomainGroupResponse) { + response = &AddDomainGroupResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_record.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_record.go new file mode 100644 index 000000000..1da602453 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/add_domain_record.go @@ -0,0 +1,112 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// AddDomainRecord invokes the alidns.AddDomainRecord API synchronously +// api document: https://help.aliyun.com/api/alidns/adddomainrecord.html +func (client *Client) AddDomainRecord(request *AddDomainRecordRequest) (response *AddDomainRecordResponse, err error) { + response = CreateAddDomainRecordResponse() + err = client.DoAction(request, response) + return +} + +// AddDomainRecordWithChan invokes the alidns.AddDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/adddomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddDomainRecordWithChan(request *AddDomainRecordRequest) (<-chan *AddDomainRecordResponse, <-chan error) { + responseChan := make(chan *AddDomainRecordResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.AddDomainRecord(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// AddDomainRecordWithCallback invokes the alidns.AddDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/adddomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) AddDomainRecordWithCallback(request *AddDomainRecordRequest, callback func(response *AddDomainRecordResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *AddDomainRecordResponse + var err error + defer close(result) + response, err = client.AddDomainRecord(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// AddDomainRecordRequest is the request struct for api AddDomainRecord +type AddDomainRecordRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + RR string `position:"Query" name:"RR"` + Type string `position:"Query" name:"Type"` + Value string `position:"Query" name:"Value"` + TTL requests.Integer `position:"Query" name:"TTL"` + Priority requests.Integer `position:"Query" name:"Priority"` + Line string `position:"Query" name:"Line"` +} + +// AddDomainRecordResponse is the response struct for api AddDomainRecord +type AddDomainRecordResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordId string `json:"RecordId" xml:"RecordId"` +} + +// CreateAddDomainRecordRequest creates a request to invoke AddDomainRecord API +func CreateAddDomainRecordRequest() (request *AddDomainRecordRequest) { + request = &AddDomainRecordRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "AddDomainRecord", "", "") + return +} + +// CreateAddDomainRecordResponse creates a response to parse from AddDomainRecord response +func CreateAddDomainRecordResponse() (response *AddDomainRecordResponse) { + response = &AddDomainRecordResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/apply_for_retrieval_domain_name.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/apply_for_retrieval_domain_name.go new file mode 100644 index 000000000..86b68e7b9 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/apply_for_retrieval_domain_name.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// ApplyForRetrievalDomainName invokes the alidns.ApplyForRetrievalDomainName API synchronously +// api document: https://help.aliyun.com/api/alidns/applyforretrievaldomainname.html +func (client *Client) ApplyForRetrievalDomainName(request *ApplyForRetrievalDomainNameRequest) (response *ApplyForRetrievalDomainNameResponse, err error) { + response = CreateApplyForRetrievalDomainNameResponse() + err = client.DoAction(request, response) + return +} + +// ApplyForRetrievalDomainNameWithChan invokes the alidns.ApplyForRetrievalDomainName API asynchronously +// api document: https://help.aliyun.com/api/alidns/applyforretrievaldomainname.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ApplyForRetrievalDomainNameWithChan(request *ApplyForRetrievalDomainNameRequest) (<-chan *ApplyForRetrievalDomainNameResponse, <-chan error) { + responseChan := make(chan *ApplyForRetrievalDomainNameResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.ApplyForRetrievalDomainName(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// ApplyForRetrievalDomainNameWithCallback invokes the alidns.ApplyForRetrievalDomainName API asynchronously +// api document: https://help.aliyun.com/api/alidns/applyforretrievaldomainname.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ApplyForRetrievalDomainNameWithCallback(request *ApplyForRetrievalDomainNameRequest, callback func(response *ApplyForRetrievalDomainNameResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *ApplyForRetrievalDomainNameResponse + var err error + defer close(result) + response, err = client.ApplyForRetrievalDomainName(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// ApplyForRetrievalDomainNameRequest is the request struct for api ApplyForRetrievalDomainName +type ApplyForRetrievalDomainNameRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` +} + +// ApplyForRetrievalDomainNameResponse is the response struct for api ApplyForRetrievalDomainName +type ApplyForRetrievalDomainNameResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainName string `json:"DomainName" xml:"DomainName"` +} + +// CreateApplyForRetrievalDomainNameRequest creates a request to invoke ApplyForRetrievalDomainName API +func CreateApplyForRetrievalDomainNameRequest() (request *ApplyForRetrievalDomainNameRequest) { + request = &ApplyForRetrievalDomainNameRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "ApplyForRetrievalDomainName", "", "") + return +} + +// CreateApplyForRetrievalDomainNameResponse creates a response to parse from ApplyForRetrievalDomainName response +func CreateApplyForRetrievalDomainNameResponse() (response *ApplyForRetrievalDomainNameResponse) { + response = &ApplyForRetrievalDomainNameResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_group.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_group.go new file mode 100644 index 000000000..61fed5be8 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_group.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// ChangeDomainGroup invokes the alidns.ChangeDomainGroup API synchronously +// api document: https://help.aliyun.com/api/alidns/changedomaingroup.html +func (client *Client) ChangeDomainGroup(request *ChangeDomainGroupRequest) (response *ChangeDomainGroupResponse, err error) { + response = CreateChangeDomainGroupResponse() + err = client.DoAction(request, response) + return +} + +// ChangeDomainGroupWithChan invokes the alidns.ChangeDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/changedomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ChangeDomainGroupWithChan(request *ChangeDomainGroupRequest) (<-chan *ChangeDomainGroupResponse, <-chan error) { + responseChan := make(chan *ChangeDomainGroupResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.ChangeDomainGroup(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// ChangeDomainGroupWithCallback invokes the alidns.ChangeDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/changedomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ChangeDomainGroupWithCallback(request *ChangeDomainGroupRequest, callback func(response *ChangeDomainGroupResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *ChangeDomainGroupResponse + var err error + defer close(result) + response, err = client.ChangeDomainGroup(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// ChangeDomainGroupRequest is the request struct for api ChangeDomainGroup +type ChangeDomainGroupRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + GroupId string `position:"Query" name:"GroupId"` +} + +// ChangeDomainGroupResponse is the response struct for api ChangeDomainGroup +type ChangeDomainGroupResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` +} + +// CreateChangeDomainGroupRequest creates a request to invoke ChangeDomainGroup API +func CreateChangeDomainGroupRequest() (request *ChangeDomainGroupRequest) { + request = &ChangeDomainGroupRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "ChangeDomainGroup", "", "") + return +} + +// CreateChangeDomainGroupResponse creates a response to parse from ChangeDomainGroup response +func CreateChangeDomainGroupResponse() (response *ChangeDomainGroupResponse) { + response = &ChangeDomainGroupResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_of_dns_product.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_of_dns_product.go new file mode 100644 index 000000000..04f329e49 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/change_domain_of_dns_product.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// ChangeDomainOfDnsProduct invokes the alidns.ChangeDomainOfDnsProduct API synchronously +// api document: https://help.aliyun.com/api/alidns/changedomainofdnsproduct.html +func (client *Client) ChangeDomainOfDnsProduct(request *ChangeDomainOfDnsProductRequest) (response *ChangeDomainOfDnsProductResponse, err error) { + response = CreateChangeDomainOfDnsProductResponse() + err = client.DoAction(request, response) + return +} + +// ChangeDomainOfDnsProductWithChan invokes the alidns.ChangeDomainOfDnsProduct API asynchronously +// api document: https://help.aliyun.com/api/alidns/changedomainofdnsproduct.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ChangeDomainOfDnsProductWithChan(request *ChangeDomainOfDnsProductRequest) (<-chan *ChangeDomainOfDnsProductResponse, <-chan error) { + responseChan := make(chan *ChangeDomainOfDnsProductResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.ChangeDomainOfDnsProduct(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// ChangeDomainOfDnsProductWithCallback invokes the alidns.ChangeDomainOfDnsProduct API asynchronously +// api document: https://help.aliyun.com/api/alidns/changedomainofdnsproduct.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ChangeDomainOfDnsProductWithCallback(request *ChangeDomainOfDnsProductRequest, callback func(response *ChangeDomainOfDnsProductResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *ChangeDomainOfDnsProductResponse + var err error + defer close(result) + response, err = client.ChangeDomainOfDnsProduct(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// ChangeDomainOfDnsProductRequest is the request struct for api ChangeDomainOfDnsProduct +type ChangeDomainOfDnsProductRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + InstanceId string `position:"Query" name:"InstanceId"` + NewDomain string `position:"Query" name:"NewDomain"` + Force requests.Boolean `position:"Query" name:"Force"` +} + +// ChangeDomainOfDnsProductResponse is the response struct for api ChangeDomainOfDnsProduct +type ChangeDomainOfDnsProductResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + OriginalDomain string `json:"OriginalDomain" xml:"OriginalDomain"` +} + +// CreateChangeDomainOfDnsProductRequest creates a request to invoke ChangeDomainOfDnsProduct API +func CreateChangeDomainOfDnsProductRequest() (request *ChangeDomainOfDnsProductRequest) { + request = &ChangeDomainOfDnsProductRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "ChangeDomainOfDnsProduct", "", "") + return +} + +// CreateChangeDomainOfDnsProductResponse creates a response to parse from ChangeDomainOfDnsProduct response +func CreateChangeDomainOfDnsProductResponse() (response *ChangeDomainOfDnsProductResponse) { + response = &ChangeDomainOfDnsProductResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/check_domain_record.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/check_domain_record.go new file mode 100644 index 000000000..876e4deb7 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/check_domain_record.go @@ -0,0 +1,109 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// CheckDomainRecord invokes the alidns.CheckDomainRecord API synchronously +// api document: https://help.aliyun.com/api/alidns/checkdomainrecord.html +func (client *Client) CheckDomainRecord(request *CheckDomainRecordRequest) (response *CheckDomainRecordResponse, err error) { + response = CreateCheckDomainRecordResponse() + err = client.DoAction(request, response) + return +} + +// CheckDomainRecordWithChan invokes the alidns.CheckDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/checkdomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) CheckDomainRecordWithChan(request *CheckDomainRecordRequest) (<-chan *CheckDomainRecordResponse, <-chan error) { + responseChan := make(chan *CheckDomainRecordResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.CheckDomainRecord(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// CheckDomainRecordWithCallback invokes the alidns.CheckDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/checkdomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) CheckDomainRecordWithCallback(request *CheckDomainRecordRequest, callback func(response *CheckDomainRecordResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *CheckDomainRecordResponse + var err error + defer close(result) + response, err = client.CheckDomainRecord(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// CheckDomainRecordRequest is the request struct for api CheckDomainRecord +type CheckDomainRecordRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + RR string `position:"Query" name:"RR"` + Type string `position:"Query" name:"Type"` + Value string `position:"Query" name:"Value"` +} + +// CheckDomainRecordResponse is the response struct for api CheckDomainRecord +type CheckDomainRecordResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + IsExist bool `json:"IsExist" xml:"IsExist"` +} + +// CreateCheckDomainRecordRequest creates a request to invoke CheckDomainRecord API +func CreateCheckDomainRecordRequest() (request *CheckDomainRecordRequest) { + request = &CheckDomainRecordRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "CheckDomainRecord", "", "") + return +} + +// CreateCheckDomainRecordResponse creates a response to parse from CheckDomainRecord response +func CreateCheckDomainRecordResponse() (response *CheckDomainRecordResponse) { + response = &CheckDomainRecordResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/client.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/client.go new file mode 100644 index 000000000..bd92fdd31 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/client.go @@ -0,0 +1,81 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth" +) + +// Client is the sdk client struct, each func corresponds to an OpenAPI +type Client struct { + sdk.Client +} + +// NewClient creates a sdk client with environment variables +func NewClient() (client *Client, err error) { + client = &Client{} + err = client.Init() + return +} + +// NewClientWithOptions creates a sdk client with regionId/sdkConfig/credential +// this is the common api to create a sdk client +func NewClientWithOptions(regionId string, config *sdk.Config, credential auth.Credential) (client *Client, err error) { + client = &Client{} + err = client.InitWithOptions(regionId, config, credential) + return +} + +// NewClientWithAccessKey is a shortcut to create sdk client with accesskey +// usage: https://help.aliyun.com/document_detail/66217.html +func NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret string) (client *Client, err error) { + client = &Client{} + err = client.InitWithAccessKey(regionId, accessKeyId, accessKeySecret) + return +} + +// NewClientWithStsToken is a shortcut to create sdk client with sts token +// usage: https://help.aliyun.com/document_detail/66222.html +func NewClientWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken string) (client *Client, err error) { + client = &Client{} + err = client.InitWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken) + return +} + +// NewClientWithRamRoleArn is a shortcut to create sdk client with ram roleArn +// usage: https://help.aliyun.com/document_detail/66222.html +func NewClientWithRamRoleArn(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (client *Client, err error) { + client = &Client{} + err = client.InitWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName) + return +} + +// NewClientWithEcsRamRole is a shortcut to create sdk client with ecs ram role +// usage: https://help.aliyun.com/document_detail/66223.html +func NewClientWithEcsRamRole(regionId string, roleName string) (client *Client, err error) { + client = &Client{} + err = client.InitWithEcsRamRole(regionId, roleName) + return +} + +// NewClientWithRsaKeyPair is a shortcut to create sdk client with rsa key pair +// attention: rsa key pair auth is only Japan regions available +func NewClientWithRsaKeyPair(regionId string, publicKeyId, privateKey string, sessionExpiration int) (client *Client, err error) { + client = &Client{} + err = client.InitWithRsaKeyPair(regionId, publicKeyId, privateKey, sessionExpiration) + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domain_records.go new file mode 100644 index 000000000..8c973f766 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domain_records.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DeleteBatchDomainRecords invokes the alidns.DeleteBatchDomainRecords API synchronously +// api document: https://help.aliyun.com/api/alidns/deletebatchdomainrecords.html +func (client *Client) DeleteBatchDomainRecords(request *DeleteBatchDomainRecordsRequest) (response *DeleteBatchDomainRecordsResponse, err error) { + response = CreateDeleteBatchDomainRecordsResponse() + err = client.DoAction(request, response) + return +} + +// DeleteBatchDomainRecordsWithChan invokes the alidns.DeleteBatchDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletebatchdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteBatchDomainRecordsWithChan(request *DeleteBatchDomainRecordsRequest) (<-chan *DeleteBatchDomainRecordsResponse, <-chan error) { + responseChan := make(chan *DeleteBatchDomainRecordsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DeleteBatchDomainRecords(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DeleteBatchDomainRecordsWithCallback invokes the alidns.DeleteBatchDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletebatchdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteBatchDomainRecordsWithCallback(request *DeleteBatchDomainRecordsRequest, callback func(response *DeleteBatchDomainRecordsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DeleteBatchDomainRecordsResponse + var err error + defer close(result) + response, err = client.DeleteBatchDomainRecords(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DeleteBatchDomainRecordsRequest is the request struct for api DeleteBatchDomainRecords +type DeleteBatchDomainRecordsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + Records string `position:"Query" name:"Records"` +} + +// DeleteBatchDomainRecordsResponse is the response struct for api DeleteBatchDomainRecords +type DeleteBatchDomainRecordsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TraceId string `json:"TraceId" xml:"TraceId"` +} + +// CreateDeleteBatchDomainRecordsRequest creates a request to invoke DeleteBatchDomainRecords API +func CreateDeleteBatchDomainRecordsRequest() (request *DeleteBatchDomainRecordsRequest) { + request = &DeleteBatchDomainRecordsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DeleteBatchDomainRecords", "", "") + return +} + +// CreateDeleteBatchDomainRecordsResponse creates a response to parse from DeleteBatchDomainRecords response +func CreateDeleteBatchDomainRecordsResponse() (response *DeleteBatchDomainRecordsResponse) { + response = &DeleteBatchDomainRecordsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domains.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domains.go new file mode 100644 index 000000000..fbbac4cec --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_batch_domains.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DeleteBatchDomains invokes the alidns.DeleteBatchDomains API synchronously +// api document: https://help.aliyun.com/api/alidns/deletebatchdomains.html +func (client *Client) DeleteBatchDomains(request *DeleteBatchDomainsRequest) (response *DeleteBatchDomainsResponse, err error) { + response = CreateDeleteBatchDomainsResponse() + err = client.DoAction(request, response) + return +} + +// DeleteBatchDomainsWithChan invokes the alidns.DeleteBatchDomains API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletebatchdomains.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteBatchDomainsWithChan(request *DeleteBatchDomainsRequest) (<-chan *DeleteBatchDomainsResponse, <-chan error) { + responseChan := make(chan *DeleteBatchDomainsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DeleteBatchDomains(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DeleteBatchDomainsWithCallback invokes the alidns.DeleteBatchDomains API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletebatchdomains.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteBatchDomainsWithCallback(request *DeleteBatchDomainsRequest, callback func(response *DeleteBatchDomainsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DeleteBatchDomainsResponse + var err error + defer close(result) + response, err = client.DeleteBatchDomains(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DeleteBatchDomainsRequest is the request struct for api DeleteBatchDomains +type DeleteBatchDomainsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + Domains string `position:"Query" name:"Domains"` +} + +// DeleteBatchDomainsResponse is the response struct for api DeleteBatchDomains +type DeleteBatchDomainsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TraceId string `json:"TraceId" xml:"TraceId"` +} + +// CreateDeleteBatchDomainsRequest creates a request to invoke DeleteBatchDomains API +func CreateDeleteBatchDomainsRequest() (request *DeleteBatchDomainsRequest) { + request = &DeleteBatchDomainsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DeleteBatchDomains", "", "") + return +} + +// CreateDeleteBatchDomainsResponse creates a response to parse from DeleteBatchDomains response +func CreateDeleteBatchDomainsResponse() (response *DeleteBatchDomainsResponse) { + response = &DeleteBatchDomainsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain.go new file mode 100644 index 000000000..097f37f9a --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DeleteDomain invokes the alidns.DeleteDomain API synchronously +// api document: https://help.aliyun.com/api/alidns/deletedomain.html +func (client *Client) DeleteDomain(request *DeleteDomainRequest) (response *DeleteDomainResponse, err error) { + response = CreateDeleteDomainResponse() + err = client.DoAction(request, response) + return +} + +// DeleteDomainWithChan invokes the alidns.DeleteDomain API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletedomain.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteDomainWithChan(request *DeleteDomainRequest) (<-chan *DeleteDomainResponse, <-chan error) { + responseChan := make(chan *DeleteDomainResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DeleteDomain(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DeleteDomainWithCallback invokes the alidns.DeleteDomain API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletedomain.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteDomainWithCallback(request *DeleteDomainRequest, callback func(response *DeleteDomainResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DeleteDomainResponse + var err error + defer close(result) + response, err = client.DeleteDomain(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DeleteDomainRequest is the request struct for api DeleteDomain +type DeleteDomainRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` +} + +// DeleteDomainResponse is the response struct for api DeleteDomain +type DeleteDomainResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainName string `json:"DomainName" xml:"DomainName"` +} + +// CreateDeleteDomainRequest creates a request to invoke DeleteDomain API +func CreateDeleteDomainRequest() (request *DeleteDomainRequest) { + request = &DeleteDomainRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DeleteDomain", "", "") + return +} + +// CreateDeleteDomainResponse creates a response to parse from DeleteDomain response +func CreateDeleteDomainResponse() (response *DeleteDomainResponse) { + response = &DeleteDomainResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_group.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_group.go new file mode 100644 index 000000000..341b91227 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_group.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DeleteDomainGroup invokes the alidns.DeleteDomainGroup API synchronously +// api document: https://help.aliyun.com/api/alidns/deletedomaingroup.html +func (client *Client) DeleteDomainGroup(request *DeleteDomainGroupRequest) (response *DeleteDomainGroupResponse, err error) { + response = CreateDeleteDomainGroupResponse() + err = client.DoAction(request, response) + return +} + +// DeleteDomainGroupWithChan invokes the alidns.DeleteDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletedomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteDomainGroupWithChan(request *DeleteDomainGroupRequest) (<-chan *DeleteDomainGroupResponse, <-chan error) { + responseChan := make(chan *DeleteDomainGroupResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DeleteDomainGroup(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DeleteDomainGroupWithCallback invokes the alidns.DeleteDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletedomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteDomainGroupWithCallback(request *DeleteDomainGroupRequest, callback func(response *DeleteDomainGroupResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DeleteDomainGroupResponse + var err error + defer close(result) + response, err = client.DeleteDomainGroup(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DeleteDomainGroupRequest is the request struct for api DeleteDomainGroup +type DeleteDomainGroupRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + GroupId string `position:"Query" name:"GroupId"` +} + +// DeleteDomainGroupResponse is the response struct for api DeleteDomainGroup +type DeleteDomainGroupResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + GroupName string `json:"GroupName" xml:"GroupName"` +} + +// CreateDeleteDomainGroupRequest creates a request to invoke DeleteDomainGroup API +func CreateDeleteDomainGroupRequest() (request *DeleteDomainGroupRequest) { + request = &DeleteDomainGroupRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DeleteDomainGroup", "", "") + return +} + +// CreateDeleteDomainGroupResponse creates a response to parse from DeleteDomainGroup response +func CreateDeleteDomainGroupResponse() (response *DeleteDomainGroupResponse) { + response = &DeleteDomainGroupResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_record.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_record.go new file mode 100644 index 000000000..73f2b08fc --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_domain_record.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DeleteDomainRecord invokes the alidns.DeleteDomainRecord API synchronously +// api document: https://help.aliyun.com/api/alidns/deletedomainrecord.html +func (client *Client) DeleteDomainRecord(request *DeleteDomainRecordRequest) (response *DeleteDomainRecordResponse, err error) { + response = CreateDeleteDomainRecordResponse() + err = client.DoAction(request, response) + return +} + +// DeleteDomainRecordWithChan invokes the alidns.DeleteDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletedomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteDomainRecordWithChan(request *DeleteDomainRecordRequest) (<-chan *DeleteDomainRecordResponse, <-chan error) { + responseChan := make(chan *DeleteDomainRecordResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DeleteDomainRecord(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DeleteDomainRecordWithCallback invokes the alidns.DeleteDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletedomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteDomainRecordWithCallback(request *DeleteDomainRecordRequest, callback func(response *DeleteDomainRecordResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DeleteDomainRecordResponse + var err error + defer close(result) + response, err = client.DeleteDomainRecord(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DeleteDomainRecordRequest is the request struct for api DeleteDomainRecord +type DeleteDomainRecordRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + RecordId string `position:"Query" name:"RecordId"` +} + +// DeleteDomainRecordResponse is the response struct for api DeleteDomainRecord +type DeleteDomainRecordResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordId string `json:"RecordId" xml:"RecordId"` +} + +// CreateDeleteDomainRecordRequest creates a request to invoke DeleteDomainRecord API +func CreateDeleteDomainRecordRequest() (request *DeleteDomainRecordRequest) { + request = &DeleteDomainRecordRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DeleteDomainRecord", "", "") + return +} + +// CreateDeleteDomainRecordResponse creates a response to parse from DeleteDomainRecord response +func CreateDeleteDomainRecordResponse() (response *DeleteDomainRecordResponse) { + response = &DeleteDomainRecordResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_sub_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_sub_domain_records.go new file mode 100644 index 000000000..06bb0fd72 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/delete_sub_domain_records.go @@ -0,0 +1,109 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DeleteSubDomainRecords invokes the alidns.DeleteSubDomainRecords API synchronously +// api document: https://help.aliyun.com/api/alidns/deletesubdomainrecords.html +func (client *Client) DeleteSubDomainRecords(request *DeleteSubDomainRecordsRequest) (response *DeleteSubDomainRecordsResponse, err error) { + response = CreateDeleteSubDomainRecordsResponse() + err = client.DoAction(request, response) + return +} + +// DeleteSubDomainRecordsWithChan invokes the alidns.DeleteSubDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletesubdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteSubDomainRecordsWithChan(request *DeleteSubDomainRecordsRequest) (<-chan *DeleteSubDomainRecordsResponse, <-chan error) { + responseChan := make(chan *DeleteSubDomainRecordsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DeleteSubDomainRecords(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DeleteSubDomainRecordsWithCallback invokes the alidns.DeleteSubDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/deletesubdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DeleteSubDomainRecordsWithCallback(request *DeleteSubDomainRecordsRequest, callback func(response *DeleteSubDomainRecordsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DeleteSubDomainRecordsResponse + var err error + defer close(result) + response, err = client.DeleteSubDomainRecords(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DeleteSubDomainRecordsRequest is the request struct for api DeleteSubDomainRecords +type DeleteSubDomainRecordsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + RR string `position:"Query" name:"RR"` + Type string `position:"Query" name:"Type"` +} + +// DeleteSubDomainRecordsResponse is the response struct for api DeleteSubDomainRecords +type DeleteSubDomainRecordsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RR string `json:"RR" xml:"RR"` + TotalCount string `json:"TotalCount" xml:"TotalCount"` +} + +// CreateDeleteSubDomainRecordsRequest creates a request to invoke DeleteSubDomainRecords API +func CreateDeleteSubDomainRecordsRequest() (request *DeleteSubDomainRecordsRequest) { + request = &DeleteSubDomainRecordsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DeleteSubDomainRecords", "", "") + return +} + +// CreateDeleteSubDomainRecordsResponse creates a response to parse from DeleteSubDomainRecords response +func CreateDeleteSubDomainRecordsResponse() (response *DeleteSubDomainRecordsResponse) { + response = &DeleteSubDomainRecordsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_batch_result.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_batch_result.go new file mode 100644 index 000000000..67590cb8b --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_batch_result.go @@ -0,0 +1,110 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeBatchResult invokes the alidns.DescribeBatchResult API synchronously +// api document: https://help.aliyun.com/api/alidns/describebatchresult.html +func (client *Client) DescribeBatchResult(request *DescribeBatchResultRequest) (response *DescribeBatchResultResponse, err error) { + response = CreateDescribeBatchResultResponse() + err = client.DoAction(request, response) + return +} + +// DescribeBatchResultWithChan invokes the alidns.DescribeBatchResult API asynchronously +// api document: https://help.aliyun.com/api/alidns/describebatchresult.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeBatchResultWithChan(request *DescribeBatchResultRequest) (<-chan *DescribeBatchResultResponse, <-chan error) { + responseChan := make(chan *DescribeBatchResultResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeBatchResult(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeBatchResultWithCallback invokes the alidns.DescribeBatchResult API asynchronously +// api document: https://help.aliyun.com/api/alidns/describebatchresult.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeBatchResultWithCallback(request *DescribeBatchResultRequest, callback func(response *DescribeBatchResultResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeBatchResultResponse + var err error + defer close(result) + response, err = client.DescribeBatchResult(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeBatchResultRequest is the request struct for api DescribeBatchResult +type DescribeBatchResultRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + TraceId string `position:"Query" name:"TraceId"` +} + +// DescribeBatchResultResponse is the response struct for api DescribeBatchResult +type DescribeBatchResultResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TraceId string `json:"TraceId" xml:"TraceId"` + Status int `json:"Status" xml:"Status"` + BatchCount int `json:"BatchCount" xml:"BatchCount"` + SuccessNumber int `json:"SuccessNumber" xml:"SuccessNumber"` + FailResults FailResults `json:"FailResults" xml:"FailResults"` +} + +// CreateDescribeBatchResultRequest creates a request to invoke DescribeBatchResult API +func CreateDescribeBatchResultRequest() (request *DescribeBatchResultRequest) { + request = &DescribeBatchResultRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeBatchResult", "", "") + return +} + +// CreateDescribeBatchResultResponse creates a response to parse from DescribeBatchResult response +func CreateDescribeBatchResultResponse() (response *DescribeBatchResultResponse) { + response = &DescribeBatchResultResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instance.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instance.go new file mode 100644 index 000000000..b92bf7830 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instance.go @@ -0,0 +1,134 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDnsProductInstance invokes the alidns.DescribeDnsProductInstance API synchronously +// api document: https://help.aliyun.com/api/alidns/describednsproductinstance.html +func (client *Client) DescribeDnsProductInstance(request *DescribeDnsProductInstanceRequest) (response *DescribeDnsProductInstanceResponse, err error) { + response = CreateDescribeDnsProductInstanceResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDnsProductInstanceWithChan invokes the alidns.DescribeDnsProductInstance API asynchronously +// api document: https://help.aliyun.com/api/alidns/describednsproductinstance.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDnsProductInstanceWithChan(request *DescribeDnsProductInstanceRequest) (<-chan *DescribeDnsProductInstanceResponse, <-chan error) { + responseChan := make(chan *DescribeDnsProductInstanceResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDnsProductInstance(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDnsProductInstanceWithCallback invokes the alidns.DescribeDnsProductInstance API asynchronously +// api document: https://help.aliyun.com/api/alidns/describednsproductinstance.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDnsProductInstanceWithCallback(request *DescribeDnsProductInstanceRequest, callback func(response *DescribeDnsProductInstanceResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDnsProductInstanceResponse + var err error + defer close(result) + response, err = client.DescribeDnsProductInstance(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDnsProductInstanceRequest is the request struct for api DescribeDnsProductInstance +type DescribeDnsProductInstanceRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + InstanceId string `position:"Query" name:"InstanceId"` +} + +// DescribeDnsProductInstanceResponse is the response struct for api DescribeDnsProductInstance +type DescribeDnsProductInstanceResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + InstanceId string `json:"InstanceId" xml:"InstanceId"` + VersionCode string `json:"VersionCode" xml:"VersionCode"` + VersionName string `json:"VersionName" xml:"VersionName"` + StartTime string `json:"StartTime" xml:"StartTime"` + StartTimestamp int `json:"StartTimestamp" xml:"StartTimestamp"` + EndTime string `json:"EndTime" xml:"EndTime"` + EndTimestamp int `json:"EndTimestamp" xml:"EndTimestamp"` + Domain string `json:"Domain" xml:"Domain"` + BindCount int `json:"BindCount" xml:"BindCount"` + BindUsedCount int `json:"BindUsedCount" xml:"BindUsedCount"` + TTLMinValue int `json:"TTLMinValue" xml:"TTLMinValue"` + SubDomainLevel int `json:"SubDomainLevel" xml:"SubDomainLevel"` + DnsSLBCount int `json:"DnsSLBCount" xml:"DnsSLBCount"` + URLForwardCount int `json:"URLForwardCount" xml:"URLForwardCount"` + DDosDefendFlow int `json:"DDosDefendFlow" xml:"DDosDefendFlow"` + DDosDefendQuery int `json:"DDosDefendQuery" xml:"DDosDefendQuery"` + OverseaDDosDefendFlow int `json:"OverseaDDosDefendFlow" xml:"OverseaDDosDefendFlow"` + SearchEngineLines string `json:"SearchEngineLines" xml:"SearchEngineLines"` + ISPLines string `json:"ISPLines" xml:"ISPLines"` + ISPRegionLines string `json:"ISPRegionLines" xml:"ISPRegionLines"` + OverseaLine string `json:"OverseaLine" xml:"OverseaLine"` + MonitorNodeCount int `json:"MonitorNodeCount" xml:"MonitorNodeCount"` + MonitorFrequency int `json:"MonitorFrequency" xml:"MonitorFrequency"` + MonitorTaskCount int `json:"MonitorTaskCount" xml:"MonitorTaskCount"` + RegionLines bool `json:"RegionLines" xml:"RegionLines"` + Gslb bool `json:"Gslb" xml:"Gslb"` + InClean bool `json:"InClean" xml:"InClean"` + InBlackHole bool `json:"InBlackHole" xml:"InBlackHole"` + DnsServers DnsServersInDescribeDnsProductInstance `json:"DnsServers" xml:"DnsServers"` +} + +// CreateDescribeDnsProductInstanceRequest creates a request to invoke DescribeDnsProductInstance API +func CreateDescribeDnsProductInstanceRequest() (request *DescribeDnsProductInstanceRequest) { + request = &DescribeDnsProductInstanceRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDnsProductInstance", "", "") + return +} + +// CreateDescribeDnsProductInstanceResponse creates a response to parse from DescribeDnsProductInstance response +func CreateDescribeDnsProductInstanceResponse() (response *DescribeDnsProductInstanceResponse) { + response = &DescribeDnsProductInstanceResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instances.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instances.go new file mode 100644 index 000000000..ae17278e8 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dns_product_instances.go @@ -0,0 +1,111 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDnsProductInstances invokes the alidns.DescribeDnsProductInstances API synchronously +// api document: https://help.aliyun.com/api/alidns/describednsproductinstances.html +func (client *Client) DescribeDnsProductInstances(request *DescribeDnsProductInstancesRequest) (response *DescribeDnsProductInstancesResponse, err error) { + response = CreateDescribeDnsProductInstancesResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDnsProductInstancesWithChan invokes the alidns.DescribeDnsProductInstances API asynchronously +// api document: https://help.aliyun.com/api/alidns/describednsproductinstances.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDnsProductInstancesWithChan(request *DescribeDnsProductInstancesRequest) (<-chan *DescribeDnsProductInstancesResponse, <-chan error) { + responseChan := make(chan *DescribeDnsProductInstancesResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDnsProductInstances(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDnsProductInstancesWithCallback invokes the alidns.DescribeDnsProductInstances API asynchronously +// api document: https://help.aliyun.com/api/alidns/describednsproductinstances.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDnsProductInstancesWithCallback(request *DescribeDnsProductInstancesRequest, callback func(response *DescribeDnsProductInstancesResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDnsProductInstancesResponse + var err error + defer close(result) + response, err = client.DescribeDnsProductInstances(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDnsProductInstancesRequest is the request struct for api DescribeDnsProductInstances +type DescribeDnsProductInstancesRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` + VersionCode string `position:"Query" name:"VersionCode"` +} + +// DescribeDnsProductInstancesResponse is the response struct for api DescribeDnsProductInstances +type DescribeDnsProductInstancesResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + DnsProducts DnsProducts `json:"DnsProducts" xml:"DnsProducts"` +} + +// CreateDescribeDnsProductInstancesRequest creates a request to invoke DescribeDnsProductInstances API +func CreateDescribeDnsProductInstancesRequest() (request *DescribeDnsProductInstancesRequest) { + request = &DescribeDnsProductInstancesRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDnsProductInstances", "", "") + return +} + +// CreateDescribeDnsProductInstancesResponse creates a response to parse from DescribeDnsProductInstances response +func CreateDescribeDnsProductInstancesResponse() (response *DescribeDnsProductInstancesResponse) { + response = &DescribeDnsProductInstancesResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dnsslb_sub_domains.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dnsslb_sub_domains.go new file mode 100644 index 000000000..b545f411a --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_dnsslb_sub_domains.go @@ -0,0 +1,111 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDNSSLBSubDomains invokes the alidns.DescribeDNSSLBSubDomains API synchronously +// api document: https://help.aliyun.com/api/alidns/describednsslbsubdomains.html +func (client *Client) DescribeDNSSLBSubDomains(request *DescribeDNSSLBSubDomainsRequest) (response *DescribeDNSSLBSubDomainsResponse, err error) { + response = CreateDescribeDNSSLBSubDomainsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDNSSLBSubDomainsWithChan invokes the alidns.DescribeDNSSLBSubDomains API asynchronously +// api document: https://help.aliyun.com/api/alidns/describednsslbsubdomains.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDNSSLBSubDomainsWithChan(request *DescribeDNSSLBSubDomainsRequest) (<-chan *DescribeDNSSLBSubDomainsResponse, <-chan error) { + responseChan := make(chan *DescribeDNSSLBSubDomainsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDNSSLBSubDomains(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDNSSLBSubDomainsWithCallback invokes the alidns.DescribeDNSSLBSubDomains API asynchronously +// api document: https://help.aliyun.com/api/alidns/describednsslbsubdomains.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDNSSLBSubDomainsWithCallback(request *DescribeDNSSLBSubDomainsRequest, callback func(response *DescribeDNSSLBSubDomainsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDNSSLBSubDomainsResponse + var err error + defer close(result) + response, err = client.DescribeDNSSLBSubDomains(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDNSSLBSubDomainsRequest is the request struct for api DescribeDNSSLBSubDomains +type DescribeDNSSLBSubDomainsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` +} + +// DescribeDNSSLBSubDomainsResponse is the response struct for api DescribeDNSSLBSubDomains +type DescribeDNSSLBSubDomainsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + SlbSubDomains SlbSubDomains `json:"SlbSubDomains" xml:"SlbSubDomains"` +} + +// CreateDescribeDNSSLBSubDomainsRequest creates a request to invoke DescribeDNSSLBSubDomains API +func CreateDescribeDNSSLBSubDomainsRequest() (request *DescribeDNSSLBSubDomainsRequest) { + request = &DescribeDNSSLBSubDomainsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDNSSLBSubDomains", "", "") + return +} + +// CreateDescribeDNSSLBSubDomainsResponse creates a response to parse from DescribeDNSSLBSubDomains response +func CreateDescribeDNSSLBSubDomainsResponse() (response *DescribeDNSSLBSubDomainsResponse) { + response = &DescribeDNSSLBSubDomainsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_groups.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_groups.go new file mode 100644 index 000000000..ffe1f1f37 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_groups.go @@ -0,0 +1,111 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainGroups invokes the alidns.DescribeDomainGroups API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomaingroups.html +func (client *Client) DescribeDomainGroups(request *DescribeDomainGroupsRequest) (response *DescribeDomainGroupsResponse, err error) { + response = CreateDescribeDomainGroupsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainGroupsWithChan invokes the alidns.DescribeDomainGroups API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomaingroups.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainGroupsWithChan(request *DescribeDomainGroupsRequest) (<-chan *DescribeDomainGroupsResponse, <-chan error) { + responseChan := make(chan *DescribeDomainGroupsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainGroups(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainGroupsWithCallback invokes the alidns.DescribeDomainGroups API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomaingroups.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainGroupsWithCallback(request *DescribeDomainGroupsRequest, callback func(response *DescribeDomainGroupsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainGroupsResponse + var err error + defer close(result) + response, err = client.DescribeDomainGroups(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainGroupsRequest is the request struct for api DescribeDomainGroups +type DescribeDomainGroupsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + KeyWord string `position:"Query" name:"KeyWord"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` +} + +// DescribeDomainGroupsResponse is the response struct for api DescribeDomainGroups +type DescribeDomainGroupsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + DomainGroups DomainGroups `json:"DomainGroups" xml:"DomainGroups"` +} + +// CreateDescribeDomainGroupsRequest creates a request to invoke DescribeDomainGroups API +func CreateDescribeDomainGroupsRequest() (request *DescribeDomainGroupsRequest) { + request = &DescribeDomainGroupsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainGroups", "", "") + return +} + +// CreateDescribeDomainGroupsResponse creates a response to parse from DescribeDomainGroups response +func CreateDescribeDomainGroupsResponse() (response *DescribeDomainGroupsResponse) { + response = &DescribeDomainGroupsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_info.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_info.go new file mode 100644 index 000000000..0f6f3e08b --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_info.go @@ -0,0 +1,123 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainInfo invokes the alidns.DescribeDomainInfo API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomaininfo.html +func (client *Client) DescribeDomainInfo(request *DescribeDomainInfoRequest) (response *DescribeDomainInfoResponse, err error) { + response = CreateDescribeDomainInfoResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainInfoWithChan invokes the alidns.DescribeDomainInfo API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomaininfo.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainInfoWithChan(request *DescribeDomainInfoRequest) (<-chan *DescribeDomainInfoResponse, <-chan error) { + responseChan := make(chan *DescribeDomainInfoResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainInfo(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainInfoWithCallback invokes the alidns.DescribeDomainInfo API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomaininfo.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainInfoWithCallback(request *DescribeDomainInfoRequest, callback func(response *DescribeDomainInfoResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainInfoResponse + var err error + defer close(result) + response, err = client.DescribeDomainInfo(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainInfoRequest is the request struct for api DescribeDomainInfo +type DescribeDomainInfoRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + NeedDetailAttributes requests.Boolean `position:"Query" name:"NeedDetailAttributes"` +} + +// DescribeDomainInfoResponse is the response struct for api DescribeDomainInfo +type DescribeDomainInfoResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainId string `json:"DomainId" xml:"DomainId"` + DomainName string `json:"DomainName" xml:"DomainName"` + PunyCode string `json:"PunyCode" xml:"PunyCode"` + AliDomain bool `json:"AliDomain" xml:"AliDomain"` + Remark string `json:"Remark" xml:"Remark"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` + InstanceId string `json:"InstanceId" xml:"InstanceId"` + VersionCode string `json:"VersionCode" xml:"VersionCode"` + VersionName string `json:"VersionName" xml:"VersionName"` + MinTtl int `json:"MinTtl" xml:"MinTtl"` + RecordLineTreeJson string `json:"RecordLineTreeJson" xml:"RecordLineTreeJson"` + LineType string `json:"LineType" xml:"LineType"` + RegionLines bool `json:"RegionLines" xml:"RegionLines"` + DnsServers DnsServersInDescribeDomainInfo `json:"DnsServers" xml:"DnsServers"` + AvailableTtls AvailableTtls `json:"AvailableTtls" xml:"AvailableTtls"` + RecordLines RecordLinesInDescribeDomainInfo `json:"RecordLines" xml:"RecordLines"` +} + +// CreateDescribeDomainInfoRequest creates a request to invoke DescribeDomainInfo API +func CreateDescribeDomainInfoRequest() (request *DescribeDomainInfoRequest) { + request = &DescribeDomainInfoRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainInfo", "", "") + return +} + +// CreateDescribeDomainInfoResponse creates a response to parse from DescribeDomainInfo response +func CreateDescribeDomainInfoResponse() (response *DescribeDomainInfoResponse) { + response = &DescribeDomainInfoResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_logs.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_logs.go new file mode 100644 index 000000000..39e18ce87 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_logs.go @@ -0,0 +1,114 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainLogs invokes the alidns.DescribeDomainLogs API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomainlogs.html +func (client *Client) DescribeDomainLogs(request *DescribeDomainLogsRequest) (response *DescribeDomainLogsResponse, err error) { + response = CreateDescribeDomainLogsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainLogsWithChan invokes the alidns.DescribeDomainLogs API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainlogs.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainLogsWithChan(request *DescribeDomainLogsRequest) (<-chan *DescribeDomainLogsResponse, <-chan error) { + responseChan := make(chan *DescribeDomainLogsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainLogs(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainLogsWithCallback invokes the alidns.DescribeDomainLogs API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainlogs.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainLogsWithCallback(request *DescribeDomainLogsRequest, callback func(response *DescribeDomainLogsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainLogsResponse + var err error + defer close(result) + response, err = client.DescribeDomainLogs(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainLogsRequest is the request struct for api DescribeDomainLogs +type DescribeDomainLogsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + KeyWord string `position:"Query" name:"KeyWord"` + GroupId string `position:"Query" name:"GroupId"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` + StartDate string `position:"Query" name:"StartDate"` + EndDate string `position:"Query" name:"endDate"` +} + +// DescribeDomainLogsResponse is the response struct for api DescribeDomainLogs +type DescribeDomainLogsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + DomainLogs DomainLogs `json:"DomainLogs" xml:"DomainLogs"` +} + +// CreateDescribeDomainLogsRequest creates a request to invoke DescribeDomainLogs API +func CreateDescribeDomainLogsRequest() (request *DescribeDomainLogsRequest) { + request = &DescribeDomainLogsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainLogs", "", "") + return +} + +// CreateDescribeDomainLogsResponse creates a response to parse from DescribeDomainLogs response +func CreateDescribeDomainLogsResponse() (response *DescribeDomainLogsResponse) { + response = &DescribeDomainLogsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_ns.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_ns.go new file mode 100644 index 000000000..51c86a95c --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_ns.go @@ -0,0 +1,109 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainNs invokes the alidns.DescribeDomainNs API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomainns.html +func (client *Client) DescribeDomainNs(request *DescribeDomainNsRequest) (response *DescribeDomainNsResponse, err error) { + response = CreateDescribeDomainNsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainNsWithChan invokes the alidns.DescribeDomainNs API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainns.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainNsWithChan(request *DescribeDomainNsRequest) (<-chan *DescribeDomainNsResponse, <-chan error) { + responseChan := make(chan *DescribeDomainNsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainNs(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainNsWithCallback invokes the alidns.DescribeDomainNs API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainns.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainNsWithCallback(request *DescribeDomainNsRequest, callback func(response *DescribeDomainNsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainNsResponse + var err error + defer close(result) + response, err = client.DescribeDomainNs(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainNsRequest is the request struct for api DescribeDomainNs +type DescribeDomainNsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` +} + +// DescribeDomainNsResponse is the response struct for api DescribeDomainNs +type DescribeDomainNsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + AllAliDns bool `json:"AllAliDns" xml:"AllAliDns"` + IncludeAliDns bool `json:"IncludeAliDns" xml:"IncludeAliDns"` + DnsServers DnsServersInDescribeDomainNs `json:"DnsServers" xml:"DnsServers"` + ExpectDnsServers ExpectDnsServers `json:"ExpectDnsServers" xml:"ExpectDnsServers"` +} + +// CreateDescribeDomainNsRequest creates a request to invoke DescribeDomainNs API +func CreateDescribeDomainNsRequest() (request *DescribeDomainNsRequest) { + request = &DescribeDomainNsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainNs", "", "") + return +} + +// CreateDescribeDomainNsResponse creates a response to parse from DescribeDomainNs response +func CreateDescribeDomainNsResponse() (response *DescribeDomainNsResponse) { + response = &DescribeDomainNsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_record_info.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_record_info.go new file mode 100644 index 000000000..c59a7c528 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_record_info.go @@ -0,0 +1,119 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainRecordInfo invokes the alidns.DescribeDomainRecordInfo API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomainrecordinfo.html +func (client *Client) DescribeDomainRecordInfo(request *DescribeDomainRecordInfoRequest) (response *DescribeDomainRecordInfoResponse, err error) { + response = CreateDescribeDomainRecordInfoResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainRecordInfoWithChan invokes the alidns.DescribeDomainRecordInfo API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainrecordinfo.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainRecordInfoWithChan(request *DescribeDomainRecordInfoRequest) (<-chan *DescribeDomainRecordInfoResponse, <-chan error) { + responseChan := make(chan *DescribeDomainRecordInfoResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainRecordInfo(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainRecordInfoWithCallback invokes the alidns.DescribeDomainRecordInfo API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainrecordinfo.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainRecordInfoWithCallback(request *DescribeDomainRecordInfoRequest, callback func(response *DescribeDomainRecordInfoResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainRecordInfoResponse + var err error + defer close(result) + response, err = client.DescribeDomainRecordInfo(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainRecordInfoRequest is the request struct for api DescribeDomainRecordInfo +type DescribeDomainRecordInfoRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + RecordId string `position:"Query" name:"RecordId"` +} + +// DescribeDomainRecordInfoResponse is the response struct for api DescribeDomainRecordInfo +type DescribeDomainRecordInfoResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainId string `json:"DomainId" xml:"DomainId"` + DomainName string `json:"DomainName" xml:"DomainName"` + PunyCode string `json:"PunyCode" xml:"PunyCode"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` + RecordId string `json:"RecordId" xml:"RecordId"` + RR string `json:"RR" xml:"RR"` + Type string `json:"Type" xml:"Type"` + Value string `json:"Value" xml:"Value"` + TTL int `json:"TTL" xml:"TTL"` + Priority int `json:"Priority" xml:"Priority"` + Line string `json:"Line" xml:"Line"` + Status string `json:"Status" xml:"Status"` + Locked bool `json:"Locked" xml:"Locked"` +} + +// CreateDescribeDomainRecordInfoRequest creates a request to invoke DescribeDomainRecordInfo API +func CreateDescribeDomainRecordInfoRequest() (request *DescribeDomainRecordInfoRequest) { + request = &DescribeDomainRecordInfoRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainRecordInfo", "", "") + return +} + +// CreateDescribeDomainRecordInfoResponse creates a response to parse from DescribeDomainRecordInfo response +func CreateDescribeDomainRecordInfoResponse() (response *DescribeDomainRecordInfoResponse) { + response = &DescribeDomainRecordInfoResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_records.go new file mode 100644 index 000000000..bc905384a --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_records.go @@ -0,0 +1,117 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainRecords invokes the alidns.DescribeDomainRecords API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomainrecords.html +func (client *Client) DescribeDomainRecords(request *DescribeDomainRecordsRequest) (response *DescribeDomainRecordsResponse, err error) { + response = CreateDescribeDomainRecordsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainRecordsWithChan invokes the alidns.DescribeDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainRecordsWithChan(request *DescribeDomainRecordsRequest) (<-chan *DescribeDomainRecordsResponse, <-chan error) { + responseChan := make(chan *DescribeDomainRecordsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainRecords(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainRecordsWithCallback invokes the alidns.DescribeDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainRecordsWithCallback(request *DescribeDomainRecordsRequest, callback func(response *DescribeDomainRecordsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainRecordsResponse + var err error + defer close(result) + response, err = client.DescribeDomainRecords(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainRecordsRequest is the request struct for api DescribeDomainRecords +type DescribeDomainRecordsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` + KeyWord string `position:"Query" name:"KeyWord"` + RRKeyWord string `position:"Query" name:"RRKeyWord"` + TypeKeyWord string `position:"Query" name:"TypeKeyWord"` + ValueKeyWord string `position:"Query" name:"ValueKeyWord"` + OrderBy string `position:"Query" name:"OrderBy"` + Direction string `position:"Query" name:"Direction"` +} + +// DescribeDomainRecordsResponse is the response struct for api DescribeDomainRecords +type DescribeDomainRecordsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + DomainRecords DomainRecordsInDescribeDomainRecords `json:"DomainRecords" xml:"DomainRecords"` +} + +// CreateDescribeDomainRecordsRequest creates a request to invoke DescribeDomainRecords API +func CreateDescribeDomainRecordsRequest() (request *DescribeDomainRecordsRequest) { + request = &DescribeDomainRecordsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainRecords", "", "") + return +} + +// CreateDescribeDomainRecordsResponse creates a response to parse from DescribeDomainRecords response +func CreateDescribeDomainRecordsResponse() (response *DescribeDomainRecordsResponse) { + response = &DescribeDomainRecordsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_whois_info.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_whois_info.go new file mode 100644 index 000000000..9984f6dfc --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domain_whois_info.go @@ -0,0 +1,113 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomainWhoisInfo invokes the alidns.DescribeDomainWhoisInfo API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomainwhoisinfo.html +func (client *Client) DescribeDomainWhoisInfo(request *DescribeDomainWhoisInfoRequest) (response *DescribeDomainWhoisInfoResponse, err error) { + response = CreateDescribeDomainWhoisInfoResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainWhoisInfoWithChan invokes the alidns.DescribeDomainWhoisInfo API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainwhoisinfo.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainWhoisInfoWithChan(request *DescribeDomainWhoisInfoRequest) (<-chan *DescribeDomainWhoisInfoResponse, <-chan error) { + responseChan := make(chan *DescribeDomainWhoisInfoResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomainWhoisInfo(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainWhoisInfoWithCallback invokes the alidns.DescribeDomainWhoisInfo API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomainwhoisinfo.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainWhoisInfoWithCallback(request *DescribeDomainWhoisInfoRequest, callback func(response *DescribeDomainWhoisInfoResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainWhoisInfoResponse + var err error + defer close(result) + response, err = client.DescribeDomainWhoisInfo(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainWhoisInfoRequest is the request struct for api DescribeDomainWhoisInfo +type DescribeDomainWhoisInfoRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + GroupId string `position:"Query" name:"GroupId"` +} + +// DescribeDomainWhoisInfoResponse is the response struct for api DescribeDomainWhoisInfo +type DescribeDomainWhoisInfoResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RegistrantName string `json:"RegistrantName" xml:"RegistrantName"` + RegistrantEmail string `json:"RegistrantEmail" xml:"RegistrantEmail"` + Registrar string `json:"Registrar" xml:"Registrar"` + RegistrationDate string `json:"RegistrationDate" xml:"RegistrationDate"` + ExpirationDate string `json:"ExpirationDate" xml:"ExpirationDate"` + StatusList StatusList `json:"StatusList" xml:"StatusList"` + DnsServers DnsServersInDescribeDomainWhoisInfo `json:"DnsServers" xml:"DnsServers"` +} + +// CreateDescribeDomainWhoisInfoRequest creates a request to invoke DescribeDomainWhoisInfo API +func CreateDescribeDomainWhoisInfoRequest() (request *DescribeDomainWhoisInfoRequest) { + request = &DescribeDomainWhoisInfoRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomainWhoisInfo", "", "") + return +} + +// CreateDescribeDomainWhoisInfoResponse creates a response to parse from DescribeDomainWhoisInfo response +func CreateDescribeDomainWhoisInfoResponse() (response *DescribeDomainWhoisInfoResponse) { + response = &DescribeDomainWhoisInfoResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domains.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domains.go new file mode 100644 index 000000000..a49982b7b --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_domains.go @@ -0,0 +1,112 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeDomains invokes the alidns.DescribeDomains API synchronously +// api document: https://help.aliyun.com/api/alidns/describedomains.html +func (client *Client) DescribeDomains(request *DescribeDomainsRequest) (response *DescribeDomainsResponse, err error) { + response = CreateDescribeDomainsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeDomainsWithChan invokes the alidns.DescribeDomains API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomains.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainsWithChan(request *DescribeDomainsRequest) (<-chan *DescribeDomainsResponse, <-chan error) { + responseChan := make(chan *DescribeDomainsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeDomains(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeDomainsWithCallback invokes the alidns.DescribeDomains API asynchronously +// api document: https://help.aliyun.com/api/alidns/describedomains.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeDomainsWithCallback(request *DescribeDomainsRequest, callback func(response *DescribeDomainsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeDomainsResponse + var err error + defer close(result) + response, err = client.DescribeDomains(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeDomainsRequest is the request struct for api DescribeDomains +type DescribeDomainsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + KeyWord string `position:"Query" name:"KeyWord"` + GroupId string `position:"Query" name:"GroupId"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` +} + +// DescribeDomainsResponse is the response struct for api DescribeDomains +type DescribeDomainsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + Domains Domains `json:"Domains" xml:"Domains"` +} + +// CreateDescribeDomainsRequest creates a request to invoke DescribeDomains API +func CreateDescribeDomainsRequest() (request *DescribeDomainsRequest) { + request = &DescribeDomainsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeDomains", "", "") + return +} + +// CreateDescribeDomainsResponse creates a response to parse from DescribeDomains response +func CreateDescribeDomainsResponse() (response *DescribeDomainsResponse) { + response = &DescribeDomainsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_record_logs.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_record_logs.go new file mode 100644 index 000000000..d9cd3e626 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_record_logs.go @@ -0,0 +1,114 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeRecordLogs invokes the alidns.DescribeRecordLogs API synchronously +// api document: https://help.aliyun.com/api/alidns/describerecordlogs.html +func (client *Client) DescribeRecordLogs(request *DescribeRecordLogsRequest) (response *DescribeRecordLogsResponse, err error) { + response = CreateDescribeRecordLogsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeRecordLogsWithChan invokes the alidns.DescribeRecordLogs API asynchronously +// api document: https://help.aliyun.com/api/alidns/describerecordlogs.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeRecordLogsWithChan(request *DescribeRecordLogsRequest) (<-chan *DescribeRecordLogsResponse, <-chan error) { + responseChan := make(chan *DescribeRecordLogsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeRecordLogs(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeRecordLogsWithCallback invokes the alidns.DescribeRecordLogs API asynchronously +// api document: https://help.aliyun.com/api/alidns/describerecordlogs.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeRecordLogsWithCallback(request *DescribeRecordLogsRequest, callback func(response *DescribeRecordLogsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeRecordLogsResponse + var err error + defer close(result) + response, err = client.DescribeRecordLogs(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeRecordLogsRequest is the request struct for api DescribeRecordLogs +type DescribeRecordLogsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` + KeyWord string `position:"Query" name:"KeyWord"` + StartDate string `position:"Query" name:"StartDate"` + EndDate string `position:"Query" name:"endDate"` +} + +// DescribeRecordLogsResponse is the response struct for api DescribeRecordLogs +type DescribeRecordLogsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + RecordLogs RecordLogs `json:"RecordLogs" xml:"RecordLogs"` +} + +// CreateDescribeRecordLogsRequest creates a request to invoke DescribeRecordLogs API +func CreateDescribeRecordLogsRequest() (request *DescribeRecordLogsRequest) { + request = &DescribeRecordLogsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeRecordLogs", "", "") + return +} + +// CreateDescribeRecordLogsResponse creates a response to parse from DescribeRecordLogs response +func CreateDescribeRecordLogsResponse() (response *DescribeRecordLogsResponse) { + response = &DescribeRecordLogsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_sub_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_sub_domain_records.go new file mode 100644 index 000000000..2b4ae7d94 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_sub_domain_records.go @@ -0,0 +1,112 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeSubDomainRecords invokes the alidns.DescribeSubDomainRecords API synchronously +// api document: https://help.aliyun.com/api/alidns/describesubdomainrecords.html +func (client *Client) DescribeSubDomainRecords(request *DescribeSubDomainRecordsRequest) (response *DescribeSubDomainRecordsResponse, err error) { + response = CreateDescribeSubDomainRecordsResponse() + err = client.DoAction(request, response) + return +} + +// DescribeSubDomainRecordsWithChan invokes the alidns.DescribeSubDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/describesubdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeSubDomainRecordsWithChan(request *DescribeSubDomainRecordsRequest) (<-chan *DescribeSubDomainRecordsResponse, <-chan error) { + responseChan := make(chan *DescribeSubDomainRecordsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeSubDomainRecords(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeSubDomainRecordsWithCallback invokes the alidns.DescribeSubDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/describesubdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeSubDomainRecordsWithCallback(request *DescribeSubDomainRecordsRequest, callback func(response *DescribeSubDomainRecordsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeSubDomainRecordsResponse + var err error + defer close(result) + response, err = client.DescribeSubDomainRecords(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeSubDomainRecordsRequest is the request struct for api DescribeSubDomainRecords +type DescribeSubDomainRecordsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + SubDomain string `position:"Query" name:"SubDomain"` + PageNumber requests.Integer `position:"Query" name:"PageNumber"` + PageSize requests.Integer `position:"Query" name:"PageSize"` + Type string `position:"Query" name:"Type"` +} + +// DescribeSubDomainRecordsResponse is the response struct for api DescribeSubDomainRecords +type DescribeSubDomainRecordsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TotalCount int `json:"TotalCount" xml:"TotalCount"` + PageNumber int `json:"PageNumber" xml:"PageNumber"` + PageSize int `json:"PageSize" xml:"PageSize"` + DomainRecords DomainRecordsInDescribeSubDomainRecords `json:"DomainRecords" xml:"DomainRecords"` +} + +// CreateDescribeSubDomainRecordsRequest creates a request to invoke DescribeSubDomainRecords API +func CreateDescribeSubDomainRecordsRequest() (request *DescribeSubDomainRecordsRequest) { + request = &DescribeSubDomainRecordsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeSubDomainRecords", "", "") + return +} + +// CreateDescribeSubDomainRecordsResponse creates a response to parse from DescribeSubDomainRecords response +func CreateDescribeSubDomainRecordsResponse() (response *DescribeSubDomainRecordsResponse) { + response = &DescribeSubDomainRecordsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_support_lines.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_support_lines.go new file mode 100644 index 000000000..3738c3cb2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/describe_support_lines.go @@ -0,0 +1,105 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// DescribeSupportLines invokes the alidns.DescribeSupportLines API synchronously +// api document: https://help.aliyun.com/api/alidns/describesupportlines.html +func (client *Client) DescribeSupportLines(request *DescribeSupportLinesRequest) (response *DescribeSupportLinesResponse, err error) { + response = CreateDescribeSupportLinesResponse() + err = client.DoAction(request, response) + return +} + +// DescribeSupportLinesWithChan invokes the alidns.DescribeSupportLines API asynchronously +// api document: https://help.aliyun.com/api/alidns/describesupportlines.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeSupportLinesWithChan(request *DescribeSupportLinesRequest) (<-chan *DescribeSupportLinesResponse, <-chan error) { + responseChan := make(chan *DescribeSupportLinesResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.DescribeSupportLines(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// DescribeSupportLinesWithCallback invokes the alidns.DescribeSupportLines API asynchronously +// api document: https://help.aliyun.com/api/alidns/describesupportlines.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) DescribeSupportLinesWithCallback(request *DescribeSupportLinesRequest, callback func(response *DescribeSupportLinesResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *DescribeSupportLinesResponse + var err error + defer close(result) + response, err = client.DescribeSupportLines(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// DescribeSupportLinesRequest is the request struct for api DescribeSupportLines +type DescribeSupportLinesRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` +} + +// DescribeSupportLinesResponse is the response struct for api DescribeSupportLines +type DescribeSupportLinesResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordLines RecordLinesInDescribeSupportLines `json:"RecordLines" xml:"RecordLines"` +} + +// CreateDescribeSupportLinesRequest creates a request to invoke DescribeSupportLines API +func CreateDescribeSupportLinesRequest() (request *DescribeSupportLinesRequest) { + request = &DescribeSupportLinesRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "DescribeSupportLines", "", "") + return +} + +// CreateDescribeSupportLinesResponse creates a response to parse from DescribeSupportLines response +func CreateDescribeSupportLinesResponse() (response *DescribeSupportLinesResponse) { + response = &DescribeSupportLinesResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/get_main_domain_name.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/get_main_domain_name.go new file mode 100644 index 000000000..e5199878c --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/get_main_domain_name.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// GetMainDomainName invokes the alidns.GetMainDomainName API synchronously +// api document: https://help.aliyun.com/api/alidns/getmaindomainname.html +func (client *Client) GetMainDomainName(request *GetMainDomainNameRequest) (response *GetMainDomainNameResponse, err error) { + response = CreateGetMainDomainNameResponse() + err = client.DoAction(request, response) + return +} + +// GetMainDomainNameWithChan invokes the alidns.GetMainDomainName API asynchronously +// api document: https://help.aliyun.com/api/alidns/getmaindomainname.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) GetMainDomainNameWithChan(request *GetMainDomainNameRequest) (<-chan *GetMainDomainNameResponse, <-chan error) { + responseChan := make(chan *GetMainDomainNameResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.GetMainDomainName(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// GetMainDomainNameWithCallback invokes the alidns.GetMainDomainName API asynchronously +// api document: https://help.aliyun.com/api/alidns/getmaindomainname.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) GetMainDomainNameWithCallback(request *GetMainDomainNameRequest, callback func(response *GetMainDomainNameResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *GetMainDomainNameResponse + var err error + defer close(result) + response, err = client.GetMainDomainName(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// GetMainDomainNameRequest is the request struct for api GetMainDomainName +type GetMainDomainNameRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + InputString string `position:"Query" name:"InputString"` +} + +// GetMainDomainNameResponse is the response struct for api GetMainDomainName +type GetMainDomainNameResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainName string `json:"DomainName" xml:"DomainName"` + RR string `json:"RR" xml:"RR"` + DomainLevel int `json:"DomainLevel" xml:"DomainLevel"` +} + +// CreateGetMainDomainNameRequest creates a request to invoke GetMainDomainName API +func CreateGetMainDomainNameRequest() (request *GetMainDomainNameRequest) { + request = &GetMainDomainNameRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "GetMainDomainName", "", "") + return +} + +// CreateGetMainDomainNameResponse creates a response to parse from GetMainDomainName response +func CreateGetMainDomainNameResponse() (response *GetMainDomainNameResponse) { + response = &GetMainDomainNameResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/modify_hichina_domain_dns.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/modify_hichina_domain_dns.go new file mode 100644 index 000000000..3b452cdf7 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/modify_hichina_domain_dns.go @@ -0,0 +1,107 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// ModifyHichinaDomainDNS invokes the alidns.ModifyHichinaDomainDNS API synchronously +// api document: https://help.aliyun.com/api/alidns/modifyhichinadomaindns.html +func (client *Client) ModifyHichinaDomainDNS(request *ModifyHichinaDomainDNSRequest) (response *ModifyHichinaDomainDNSResponse, err error) { + response = CreateModifyHichinaDomainDNSResponse() + err = client.DoAction(request, response) + return +} + +// ModifyHichinaDomainDNSWithChan invokes the alidns.ModifyHichinaDomainDNS API asynchronously +// api document: https://help.aliyun.com/api/alidns/modifyhichinadomaindns.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ModifyHichinaDomainDNSWithChan(request *ModifyHichinaDomainDNSRequest) (<-chan *ModifyHichinaDomainDNSResponse, <-chan error) { + responseChan := make(chan *ModifyHichinaDomainDNSResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.ModifyHichinaDomainDNS(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// ModifyHichinaDomainDNSWithCallback invokes the alidns.ModifyHichinaDomainDNS API asynchronously +// api document: https://help.aliyun.com/api/alidns/modifyhichinadomaindns.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) ModifyHichinaDomainDNSWithCallback(request *ModifyHichinaDomainDNSRequest, callback func(response *ModifyHichinaDomainDNSResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *ModifyHichinaDomainDNSResponse + var err error + defer close(result) + response, err = client.ModifyHichinaDomainDNS(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// ModifyHichinaDomainDNSRequest is the request struct for api ModifyHichinaDomainDNS +type ModifyHichinaDomainDNSRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` +} + +// ModifyHichinaDomainDNSResponse is the response struct for api ModifyHichinaDomainDNS +type ModifyHichinaDomainDNSResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + OriginalDnsServers OriginalDnsServers `json:"OriginalDnsServers" xml:"OriginalDnsServers"` + NewDnsServers NewDnsServers `json:"NewDnsServers" xml:"NewDnsServers"` +} + +// CreateModifyHichinaDomainDNSRequest creates a request to invoke ModifyHichinaDomainDNS API +func CreateModifyHichinaDomainDNSRequest() (request *ModifyHichinaDomainDNSRequest) { + request = &ModifyHichinaDomainDNSRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "ModifyHichinaDomainDNS", "", "") + return +} + +// CreateModifyHichinaDomainDNSResponse creates a response to parse from ModifyHichinaDomainDNS response +func CreateModifyHichinaDomainDNSResponse() (response *ModifyHichinaDomainDNSResponse) { + response = &ModifyHichinaDomainDNSResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/retrieval_domain_name.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/retrieval_domain_name.go new file mode 100644 index 000000000..753b85df5 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/retrieval_domain_name.go @@ -0,0 +1,107 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// RetrievalDomainName invokes the alidns.RetrievalDomainName API synchronously +// api document: https://help.aliyun.com/api/alidns/retrievaldomainname.html +func (client *Client) RetrievalDomainName(request *RetrievalDomainNameRequest) (response *RetrievalDomainNameResponse, err error) { + response = CreateRetrievalDomainNameResponse() + err = client.DoAction(request, response) + return +} + +// RetrievalDomainNameWithChan invokes the alidns.RetrievalDomainName API asynchronously +// api document: https://help.aliyun.com/api/alidns/retrievaldomainname.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) RetrievalDomainNameWithChan(request *RetrievalDomainNameRequest) (<-chan *RetrievalDomainNameResponse, <-chan error) { + responseChan := make(chan *RetrievalDomainNameResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.RetrievalDomainName(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// RetrievalDomainNameWithCallback invokes the alidns.RetrievalDomainName API asynchronously +// api document: https://help.aliyun.com/api/alidns/retrievaldomainname.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) RetrievalDomainNameWithCallback(request *RetrievalDomainNameRequest, callback func(response *RetrievalDomainNameResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *RetrievalDomainNameResponse + var err error + defer close(result) + response, err = client.RetrievalDomainName(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// RetrievalDomainNameRequest is the request struct for api RetrievalDomainName +type RetrievalDomainNameRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + DomainName string `position:"Query" name:"DomainName"` +} + +// RetrievalDomainNameResponse is the response struct for api RetrievalDomainName +type RetrievalDomainNameResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + DomainName string `json:"DomainName" xml:"DomainName"` + WhoisEmail string `json:"WhoisEmail" xml:"WhoisEmail"` +} + +// CreateRetrievalDomainNameRequest creates a request to invoke RetrievalDomainName API +func CreateRetrievalDomainNameRequest() (request *RetrievalDomainNameRequest) { + request = &RetrievalDomainNameRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "RetrievalDomainName", "", "") + return +} + +// CreateRetrievalDomainNameResponse creates a response to parse from RetrievalDomainName response +func CreateRetrievalDomainNameResponse() (response *RetrievalDomainNameResponse) { + response = &RetrievalDomainNameResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_dnsslb_status.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_dnsslb_status.go new file mode 100644 index 000000000..858d6fe67 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_dnsslb_status.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// SetDNSSLBStatus invokes the alidns.SetDNSSLBStatus API synchronously +// api document: https://help.aliyun.com/api/alidns/setdnsslbstatus.html +func (client *Client) SetDNSSLBStatus(request *SetDNSSLBStatusRequest) (response *SetDNSSLBStatusResponse, err error) { + response = CreateSetDNSSLBStatusResponse() + err = client.DoAction(request, response) + return +} + +// SetDNSSLBStatusWithChan invokes the alidns.SetDNSSLBStatus API asynchronously +// api document: https://help.aliyun.com/api/alidns/setdnsslbstatus.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) SetDNSSLBStatusWithChan(request *SetDNSSLBStatusRequest) (<-chan *SetDNSSLBStatusResponse, <-chan error) { + responseChan := make(chan *SetDNSSLBStatusResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.SetDNSSLBStatus(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// SetDNSSLBStatusWithCallback invokes the alidns.SetDNSSLBStatus API asynchronously +// api document: https://help.aliyun.com/api/alidns/setdnsslbstatus.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) SetDNSSLBStatusWithCallback(request *SetDNSSLBStatusRequest, callback func(response *SetDNSSLBStatusResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *SetDNSSLBStatusResponse + var err error + defer close(result) + response, err = client.SetDNSSLBStatus(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// SetDNSSLBStatusRequest is the request struct for api SetDNSSLBStatus +type SetDNSSLBStatusRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + SubDomain string `position:"Query" name:"SubDomain"` + Open requests.Boolean `position:"Query" name:"Open"` +} + +// SetDNSSLBStatusResponse is the response struct for api SetDNSSLBStatus +type SetDNSSLBStatusResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordCount int `json:"RecordCount" xml:"RecordCount"` + Open bool `json:"Open" xml:"Open"` +} + +// CreateSetDNSSLBStatusRequest creates a request to invoke SetDNSSLBStatus API +func CreateSetDNSSLBStatusRequest() (request *SetDNSSLBStatusRequest) { + request = &SetDNSSLBStatusRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "SetDNSSLBStatus", "", "") + return +} + +// CreateSetDNSSLBStatusResponse creates a response to parse from SetDNSSLBStatus response +func CreateSetDNSSLBStatusResponse() (response *SetDNSSLBStatusResponse) { + response = &SetDNSSLBStatusResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_domain_record_status.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_domain_record_status.go new file mode 100644 index 000000000..29ba5f727 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/set_domain_record_status.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// SetDomainRecordStatus invokes the alidns.SetDomainRecordStatus API synchronously +// api document: https://help.aliyun.com/api/alidns/setdomainrecordstatus.html +func (client *Client) SetDomainRecordStatus(request *SetDomainRecordStatusRequest) (response *SetDomainRecordStatusResponse, err error) { + response = CreateSetDomainRecordStatusResponse() + err = client.DoAction(request, response) + return +} + +// SetDomainRecordStatusWithChan invokes the alidns.SetDomainRecordStatus API asynchronously +// api document: https://help.aliyun.com/api/alidns/setdomainrecordstatus.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) SetDomainRecordStatusWithChan(request *SetDomainRecordStatusRequest) (<-chan *SetDomainRecordStatusResponse, <-chan error) { + responseChan := make(chan *SetDomainRecordStatusResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.SetDomainRecordStatus(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// SetDomainRecordStatusWithCallback invokes the alidns.SetDomainRecordStatus API asynchronously +// api document: https://help.aliyun.com/api/alidns/setdomainrecordstatus.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) SetDomainRecordStatusWithCallback(request *SetDomainRecordStatusRequest, callback func(response *SetDomainRecordStatusResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *SetDomainRecordStatusResponse + var err error + defer close(result) + response, err = client.SetDomainRecordStatus(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// SetDomainRecordStatusRequest is the request struct for api SetDomainRecordStatus +type SetDomainRecordStatusRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + RecordId string `position:"Query" name:"RecordId"` + Status string `position:"Query" name:"Status"` +} + +// SetDomainRecordStatusResponse is the response struct for api SetDomainRecordStatus +type SetDomainRecordStatusResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordId string `json:"RecordId" xml:"RecordId"` + Status string `json:"Status" xml:"Status"` +} + +// CreateSetDomainRecordStatusRequest creates a request to invoke SetDomainRecordStatus API +func CreateSetDomainRecordStatusRequest() (request *SetDomainRecordStatusRequest) { + request = &SetDomainRecordStatusRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "SetDomainRecordStatus", "", "") + return +} + +// CreateSetDomainRecordStatusResponse creates a response to parse from SetDomainRecordStatus response +func CreateSetDomainRecordStatusResponse() (response *SetDomainRecordStatusResponse) { + response = &SetDomainRecordStatusResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_available_ttls.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_available_ttls.go new file mode 100644 index 000000000..e1d0ac7d5 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_available_ttls.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// AvailableTtls is a nested struct in alidns response +type AvailableTtls struct { + AvailableTtl []string `json:"AvailableTtl" xml:"AvailableTtl"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_product.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_product.go new file mode 100644 index 000000000..50a2e8678 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_product.go @@ -0,0 +1,48 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsProduct is a nested struct in alidns response +type DnsProduct struct { + InstanceId string `json:"InstanceId" xml:"InstanceId"` + VersionCode string `json:"VersionCode" xml:"VersionCode"` + VersionName string `json:"VersionName" xml:"VersionName"` + StartTime string `json:"StartTime" xml:"StartTime"` + EndTime string `json:"EndTime" xml:"EndTime"` + StartTimestamp int `json:"StartTimestamp" xml:"StartTimestamp"` + EndTimestamp int `json:"EndTimestamp" xml:"EndTimestamp"` + Domain string `json:"Domain" xml:"Domain"` + BindCount int `json:"BindCount" xml:"BindCount"` + BindUsedCount int `json:"BindUsedCount" xml:"BindUsedCount"` + TTLMinValue int `json:"TTLMinValue" xml:"TTLMinValue"` + SubDomainLevel int `json:"SubDomainLevel" xml:"SubDomainLevel"` + DnsSLBCount int `json:"DnsSLBCount" xml:"DnsSLBCount"` + URLForwardCount int `json:"URLForwardCount" xml:"URLForwardCount"` + DDosDefendFlow int `json:"DDosDefendFlow" xml:"DDosDefendFlow"` + DDosDefendQuery int `json:"DDosDefendQuery" xml:"DDosDefendQuery"` + OverseaDDosDefendFlow int `json:"OverseaDDosDefendFlow" xml:"OverseaDDosDefendFlow"` + SearchEngineLines string `json:"SearchEngineLines" xml:"SearchEngineLines"` + ISPLines string `json:"ISPLines" xml:"ISPLines"` + ISPRegionLines string `json:"ISPRegionLines" xml:"ISPRegionLines"` + OverseaLine string `json:"OverseaLine" xml:"OverseaLine"` + MonitorNodeCount int `json:"MonitorNodeCount" xml:"MonitorNodeCount"` + MonitorFrequency int `json:"MonitorFrequency" xml:"MonitorFrequency"` + MonitorTaskCount int `json:"MonitorTaskCount" xml:"MonitorTaskCount"` + RegionLines bool `json:"RegionLines" xml:"RegionLines"` + Gslb bool `json:"Gslb" xml:"Gslb"` + InClean bool `json:"InClean" xml:"InClean"` + InBlackHole bool `json:"InBlackHole" xml:"InBlackHole"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_products.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_products.go new file mode 100644 index 000000000..636a96ff0 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_products.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsProducts is a nested struct in alidns response +type DnsProducts struct { + DnsProduct []DnsProduct `json:"DnsProduct" xml:"DnsProduct"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_add_domain.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_add_domain.go new file mode 100644 index 000000000..d6f5bfb2b --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_add_domain.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsServersInAddDomain is a nested struct in alidns response +type DnsServersInAddDomain struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_dns_product_instance.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_dns_product_instance.go new file mode 100644 index 000000000..64d0db2ff --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_dns_product_instance.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsServersInDescribeDnsProductInstance is a nested struct in alidns response +type DnsServersInDescribeDnsProductInstance struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_info.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_info.go new file mode 100644 index 000000000..debe6483b --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_info.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsServersInDescribeDomainInfo is a nested struct in alidns response +type DnsServersInDescribeDomainInfo struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_ns.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_ns.go new file mode 100644 index 000000000..67d752ec9 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_ns.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsServersInDescribeDomainNs is a nested struct in alidns response +type DnsServersInDescribeDomainNs struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_whois_info.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_whois_info.go new file mode 100644 index 000000000..fc03ed41a --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domain_whois_info.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsServersInDescribeDomainWhoisInfo is a nested struct in alidns response +type DnsServersInDescribeDomainWhoisInfo struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domains.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domains.go new file mode 100644 index 000000000..dada40af2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_dns_servers_in_describe_domains.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DnsServersInDescribeDomains is a nested struct in alidns response +type DnsServersInDescribeDomains struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain.go new file mode 100644 index 000000000..0dd46e6f3 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain.go @@ -0,0 +1,35 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// Domain is a nested struct in alidns response +type Domain struct { + DomainId string `json:"DomainId" xml:"DomainId"` + DomainName string `json:"DomainName" xml:"DomainName"` + PunyCode string `json:"PunyCode" xml:"PunyCode"` + AliDomain bool `json:"AliDomain" xml:"AliDomain"` + RecordCount int `json:"RecordCount" xml:"RecordCount"` + RegistrantEmail string `json:"RegistrantEmail" xml:"RegistrantEmail"` + Remark string `json:"Remark" xml:"Remark"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` + InstanceId string `json:"InstanceId" xml:"InstanceId"` + VersionCode string `json:"VersionCode" xml:"VersionCode"` + VersionName string `json:"VersionName" xml:"VersionName"` + InstanceEndTime string `json:"InstanceEndTime" xml:"InstanceEndTime"` + InstanceExpired bool `json:"InstanceExpired" xml:"InstanceExpired"` + DnsServers DnsServersInDescribeDomains `json:"DnsServers" xml:"DnsServers"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_group.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_group.go new file mode 100644 index 000000000..89646e8a2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_group.go @@ -0,0 +1,23 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DomainGroup is a nested struct in alidns response +type DomainGroup struct { + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` + DomainCount int `json:"DomainCount" xml:"DomainCount"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_groups.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_groups.go new file mode 100644 index 000000000..ce193d883 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_groups.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DomainGroups is a nested struct in alidns response +type DomainGroups struct { + DomainGroup []DomainGroup `json:"DomainGroup" xml:"DomainGroup"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_log.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_log.go new file mode 100644 index 000000000..40128f6b9 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_log.go @@ -0,0 +1,26 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DomainLog is a nested struct in alidns response +type DomainLog struct { + ActionTime string `json:"ActionTime" xml:"ActionTime"` + ActionTimestamp int `json:"ActionTimestamp" xml:"ActionTimestamp"` + DomainName string `json:"DomainName" xml:"DomainName"` + Action string `json:"Action" xml:"Action"` + Message string `json:"Message" xml:"Message"` + ClientIp string `json:"ClientIp" xml:"ClientIp"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_logs.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_logs.go new file mode 100644 index 000000000..3964d02b5 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_logs.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DomainLogs is a nested struct in alidns response +type DomainLogs struct { + DomainLog []DomainLog `json:"DomainLog" xml:"DomainLog"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_domain_records.go new file mode 100644 index 000000000..5ef972598 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_domain_records.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DomainRecordsInDescribeDomainRecords is a nested struct in alidns response +type DomainRecordsInDescribeDomainRecords struct { + Record []Record `json:"Record" xml:"Record"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_sub_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_sub_domain_records.go new file mode 100644 index 000000000..9b64ba0c8 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domain_records_in_describe_sub_domain_records.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// DomainRecordsInDescribeSubDomainRecords is a nested struct in alidns response +type DomainRecordsInDescribeSubDomainRecords struct { + Record []Record `json:"Record" xml:"Record"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domains.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domains.go new file mode 100644 index 000000000..aff37f854 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_domains.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// Domains is a nested struct in alidns response +type Domains struct { + Domain []Domain `json:"Domain" xml:"Domain"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_expect_dns_servers.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_expect_dns_servers.go new file mode 100644 index 000000000..9befd95b8 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_expect_dns_servers.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// ExpectDnsServers is a nested struct in alidns response +type ExpectDnsServers struct { + ExpectDnsServer []string `json:"ExpectDnsServer" xml:"ExpectDnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_result.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_result.go new file mode 100644 index 000000000..0fc2d3051 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_result.go @@ -0,0 +1,22 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// FailResult is a nested struct in alidns response +type FailResult struct { + BatchIndex string `json:"BatchIndex" xml:"BatchIndex"` + ErrorCode string `json:"ErrorCode" xml:"ErrorCode"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_results.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_results.go new file mode 100644 index 000000000..54ee4d18d --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_fail_results.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// FailResults is a nested struct in alidns response +type FailResults struct { + FailResult []FailResult `json:"FailResult" xml:"FailResult"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_new_dns_servers.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_new_dns_servers.go new file mode 100644 index 000000000..5fd8b180f --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_new_dns_servers.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// NewDnsServers is a nested struct in alidns response +type NewDnsServers struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_original_dns_servers.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_original_dns_servers.go new file mode 100644 index 000000000..b6f90bb3d --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_original_dns_servers.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// OriginalDnsServers is a nested struct in alidns response +type OriginalDnsServers struct { + DnsServer []string `json:"DnsServer" xml:"DnsServer"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record.go new file mode 100644 index 000000000..b4f0f1913 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record.go @@ -0,0 +1,32 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// Record is a nested struct in alidns response +type Record struct { + Locked bool `json:"Locked" xml:"Locked"` + Type string `json:"Type" xml:"Type"` + Line string `json:"Line" xml:"Line"` + RR string `json:"RR" xml:"RR"` + Remark string `json:"Remark" xml:"Remark"` + Priority int `json:"Priority" xml:"Priority"` + RecordId string `json:"RecordId" xml:"RecordId"` + Value string `json:"Value" xml:"Value"` + DomainName string `json:"DomainName" xml:"DomainName"` + Weight int `json:"Weight" xml:"Weight"` + Status string `json:"Status" xml:"Status"` + TTL int `json:"TTL" xml:"TTL"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_line.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_line.go new file mode 100644 index 000000000..2219c2d16 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_line.go @@ -0,0 +1,24 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// RecordLine is a nested struct in alidns response +type RecordLine struct { + LineName string `json:"LineName" xml:"LineName"` + FatherCode string `json:"FatherCode" xml:"FatherCode"` + LineDisplayName string `json:"LineDisplayName" xml:"LineDisplayName"` + LineCode string `json:"LineCode" xml:"LineCode"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_domain_info.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_domain_info.go new file mode 100644 index 000000000..203f525b2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_domain_info.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// RecordLinesInDescribeDomainInfo is a nested struct in alidns response +type RecordLinesInDescribeDomainInfo struct { + RecordLine []RecordLine `json:"RecordLine" xml:"RecordLine"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_support_lines.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_support_lines.go new file mode 100644 index 000000000..5c2e05f20 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_lines_in_describe_support_lines.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// RecordLinesInDescribeSupportLines is a nested struct in alidns response +type RecordLinesInDescribeSupportLines struct { + RecordLine []RecordLine `json:"RecordLine" xml:"RecordLine"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_log.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_log.go new file mode 100644 index 000000000..ee4583520 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_log.go @@ -0,0 +1,25 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// RecordLog is a nested struct in alidns response +type RecordLog struct { + ActionTime string `json:"ActionTime" xml:"ActionTime"` + ActionTimestamp int `json:"ActionTimestamp" xml:"ActionTimestamp"` + Action string `json:"Action" xml:"Action"` + Message string `json:"Message" xml:"Message"` + ClientIp string `json:"ClientIp" xml:"ClientIp"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_logs.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_logs.go new file mode 100644 index 000000000..b5a42b5eb --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_record_logs.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// RecordLogs is a nested struct in alidns response +type RecordLogs struct { + RecordLog []RecordLog `json:"RecordLog" xml:"RecordLog"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domain.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domain.go new file mode 100644 index 000000000..043a44718 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domain.go @@ -0,0 +1,24 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// SlbSubDomain is a nested struct in alidns response +type SlbSubDomain struct { + SubDomain string `json:"SubDomain" xml:"SubDomain"` + RecordCount int `json:"RecordCount" xml:"RecordCount"` + Open bool `json:"Open" xml:"Open"` + Type string `json:"Type" xml:"Type"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domains.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domains.go new file mode 100644 index 000000000..4618d59c3 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_slb_sub_domains.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// SlbSubDomains is a nested struct in alidns response +type SlbSubDomains struct { + SlbSubDomain []SlbSubDomain `json:"SlbSubDomain" xml:"SlbSubDomain"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_status_list.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_status_list.go new file mode 100644 index 000000000..341e43d03 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/struct_status_list.go @@ -0,0 +1,21 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +// StatusList is a nested struct in alidns response +type StatusList struct { + Status []string `json:"Status" xml:"Status"` +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_batch_domain_records.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_batch_domain_records.go new file mode 100644 index 000000000..c90e20455 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_batch_domain_records.go @@ -0,0 +1,106 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// UpdateBatchDomainRecords invokes the alidns.UpdateBatchDomainRecords API synchronously +// api document: https://help.aliyun.com/api/alidns/updatebatchdomainrecords.html +func (client *Client) UpdateBatchDomainRecords(request *UpdateBatchDomainRecordsRequest) (response *UpdateBatchDomainRecordsResponse, err error) { + response = CreateUpdateBatchDomainRecordsResponse() + err = client.DoAction(request, response) + return +} + +// UpdateBatchDomainRecordsWithChan invokes the alidns.UpdateBatchDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatebatchdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateBatchDomainRecordsWithChan(request *UpdateBatchDomainRecordsRequest) (<-chan *UpdateBatchDomainRecordsResponse, <-chan error) { + responseChan := make(chan *UpdateBatchDomainRecordsResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.UpdateBatchDomainRecords(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// UpdateBatchDomainRecordsWithCallback invokes the alidns.UpdateBatchDomainRecords API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatebatchdomainrecords.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateBatchDomainRecordsWithCallback(request *UpdateBatchDomainRecordsRequest, callback func(response *UpdateBatchDomainRecordsResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *UpdateBatchDomainRecordsResponse + var err error + defer close(result) + response, err = client.UpdateBatchDomainRecords(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// UpdateBatchDomainRecordsRequest is the request struct for api UpdateBatchDomainRecords +type UpdateBatchDomainRecordsRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + Records string `position:"Query" name:"Records"` +} + +// UpdateBatchDomainRecordsResponse is the response struct for api UpdateBatchDomainRecords +type UpdateBatchDomainRecordsResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + TraceId string `json:"TraceId" xml:"TraceId"` +} + +// CreateUpdateBatchDomainRecordsRequest creates a request to invoke UpdateBatchDomainRecords API +func CreateUpdateBatchDomainRecordsRequest() (request *UpdateBatchDomainRecordsRequest) { + request = &UpdateBatchDomainRecordsRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "UpdateBatchDomainRecords", "", "") + return +} + +// CreateUpdateBatchDomainRecordsResponse creates a response to parse from UpdateBatchDomainRecords response +func CreateUpdateBatchDomainRecordsResponse() (response *UpdateBatchDomainRecordsResponse) { + response = &UpdateBatchDomainRecordsResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_dnsslb_weight.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_dnsslb_weight.go new file mode 100644 index 000000000..ec372a215 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_dnsslb_weight.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// UpdateDNSSLBWeight invokes the alidns.UpdateDNSSLBWeight API synchronously +// api document: https://help.aliyun.com/api/alidns/updatednsslbweight.html +func (client *Client) UpdateDNSSLBWeight(request *UpdateDNSSLBWeightRequest) (response *UpdateDNSSLBWeightResponse, err error) { + response = CreateUpdateDNSSLBWeightResponse() + err = client.DoAction(request, response) + return +} + +// UpdateDNSSLBWeightWithChan invokes the alidns.UpdateDNSSLBWeight API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatednsslbweight.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateDNSSLBWeightWithChan(request *UpdateDNSSLBWeightRequest) (<-chan *UpdateDNSSLBWeightResponse, <-chan error) { + responseChan := make(chan *UpdateDNSSLBWeightResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.UpdateDNSSLBWeight(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// UpdateDNSSLBWeightWithCallback invokes the alidns.UpdateDNSSLBWeight API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatednsslbweight.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateDNSSLBWeightWithCallback(request *UpdateDNSSLBWeightRequest, callback func(response *UpdateDNSSLBWeightResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *UpdateDNSSLBWeightResponse + var err error + defer close(result) + response, err = client.UpdateDNSSLBWeight(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// UpdateDNSSLBWeightRequest is the request struct for api UpdateDNSSLBWeight +type UpdateDNSSLBWeightRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + RecordId string `position:"Query" name:"RecordId"` + Weight requests.Integer `position:"Query" name:"Weight"` +} + +// UpdateDNSSLBWeightResponse is the response struct for api UpdateDNSSLBWeight +type UpdateDNSSLBWeightResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordId string `json:"RecordId" xml:"RecordId"` + Weight int `json:"Weight" xml:"Weight"` +} + +// CreateUpdateDNSSLBWeightRequest creates a request to invoke UpdateDNSSLBWeight API +func CreateUpdateDNSSLBWeightRequest() (request *UpdateDNSSLBWeightRequest) { + request = &UpdateDNSSLBWeightRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "UpdateDNSSLBWeight", "", "") + return +} + +// CreateUpdateDNSSLBWeightResponse creates a response to parse from UpdateDNSSLBWeight response +func CreateUpdateDNSSLBWeightResponse() (response *UpdateDNSSLBWeightResponse) { + response = &UpdateDNSSLBWeightResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_group.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_group.go new file mode 100644 index 000000000..61f1fb1d2 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_group.go @@ -0,0 +1,108 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// UpdateDomainGroup invokes the alidns.UpdateDomainGroup API synchronously +// api document: https://help.aliyun.com/api/alidns/updatedomaingroup.html +func (client *Client) UpdateDomainGroup(request *UpdateDomainGroupRequest) (response *UpdateDomainGroupResponse, err error) { + response = CreateUpdateDomainGroupResponse() + err = client.DoAction(request, response) + return +} + +// UpdateDomainGroupWithChan invokes the alidns.UpdateDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatedomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateDomainGroupWithChan(request *UpdateDomainGroupRequest) (<-chan *UpdateDomainGroupResponse, <-chan error) { + responseChan := make(chan *UpdateDomainGroupResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.UpdateDomainGroup(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// UpdateDomainGroupWithCallback invokes the alidns.UpdateDomainGroup API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatedomaingroup.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateDomainGroupWithCallback(request *UpdateDomainGroupRequest, callback func(response *UpdateDomainGroupResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *UpdateDomainGroupResponse + var err error + defer close(result) + response, err = client.UpdateDomainGroup(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// UpdateDomainGroupRequest is the request struct for api UpdateDomainGroup +type UpdateDomainGroupRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + GroupId string `position:"Query" name:"GroupId"` + GroupName string `position:"Query" name:"GroupName"` +} + +// UpdateDomainGroupResponse is the response struct for api UpdateDomainGroup +type UpdateDomainGroupResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + GroupId string `json:"GroupId" xml:"GroupId"` + GroupName string `json:"GroupName" xml:"GroupName"` +} + +// CreateUpdateDomainGroupRequest creates a request to invoke UpdateDomainGroup API +func CreateUpdateDomainGroupRequest() (request *UpdateDomainGroupRequest) { + request = &UpdateDomainGroupRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "UpdateDomainGroup", "", "") + return +} + +// CreateUpdateDomainGroupResponse creates a response to parse from UpdateDomainGroup response +func CreateUpdateDomainGroupResponse() (response *UpdateDomainGroupResponse) { + response = &UpdateDomainGroupResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_record.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_record.go new file mode 100644 index 000000000..1e9c0daf1 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/alidns/update_domain_record.go @@ -0,0 +1,112 @@ +package alidns + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// UpdateDomainRecord invokes the alidns.UpdateDomainRecord API synchronously +// api document: https://help.aliyun.com/api/alidns/updatedomainrecord.html +func (client *Client) UpdateDomainRecord(request *UpdateDomainRecordRequest) (response *UpdateDomainRecordResponse, err error) { + response = CreateUpdateDomainRecordResponse() + err = client.DoAction(request, response) + return +} + +// UpdateDomainRecordWithChan invokes the alidns.UpdateDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatedomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateDomainRecordWithChan(request *UpdateDomainRecordRequest) (<-chan *UpdateDomainRecordResponse, <-chan error) { + responseChan := make(chan *UpdateDomainRecordResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.UpdateDomainRecord(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// UpdateDomainRecordWithCallback invokes the alidns.UpdateDomainRecord API asynchronously +// api document: https://help.aliyun.com/api/alidns/updatedomainrecord.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) UpdateDomainRecordWithCallback(request *UpdateDomainRecordRequest, callback func(response *UpdateDomainRecordResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *UpdateDomainRecordResponse + var err error + defer close(result) + response, err = client.UpdateDomainRecord(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// UpdateDomainRecordRequest is the request struct for api UpdateDomainRecord +type UpdateDomainRecordRequest struct { + *requests.RpcRequest + Lang string `position:"Query" name:"Lang"` + UserClientIp string `position:"Query" name:"UserClientIp"` + RecordId string `position:"Query" name:"RecordId"` + RR string `position:"Query" name:"RR"` + Type string `position:"Query" name:"Type"` + Value string `position:"Query" name:"Value"` + TTL requests.Integer `position:"Query" name:"TTL"` + Priority requests.Integer `position:"Query" name:"Priority"` + Line string `position:"Query" name:"Line"` +} + +// UpdateDomainRecordResponse is the response struct for api UpdateDomainRecord +type UpdateDomainRecordResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + RecordId string `json:"RecordId" xml:"RecordId"` +} + +// CreateUpdateDomainRecordRequest creates a request to invoke UpdateDomainRecord API +func CreateUpdateDomainRecordRequest() (request *UpdateDomainRecordRequest) { + request = &UpdateDomainRecordRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Alidns", "2015-01-09", "UpdateDomainRecord", "", "") + return +} + +// CreateUpdateDomainRecordResponse creates a response to parse from UpdateDomainRecord response +func CreateUpdateDomainRecordResponse() (response *UpdateDomainRecordResponse) { + response = &UpdateDomainRecordResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_to_android.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_to_android.go new file mode 100644 index 000000000..ef385f3a9 --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_to_android.go @@ -0,0 +1,110 @@ +package push + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// PushNoticeToAndroid invokes the push.PushNoticeToAndroid API synchronously +// api document: https://help.aliyun.com/api/push/pushnoticetoandroid.html +func (client *Client) PushNoticeToAndroid(request *PushNoticeToAndroidRequest) (response *PushNoticeToAndroidResponse, err error) { + response = CreatePushNoticeToAndroidResponse() + err = client.DoAction(request, response) + return +} + +// PushNoticeToAndroidWithChan invokes the push.PushNoticeToAndroid API asynchronously +// api document: https://help.aliyun.com/api/push/pushnoticetoandroid.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) PushNoticeToAndroidWithChan(request *PushNoticeToAndroidRequest) (<-chan *PushNoticeToAndroidResponse, <-chan error) { + responseChan := make(chan *PushNoticeToAndroidResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.PushNoticeToAndroid(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// PushNoticeToAndroidWithCallback invokes the push.PushNoticeToAndroid API asynchronously +// api document: https://help.aliyun.com/api/push/pushnoticetoandroid.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) PushNoticeToAndroidWithCallback(request *PushNoticeToAndroidRequest, callback func(response *PushNoticeToAndroidResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *PushNoticeToAndroidResponse + var err error + defer close(result) + response, err = client.PushNoticeToAndroid(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// PushNoticeToAndroidRequest is the request struct for api PushNoticeToAndroid +type PushNoticeToAndroidRequest struct { + *requests.RpcRequest + AppKey requests.Integer `position:"Query" name:"AppKey"` + Target string `position:"Query" name:"Target"` + TargetValue string `position:"Query" name:"TargetValue"` + Title string `position:"Query" name:"Title"` + Body string `position:"Query" name:"Body"` + JobKey string `position:"Query" name:"JobKey"` + ExtParameters string `position:"Query" name:"ExtParameters"` +} + +// PushNoticeToAndroidResponse is the response struct for api PushNoticeToAndroid +type PushNoticeToAndroidResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + MessageId string `json:"MessageId" xml:"MessageId"` +} + +// CreatePushNoticeToAndroidRequest creates a request to invoke PushNoticeToAndroid API +func CreatePushNoticeToAndroidRequest() (request *PushNoticeToAndroidRequest) { + request = &PushNoticeToAndroidRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Push", "2016-08-01", "PushNoticeToAndroid", "", "") + return +} + +// CreatePushNoticeToAndroidResponse creates a response to parse from PushNoticeToAndroid response +func CreatePushNoticeToAndroidResponse() (response *PushNoticeToAndroidResponse) { + response = &PushNoticeToAndroidResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_toi_os.go b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_toi_os.go new file mode 100644 index 000000000..73d1a588e --- /dev/null +++ b/vendor/github.com/aliyun/alibaba-cloud-sdk-go/services/push/push_notice_toi_os.go @@ -0,0 +1,111 @@ +package push + +//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. +// +// Code generated by Alibaba Cloud SDK Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +import ( + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" +) + +// PushNoticeToiOS invokes the push.PushNoticeToiOS API synchronously +// api document: https://help.aliyun.com/api/push/pushnoticetoios.html +func (client *Client) PushNoticeToiOS(request *PushNoticeToiOSRequest) (response *PushNoticeToiOSResponse, err error) { + response = CreatePushNoticeToiOSResponse() + err = client.DoAction(request, response) + return +} + +// PushNoticeToiOSWithChan invokes the push.PushNoticeToiOS API asynchronously +// api document: https://help.aliyun.com/api/push/pushnoticetoios.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) PushNoticeToiOSWithChan(request *PushNoticeToiOSRequest) (<-chan *PushNoticeToiOSResponse, <-chan error) { + responseChan := make(chan *PushNoticeToiOSResponse, 1) + errChan := make(chan error, 1) + err := client.AddAsyncTask(func() { + defer close(responseChan) + defer close(errChan) + response, err := client.PushNoticeToiOS(request) + if err != nil { + errChan <- err + } else { + responseChan <- response + } + }) + if err != nil { + errChan <- err + close(responseChan) + close(errChan) + } + return responseChan, errChan +} + +// PushNoticeToiOSWithCallback invokes the push.PushNoticeToiOS API asynchronously +// api document: https://help.aliyun.com/api/push/pushnoticetoios.html +// asynchronous document: https://help.aliyun.com/document_detail/66220.html +func (client *Client) PushNoticeToiOSWithCallback(request *PushNoticeToiOSRequest, callback func(response *PushNoticeToiOSResponse, err error)) <-chan int { + result := make(chan int, 1) + err := client.AddAsyncTask(func() { + var response *PushNoticeToiOSResponse + var err error + defer close(result) + response, err = client.PushNoticeToiOS(request) + callback(response, err) + result <- 1 + }) + if err != nil { + defer close(result) + callback(nil, err) + result <- 0 + } + return result +} + +// PushNoticeToiOSRequest is the request struct for api PushNoticeToiOS +type PushNoticeToiOSRequest struct { + *requests.RpcRequest + AppKey requests.Integer `position:"Query" name:"AppKey"` + Target string `position:"Query" name:"Target"` + TargetValue string `position:"Query" name:"TargetValue"` + ApnsEnv string `position:"Query" name:"ApnsEnv"` + Title string `position:"Query" name:"Title"` + Body string `position:"Query" name:"Body"` + JobKey string `position:"Query" name:"JobKey"` + ExtParameters string `position:"Query" name:"ExtParameters"` +} + +// PushNoticeToiOSResponse is the response struct for api PushNoticeToiOS +type PushNoticeToiOSResponse struct { + *responses.BaseResponse + RequestId string `json:"RequestId" xml:"RequestId"` + MessageId string `json:"MessageId" xml:"MessageId"` +} + +// CreatePushNoticeToiOSRequest creates a request to invoke PushNoticeToiOS API +func CreatePushNoticeToiOSRequest() (request *PushNoticeToiOSRequest) { + request = &PushNoticeToiOSRequest{ + RpcRequest: &requests.RpcRequest{}, + } + request.InitWithApiInfo("Push", "2016-08-01", "PushNoticeToiOS", "", "") + return +} + +// CreatePushNoticeToiOSResponse creates a response to parse from PushNoticeToiOS response +func CreatePushNoticeToiOSResponse() (response *PushNoticeToiOSResponse) { + response = &PushNoticeToiOSResponse{ + BaseResponse: &responses.BaseResponse{}, + } + return +} diff --git a/vendor/github.com/exoscale/egoscale/accounts.go b/vendor/github.com/exoscale/egoscale/accounts.go index 1c8b4d115..9c26208ba 100644 --- a/vendor/github.com/exoscale/egoscale/accounts.go +++ b/vendor/github.com/exoscale/egoscale/accounts.go @@ -1,98 +1,161 @@ package egoscale -const ( - // UserAccount represents a User - UserAccount = iota - // AdminAccount represents an Admin - AdminAccount - // DomainAdminAccount represents a Domain Admin - DomainAdminAccount -) +import "fmt" // AccountType represents the type of an Account // // http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.8/accounts.html#accounts-users-and-domains -type AccountType int64 +type AccountType int16 + +//go:generate stringer -type AccountType +const ( + // UserAccount represents a User + UserAccount AccountType = 0 + // AdminAccount represents an Admin + AdminAccount AccountType = 1 + // DomainAdminAccount represents a Domain Admin + DomainAdminAccount AccountType = 2 +) // Account provides the detailed account information type Account struct { - ID string `json:"id"` - AccountType AccountType `json:"accounttype,omitempty"` - AccountDetails string `json:"accountdetails,omitempty"` - CPUAvailable string `json:"cpuavailable,omitempty"` - CPULimit string `json:"cpulimit,omitempty"` - CPUTotal int64 `json:"cputotal,omitempty"` - DefaultZoneID string `json:"defaultzoneid,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - EIPLimit string `json:"eiplimit,omitempty"` - Groups []string `json:"groups,omitempty"` - IPAvailable string `json:"ipavailable,omitempty"` - IPLimit string `json:"iplimit,omitempty"` - IPTotal int64 `json:"iptotal,omitempty"` - IsDefault bool `json:"isdefault,omitempty"` - MemoryAvailable string `json:"memoryavailable,omitempty"` - MemoryLimit string `json:"memorylimit,omitempty"` - MemoryTotal int64 `json:"memorytotal,omitempty"` - Name string `json:"name,omitempty"` - NetworkAvailable string `json:"networkavailable,omitempty"` - NetworkDomain string `json:"networkdomain,omitempty"` - NetworkLimit string `json:"networklimit,omitempty"` - NetworkTotal int16 `json:"networktotal,omitempty"` - PrimaryStorageAvailable string `json:"primarystorageavailable,omitempty"` - PrimaryStorageLimit string `json:"primarystoragelimit,omitempty"` - PrimaryStorageTotal int64 `json:"primarystoragetotal,omitempty"` - ProjectAvailable string `json:"projectavailable,omitempty"` - ProjectLimit string `json:"projectlimit,omitempty"` - ProjectTotal int64 `json:"projecttotal,omitempty"` - SecondaryStorageAvailable string `json:"secondarystorageavailable,omitempty"` - SecondaryStorageLimit string `json:"secondarystoragelimit,omitempty"` - SecondaryStorageTotal int64 `json:"secondarystoragetotal,omitempty"` - SnapshotAvailable string `json:"snapshotavailable,omitempty"` - SnapshotLimit string `json:"snapshotlimit,omitempty"` - SnapshotTotal int64 `json:"snapshottotal,omitempty"` - State string `json:"state,omitempty"` - TemplateAvailable string `json:"templateavailable,omitempty"` - TemplateLimit string `json:"templatelimit,omitempty"` - TemplateTotal int64 `json:"templatetotal,omitempty"` - VMAvailable string `json:"vmavailable,omitempty"` - VMLimit string `json:"vmlimit,omitempty"` - VMTotal int64 `json:"vmtotal,omitempty"` - VolumeAvailable string `json:"volumeavailable,omitempty"` - VolumeLimit string `json:"volumelimit,omitempty"` - VolumeTotal int64 `json:"volumetotal,omitempty"` - VPCAvailable string `json:"vpcavailable,omitempty"` - VPCLimit string `json:"vpclimit,omitempty"` - VPCTotal int64 `json:"vpctotal,omitempty"` - User []User `json:"user,omitempty"` + AccountDetails map[string]string `json:"accountdetails,omitempty" doc:"details for the account"` + AccountType AccountType `json:"accounttype,omitempty" doc:"account type (admin, domain-admin, user)"` + CPUAvailable string `json:"cpuavailable,omitempty" doc:"the total number of cpu cores available to be created for this account"` + CPULimit string `json:"cpulimit,omitempty" doc:"the total number of cpu cores the account can own"` + CPUTotal int64 `json:"cputotal,omitempty" doc:"the total number of cpu cores owned by account"` + DefaultZoneID *UUID `json:"defaultzoneid,omitempty" doc:"the default zone of the account"` + Domain string `json:"domain,omitempty" doc:"name of the Domain the account belongs too"` + DomainID *UUID `json:"domainid,omitempty" doc:"id of the Domain the account belongs too"` + EipLimit string `json:"eiplimit,omitempty" doc:"the total number of public elastic ip addresses this account can acquire"` + Groups []string `json:"groups,omitempty" doc:"the list of acl groups that account belongs to"` + ID *UUID `json:"id,omitempty" doc:"the id of the account"` + IPAvailable string `json:"ipavailable,omitempty" doc:"the total number of public ip addresses available for this account to acquire"` + IPLimit string `json:"iplimit,omitempty" doc:"the total number of public ip addresses this account can acquire"` + IPTotal int64 `json:"iptotal,omitempty" doc:"the total number of public ip addresses allocated for this account"` + IsCleanupRequired bool `json:"iscleanuprequired,omitempty" doc:"true if the account requires cleanup"` + IsDefault bool `json:"isdefault,omitempty" doc:"true if account is default, false otherwise"` + MemoryAvailable string `json:"memoryavailable,omitempty" doc:"the total memory (in MB) available to be created for this account"` + MemoryLimit string `json:"memorylimit,omitempty" doc:"the total memory (in MB) the account can own"` + MemoryTotal int64 `json:"memorytotal,omitempty" doc:"the total memory (in MB) owned by account"` + Name string `json:"name,omitempty" doc:"the name of the account"` + NetworkAvailable string `json:"networkavailable,omitempty" doc:"the total number of networks available to be created for this account"` + NetworkDomain string `json:"networkdomain,omitempty" doc:"the network domain"` + NetworkLimit string `json:"networklimit,omitempty" doc:"the total number of networks the account can own"` + NetworkTotal int64 `json:"networktotal,omitempty" doc:"the total number of networks owned by account"` + PrimaryStorageAvailable string `json:"primarystorageavailable,omitempty" doc:"the total primary storage space (in GiB) available to be used for this account"` + PrimaryStorageLimit string `json:"primarystoragelimit,omitempty" doc:"the total primary storage space (in GiB) the account can own"` + PrimaryStorageTotal int64 `json:"primarystoragetotal,omitempty" doc:"the total primary storage space (in GiB) owned by account"` + ProjectAvailable string `json:"projectavailable,omitempty" doc:"the total number of projects available for administration by this account"` + ProjectLimit string `json:"projectlimit,omitempty" doc:"the total number of projects the account can own"` + ProjectTotal int64 `json:"projecttotal,omitempty" doc:"the total number of projects being administrated by this account"` + SecondaryStorageAvailable string `json:"secondarystorageavailable,omitempty" doc:"the total secondary storage space (in GiB) available to be used for this account"` + SecondaryStorageLimit string `json:"secondarystoragelimit,omitempty" doc:"the total secondary storage space (in GiB) the account can own"` + SecondaryStorageTotal int64 `json:"secondarystoragetotal,omitempty" doc:"the total secondary storage space (in GiB) owned by account"` + SMTP bool `json:"smtp,omitempty" doc:"if SMTP outbound is allowed"` + SnapshotAvailable string `json:"snapshotavailable,omitempty" doc:"the total number of snapshots available for this account"` + SnapshotLimit string `json:"snapshotlimit,omitempty" doc:"the total number of snapshots which can be stored by this account"` + SnapshotTotal int64 `json:"snapshottotal,omitempty" doc:"the total number of snapshots stored by this account"` + State string `json:"state,omitempty" doc:"the state of the account"` + TemplateAvailable string `json:"templateavailable,omitempty" doc:"the total number of templates available to be created by this account"` + TemplateLimit string `json:"templatelimit,omitempty" doc:"the total number of templates which can be created by this account"` + TemplateTotal int64 `json:"templatetotal,omitempty" doc:"the total number of templates which have been created by this account"` + User []User `json:"user,omitempty" doc:"the list of users associated with account"` + VMAvailable string `json:"vmavailable,omitempty" doc:"the total number of virtual machines available for this account to acquire"` + VMLimit string `json:"vmlimit,omitempty" doc:"the total number of virtual machines that can be deployed by this account"` + VMRunning int `json:"vmrunning,omitempty" doc:"the total number of virtual machines running for this account"` + VMStopped int `json:"vmstopped,omitempty" doc:"the total number of virtual machines stopped for this account"` + VMTotal int64 `json:"vmtotal,omitempty" doc:"the total number of virtual machines deployed by this account"` + VolumeAvailable string `json:"volumeavailable,omitempty" doc:"the total volume available for this account"` + VolumeLimit string `json:"volumelimit,omitempty" doc:"the total volume which can be used by this account"` + VolumeTotal int64 `json:"volumetotal,omitempty" doc:"the total volume being used by this account"` +} + +// ListRequest builds the ListAccountsGroups request +func (a Account) ListRequest() (ListCommand, error) { + return &ListAccounts{ + ID: a.ID, + DomainID: a.DomainID, + AccountType: a.AccountType, + State: a.State, + }, nil } // ListAccounts represents a query to display the accounts -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAccounts.html type ListAccounts struct { - AccountType int64 `json:"accounttype,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IsCleanUpRequired bool `json:"iscleanuprequired,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` - State string `json:"state,omitempty"` + AccountType AccountType `json:"accounttype,omitempty" doc:"list accounts by account type. Valid account types are 1 (admin), 2 (domain-admin), and 0 (user)."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"list account by account ID"` + IsCleanUpRequired *bool `json:"iscleanuprequired,omitempty" doc:"list accounts by cleanuprequired attribute (values are true or false)"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"list account by account name"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + State string `json:"state,omitempty" doc:"list accounts by state. Valid states are enabled, disabled, and locked."` + _ bool `name:"listAccounts" description:"Lists accounts and provides detailed account information for listed accounts"` } -func (*ListAccounts) name() string { - return "listAccounts" -} - -func (*ListAccounts) response() interface{} { +func (ListAccounts) response() interface{} { return new(ListAccountsResponse) } +// SetPage sets the current page +func (ls *ListAccounts) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListAccounts) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListAccounts) each(resp interface{}, callback IterateItemFunc) { + vms, ok := resp.(*ListAccountsResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListAccountsResponse expected, got %T", resp)) + return + } + + for i := range vms.Account { + if !callback(&vms.Account[i], nil) { + break + } + } +} + // ListAccountsResponse represents a list of accounts type ListAccountsResponse struct { Count int `json:"count"` Account []Account `json:"account"` } + +// EnableAccount represents the activation of an account +type EnableAccount struct { + Account string `json:"account,omitempty" doc:"Enables specified account."` + DomainID *UUID `json:"domainid,omitempty" doc:"Enables specified account in this domain."` + ID *UUID `json:"id,omitempty" doc:"Account id"` + _ bool `name:"enableAccount" description:"Enables an account"` +} + +func (EnableAccount) response() interface{} { + return new(Account) +} + +// DisableAccount (Async) represents the deactivation of an account +type DisableAccount struct { + Lock *bool `json:"lock" doc:"If true, only lock the account; else disable the account"` + Account string `json:"account,omitempty" doc:"Disables specified account."` + DomainID *UUID `json:"domainid,omitempty" doc:"Disables specified account in this domain."` + ID *UUID `json:"id,omitempty" doc:"Account id"` + _ bool `name:"disableAccount" description:"Disables an account"` +} + +func (DisableAccount) response() interface{} { + return new(AsyncJobResult) +} + +func (DisableAccount) asyncResponse() interface{} { + return new(Account) +} diff --git a/vendor/github.com/exoscale/egoscale/accounttype_string.go b/vendor/github.com/exoscale/egoscale/accounttype_string.go new file mode 100644 index 000000000..7d622a16c --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/accounttype_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type AccountType"; DO NOT EDIT. + +package egoscale + +import "strconv" + +const _AccountType_name = "UserAccountAdminAccountDomainAdminAccount" + +var _AccountType_index = [...]uint8{0, 11, 23, 41} + +func (i AccountType) String() string { + if i < 0 || i >= AccountType(len(_AccountType_index)-1) { + return "AccountType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _AccountType_name[_AccountType_index[i]:_AccountType_index[i+1]] +} diff --git a/vendor/github.com/exoscale/egoscale/addresses.go b/vendor/github.com/exoscale/egoscale/addresses.go index 649c4acd8..46eaaebcc 100644 --- a/vendor/github.com/exoscale/egoscale/addresses.go +++ b/vendor/github.com/exoscale/egoscale/addresses.go @@ -1,144 +1,163 @@ package egoscale -import "net" +import ( + "context" + "fmt" + "net" +) // IPAddress represents an IP Address type IPAddress struct { - ID string `json:"id"` - Account string `json:"account,omitempty"` - AllocatedAt string `json:"allocated,omitempty"` - AssociatedNetworkID string `json:"associatednetworkid,omitempty"` - AssociatedNetworkName string `json:"associatednetworkname,omitempty"` - DomainID string `json:"domainid,omitempty"` - DomainName string `json:"domainname,omitempty"` - ForDisplay bool `json:"fordisplay,omitempty"` - ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty"` - IPAddress net.IP `json:"ipaddress"` - IsElastic bool `json:"iselastic,omitempty"` - IsPortable bool `json:"isportable,omitempty"` - IsSourceNat bool `json:"issourcenat,omitempty"` - IsSystem bool `json:"issystem,omitempty"` - NetworkID string `json:"networkid,omitempty"` - PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Purpose string `json:"purpose,omitempty"` - State string `json:"state,omitempty"` - VirtualMachineDisplayName string `json:"virtualmachinedisplayname,omitempty"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` - VirtualMachineName string `json:"virtualmachineName,omitempty"` - VlanID string `json:"vlanid,omitempty"` - VlanName string `json:"vlanname,omitempty"` - VMIPAddress net.IP `json:"vmipaddress,omitempty"` - VpcID string `json:"vpcid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` - ZoneName string `json:"zonename,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - JobID string `json:"jobid,omitempty"` - JobStatus JobStatusType `json:"jobstatus,omitempty"` + Account string `json:"account,omitempty" doc:"the account the public IP address is associated with"` + Allocated string `json:"allocated,omitempty" doc:"date the public IP address was acquired"` + Associated string `json:"associated,omitempty" doc:"date the public IP address was associated"` + AssociatedNetworkID *UUID `json:"associatednetworkid,omitempty" doc:"the ID of the Network associated with the IP address"` + AssociatedNetworkName string `json:"associatednetworkname,omitempty" doc:"the name of the Network associated with the IP address"` + Domain string `json:"domain,omitempty" doc:"the domain the public IP address is associated with"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID the public IP address is associated with"` + ForDisplay bool `json:"fordisplay,omitempty" doc:"is public ip for display to the regular user"` + ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"` + ID *UUID `json:"id,omitempty" doc:"public IP address id"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"public IP address"` + IsElastic bool `json:"iselastic,omitempty" doc:"is an elastic ip"` + IsPortable bool `json:"isportable,omitempty" doc:"is public IP portable across the zones"` + IsSourceNat bool `json:"issourcenat,omitempty" doc:"true if the IP address is a source nat address, false otherwise"` + IsStaticNat *bool `json:"isstaticnat,omitempty" doc:"true if this ip is for static nat, false otherwise"` + IsSystem bool `json:"issystem,omitempty" doc:"true if this ip is system ip (was allocated as a part of deployVm or createLbRule)"` + NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the Network where ip belongs to"` + PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"the physical network this belongs to"` + Purpose string `json:"purpose,omitempty" doc:"purpose of the IP address. In Acton this value is not null for Ips with isSystem=true, and can have either StaticNat or LB value"` + ReverseDNS []ReverseDNS `json:"reversedns,omitempty" doc:"the list of PTR record(s) associated with the ip address"` + State string `json:"state,omitempty" doc:"State of the ip address. Can be: Allocatin, Allocated and Releasing"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with ip address"` + VirtualMachineDisplayName string `json:"virtualmachinedisplayname,omitempty" doc:"virtual machine display name the ip address is assigned to (not null only for static nat Ip)"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"virtual machine id the ip address is assigned to (not null only for static nat Ip)"` + VirtualMachineName string `json:"virtualmachinename,omitempty" doc:"virtual machine name the ip address is assigned to (not null only for static nat Ip)"` + VlanID *UUID `json:"vlanid,omitempty" doc:"the ID of the VLAN associated with the IP address. This parameter is visible to ROOT admins only"` + VlanName string `json:"vlanname,omitempty" doc:"the VLAN associated with the IP address"` + VMIPAddress net.IP `json:"vmipaddress,omitempty" doc:"virtual machine (dnat) ip address (not null only for static nat Ip)"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the zone the public IP address belongs to"` + ZoneName string `json:"zonename,omitempty" doc:"the name of the zone the public IP address belongs to"` } // ResourceType returns the type of the resource -func (*IPAddress) ResourceType() string { +func (IPAddress) ResourceType() string { return "PublicIpAddress" } +// ListRequest builds the ListAdresses request +func (ipaddress IPAddress) ListRequest() (ListCommand, error) { + req := &ListPublicIPAddresses{ + Account: ipaddress.Account, + AssociatedNetworkID: ipaddress.AssociatedNetworkID, + DomainID: ipaddress.DomainID, + ID: ipaddress.ID, + IPAddress: ipaddress.IPAddress, + PhysicalNetworkID: ipaddress.PhysicalNetworkID, + VlanID: ipaddress.VlanID, + ZoneID: ipaddress.ZoneID, + } + if ipaddress.IsElastic { + req.IsElastic = &ipaddress.IsElastic + } + if ipaddress.IsSourceNat { + req.IsSourceNat = &ipaddress.IsSourceNat + } + if ipaddress.ForDisplay { + req.ForDisplay = &ipaddress.ForDisplay + } + if ipaddress.ForVirtualNetwork { + req.ForVirtualNetwork = &ipaddress.ForVirtualNetwork + } + + return req, nil +} + +// Delete removes the resource +func (ipaddress IPAddress) Delete(ctx context.Context, client *Client) error { + if ipaddress.ID == nil { + return fmt.Errorf("an IPAddress may only be deleted using ID") + } + + return client.BooleanRequestWithContext(ctx, &DisassociateIPAddress{ + ID: ipaddress.ID, + }) +} + // AssociateIPAddress (Async) represents the IP creation -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/associateIpAddress.html type AssociateIPAddress struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ForDisplay bool `json:"fordisplay,omitempty"` - IsPortable bool `json:"isportable,omitempty"` - NetworkdID string `json:"networkid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - RegionID string `json:"regionid,omitempty"` - VpcID string `json:"vpcid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` + Account string `json:"account,omitempty" doc:"the account to associate with this IP address"` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain to associate with this IP address"` + ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"` + IsPortable *bool `json:"isportable,omitempty" doc:"should be set to true if public IP is required to be transferable across zones, if not specified defaults to false"` + NetworkdID *UUID `json:"networkid,omitempty" doc:"The network this ip address should be associated to."` + RegionID int `json:"regionid,omitempty" doc:"region ID from where portable ip is to be associated."` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the availability zone you want to acquire an public IP address from"` + _ bool `name:"associateIpAddress" description:"Acquires and associates a public IP to an account."` } -func (*AssociateIPAddress) name() string { - return "associateIpAddress" +func (AssociateIPAddress) response() interface{} { + return new(AsyncJobResult) } -func (*AssociateIPAddress) asyncResponse() interface{} { - return new(AssociateIPAddressResponse) -} - -// AssociateIPAddressResponse represents the response to the creation of an IPAddress -type AssociateIPAddressResponse struct { - IPAddress IPAddress `json:"ipaddress"` +func (AssociateIPAddress) asyncResponse() interface{} { + return new(IPAddress) } // DisassociateIPAddress (Async) represents the IP deletion -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/disassociateIpAddress.html type DisassociateIPAddress struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"the id of the public ip address to disassociate"` + _ bool `name:"disassociateIpAddress" description:"Disassociates an ip address from the account."` } -func (*DisassociateIPAddress) name() string { - return "disassociateIpAddress" +func (DisassociateIPAddress) response() interface{} { + return new(AsyncJobResult) } -func (*DisassociateIPAddress) asyncResponse() interface{} { - return new(booleanAsyncResponse) + +func (DisassociateIPAddress) asyncResponse() interface{} { + return new(booleanResponse) } // UpdateIPAddress (Async) represents the IP modification -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/updateIpAddress.html type UpdateIPAddress struct { - ID string `json:"id"` - CustomID string `json:"customid,omitempty"` // root only - ForDisplay bool `json:"fordisplay,omitempty"` + ID *UUID `json:"id" doc:"the id of the public ip address to update"` + CustomID *UUID `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"` + ForDisplay *bool `json:"fordisplay,omitempty" doc:"an optional field, whether to the display the ip to the end user or not"` + _ bool `name:"updateIpAddress" description:"Updates an ip address"` } -func (*UpdateIPAddress) name() string { - return "updateIpAddress" -} -func (*UpdateIPAddress) asyncResponse() interface{} { - return new(UpdateIPAddressResponse) +func (UpdateIPAddress) response() interface{} { + return new(AsyncJobResult) } -// UpdateIPAddressResponse represents the modified IP Address -type UpdateIPAddressResponse AssociateIPAddressResponse +func (UpdateIPAddress) asyncResponse() interface{} { + return new(IPAddress) +} // ListPublicIPAddresses represents a search for public IP addresses -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listPublicIpAddresses.html type ListPublicIPAddresses struct { - Account string `json:"account,omitempty"` - AllocatedOnly bool `json:"allocatedonly,omitempty"` - AllocatedNetworkID string `json:"allocatednetworkid,omitempty"` - DomainID string `json:"domainid,omitempty"` - ForDisplay bool `json:"fordisplay,omitempty"` - ForLoadBalancing bool `json:"forloadbalancing,omitempty"` - ForVirtualNetwork string `json:"forvirtualnetwork,omitempty"` - ID string `json:"id,omitempty"` - IPAddress net.IP `json:"ipaddress,omitempty"` - IsElastic bool `json:"iselastic,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - IsSourceNat bool `json:"issourcenat,omitempty"` - IsStaticNat bool `json:"isstaticnat,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omiempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` - PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - VlanID string `json:"vlanid,omitempty"` - VpcID string `json:"vpcid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListPublicIPAddresses) name() string { - return "listPublicIpAddresses" -} - -func (*ListPublicIPAddresses) response() interface{} { - return new(ListPublicIPAddressesResponse) + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + AllocatedOnly *bool `json:"allocatedonly,omitempty" doc:"limits search results to allocated public IP addresses"` + AssociatedNetworkID *UUID `json:"associatednetworkid,omitempty" doc:"lists all public IP addresses associated to the network specified"` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ForDisplay *bool `json:"fordisplay,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"` + ForLoadBalancing *bool `json:"forloadbalancing,omitempty" doc:"list only ips used for load balancing"` + ForVirtualNetwork *bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the IP address"` + ID *UUID `json:"id,omitempty" doc:"lists ip address by id"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"lists the specified IP address"` + IsElastic *bool `json:"iselastic,omitempty" doc:"list only elastic ip addresses"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + IsSourceNat *bool `json:"issourcenat,omitempty" doc:"list only source nat ip addresses"` + IsStaticNat *bool `json:"isstaticnat,omitempty" doc:"list only static nat ip addresses"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"lists all public IP addresses by physical network id"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + VlanID *UUID `json:"vlanid,omitempty" doc:"lists all public IP addresses by VLAN ID"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"lists all public IP addresses by Zone ID"` + _ bool `name:"listPublicIpAddresses" description:"Lists all public ip addresses"` } // ListPublicIPAddressesResponse represents a list of public IP addresses @@ -146,3 +165,31 @@ type ListPublicIPAddressesResponse struct { Count int `json:"count"` PublicIPAddress []IPAddress `json:"publicipaddress"` } + +func (ListPublicIPAddresses) response() interface{} { + return new(ListPublicIPAddressesResponse) +} + +// SetPage sets the current page +func (ls *ListPublicIPAddresses) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListPublicIPAddresses) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListPublicIPAddresses) each(resp interface{}, callback IterateItemFunc) { + ips, ok := resp.(*ListPublicIPAddressesResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListPublicIPAddressesResponse expected, got %T", resp)) + return + } + + for i := range ips.PublicIPAddress { + if !callback(&ips.PublicIPAddress[i], nil) { + break + } + } +} diff --git a/vendor/github.com/exoscale/egoscale/affinity_groups.go b/vendor/github.com/exoscale/egoscale/affinity_groups.go index eed628ce9..7ba165e2f 100644 --- a/vendor/github.com/exoscale/egoscale/affinity_groups.go +++ b/vendor/github.com/exoscale/egoscale/affinity_groups.go @@ -1,68 +1,86 @@ package egoscale import ( + "context" + "fmt" "net/url" ) // AffinityGroup represents an (anti-)affinity group +// +// Affinity and Anti-Affinity groups provide a way to influence where VMs should run. +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#affinity-groups type AffinityGroup struct { - ID string `json:"id,omitempty"` - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - VirtualMachineIDs []string `json:"virtualmachineIDs,omitempty"` // *I*ds is not a typo + Account string `json:"account,omitempty" doc:"the account owning the affinity group"` + Description string `json:"description,omitempty" doc:"the description of the affinity group"` + Domain string `json:"domain,omitempty" doc:"the domain name of the affinity group"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of the affinity group"` + ID *UUID `json:"id,omitempty" doc:"the ID of the affinity group"` + Name string `json:"name,omitempty" doc:"the name of the affinity group"` + Type string `json:"type,omitempty" doc:"the type of the affinity group"` + VirtualMachineIDs []string `json:"virtualmachineIds,omitempty" doc:"virtual machine Ids associated with this affinity group"` +} + +// ListRequest builds the ListAffinityGroups request +func (ag AffinityGroup) ListRequest() (ListCommand, error) { + return &ListAffinityGroups{ + ID: ag.ID, + Name: ag.Name, + }, nil +} + +// Delete removes the given Affinity Group +func (ag AffinityGroup) Delete(ctx context.Context, client *Client) error { + if ag.ID == nil && ag.Name == "" { + return fmt.Errorf("an Affinity Group may only be deleted using ID or Name") + } + + req := &DeleteAffinityGroup{ + Account: ag.Account, + DomainID: ag.DomainID, + } + + if ag.ID != nil { + req.ID = ag.ID + } else { + req.Name = ag.Name + } + + return client.BooleanRequestWithContext(ctx, req) } // AffinityGroupType represent an affinity group type type AffinityGroupType struct { - Type string `json:"type"` + Type string `json:"type,omitempty" doc:"the type of the affinity group"` } // CreateAffinityGroup (Async) represents a new (anti-)affinity group -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createAffinityGroup.html type CreateAffinityGroup struct { - Name string `json:"name"` - Type string `json:"type"` - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - DomainID string `json:"domainid,omitempty"` + Account string `json:"account,omitempty" doc:"an account for the affinity group. Must be used with domainId."` + Description string `json:"description,omitempty" doc:"optional description of the affinity group"` + DomainID *UUID `json:"domainid,omitempty" doc:"domainId of the account owning the affinity group"` + Name string `json:"name" doc:"name of the affinity group"` + Type string `json:"type" doc:"Type of the affinity group from the available affinity/anti-affinity group types"` + _ bool `name:"createAffinityGroup" description:"Creates an affinity/anti-affinity group"` } -func (*CreateAffinityGroup) name() string { - return "createAffinityGroup" +func (CreateAffinityGroup) response() interface{} { + return new(AsyncJobResult) } -func (*CreateAffinityGroup) asyncResponse() interface{} { - return new(CreateAffinityGroupResponse) -} - -// CreateAffinityGroupResponse represents the response of the creation of an (anti-)affinity group -type CreateAffinityGroupResponse struct { - AffinityGroup AffinityGroup `json:"affinitygroup"` +func (CreateAffinityGroup) asyncResponse() interface{} { + return new(AffinityGroup) } // UpdateVMAffinityGroup (Async) represents a modification of a (anti-)affinity group -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateVMAffinityGroup.html type UpdateVMAffinityGroup struct { - ID string `json:"id"` - AffinityGroupIDs []string `json:"affinitygroupids,omitempty"` // mutually exclusive with names - AffinityGroupNames []string `json:"affinitygroupnames,omitempty"` // mutually exclusive with ids + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + AffinityGroupIDs []UUID `json:"affinitygroupids,omitempty" doc:"comma separated list of affinity groups id that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter"` + AffinityGroupNames []string `json:"affinitygroupnames,omitempty" doc:"comma separated list of affinity groups names that are going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter"` + _ bool `name:"updateVMAffinityGroup" description:"Updates the affinity/anti-affinity group associations of a virtual machine. The VM has to be stopped and restarted for the new properties to take effect."` } -func (*UpdateVMAffinityGroup) name() string { - return "updateVMAffinityGroup" -} - -func (*UpdateVMAffinityGroup) asyncResponse() interface{} { - return new(UpdateVMAffinityGroupResponse) -} - -func (req *UpdateVMAffinityGroup) onBeforeSend(params *url.Values) error { +func (req UpdateVMAffinityGroup) onBeforeSend(params url.Values) error { // Either AffinityGroupIDs or AffinityGroupNames must be set if len(req.AffinityGroupIDs) == 0 && len(req.AffinityGroupNames) == 0 { params.Set("affinitygroupids", "") @@ -70,69 +88,73 @@ func (req *UpdateVMAffinityGroup) onBeforeSend(params *url.Values) error { return nil } -// UpdateVMAffinityGroupResponse represents the new VM -type UpdateVMAffinityGroupResponse VirtualMachineResponse +func (UpdateVMAffinityGroup) response() interface{} { + return new(AsyncJobResult) +} + +func (UpdateVMAffinityGroup) asyncResponse() interface{} { + return new(VirtualMachine) +} // DeleteAffinityGroup (Async) represents an (anti-)affinity group to be deleted -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteAffinityGroup.html type DeleteAffinityGroup struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - DomainID string `json:"domainid,omitempty"` + Account string `json:"account,omitempty" doc:"the account of the affinity group. Must be specified with domain ID"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of account owning the affinity group"` + ID *UUID `json:"id,omitempty" doc:"The ID of the affinity group. Mutually exclusive with name parameter"` + Name string `json:"name,omitempty" doc:"The name of the affinity group. Mutually exclusive with ID parameter"` + _ bool `name:"deleteAffinityGroup" description:"Deletes affinity group"` } -func (*DeleteAffinityGroup) name() string { - return "deleteAffinityGroup" +func (DeleteAffinityGroup) response() interface{} { + return new(AsyncJobResult) } -func (*DeleteAffinityGroup) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (DeleteAffinityGroup) asyncResponse() interface{} { + return new(booleanResponse) } // ListAffinityGroups represents an (anti-)affinity groups search -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAffinityGroups.html type ListAffinityGroups struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Name string `json:"name,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"list the affinity group by the ID provided"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"lists affinity groups by name"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - Type string `json:"type,omitempty"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` + Type string `json:"type,omitempty" doc:"lists affinity groups by type"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"lists affinity groups by virtual machine ID"` + _ bool `name:"listAffinityGroups" description:"Lists affinity groups"` } -func (*ListAffinityGroups) name() string { - return "listAffinityGroups" -} - -func (*ListAffinityGroups) response() interface{} { +func (ListAffinityGroups) response() interface{} { return new(ListAffinityGroupsResponse) } -// ListAffinityGroupTypes represents an (anti-)affinity groups search -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listAffinityGroupTypes.html -type ListAffinityGroupTypes struct { - Keyword string `json:"keyword,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` +// SetPage sets the current page +func (ls *ListAffinityGroups) SetPage(page int) { + ls.Page = page } -func (*ListAffinityGroupTypes) name() string { - return "listAffinityGroupTypes" +// SetPageSize sets the page size +func (ls *ListAffinityGroups) SetPageSize(pageSize int) { + ls.PageSize = pageSize } -func (*ListAffinityGroupTypes) response() interface{} { - return new(ListAffinityGroupTypesResponse) +func (ListAffinityGroups) each(resp interface{}, callback IterateItemFunc) { + vms, ok := resp.(*ListAffinityGroupsResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListAffinityGroupsResponse expected, got %T", resp)) + return + } + + for i := range vms.AffinityGroup { + if !callback(&vms.AffinityGroup[i], nil) { + break + } + } } // ListAffinityGroupsResponse represents a list of (anti-)affinity groups @@ -141,36 +163,20 @@ type ListAffinityGroupsResponse struct { AffinityGroup []AffinityGroup `json:"affinitygroup"` } +// ListAffinityGroupTypes represents an (anti-)affinity groups search +type ListAffinityGroupTypes struct { + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + _ bool `name:"listAffinityGroupTypes" description:"Lists affinity group types available"` +} + +func (ListAffinityGroupTypes) response() interface{} { + return new(ListAffinityGroupTypesResponse) +} + // ListAffinityGroupTypesResponse represents a list of (anti-)affinity group types type ListAffinityGroupTypesResponse struct { Count int `json:"count"` AffinityGroupType []AffinityGroupType `json:"affinitygrouptype"` } - -// Legacy methods - -// CreateAffinityGroup creates a group -// -// Deprecated: Use the API directly -func (exo *Client) CreateAffinityGroup(name string, async AsyncInfo) (*AffinityGroup, error) { - req := &CreateAffinityGroup{ - Name: name, - } - resp, err := exo.AsyncRequest(req, async) - if err != nil { - return nil, err - } - - ag := resp.(*CreateAffinityGroupResponse).AffinityGroup - return &ag, nil -} - -// DeleteAffinityGroup deletes a group -// -// Deprecated: Use the API directly -func (exo *Client) DeleteAffinityGroup(name string, async AsyncInfo) error { - req := &DeleteAffinityGroup{ - Name: name, - } - return exo.BooleanAsyncRequest(req, async) -} diff --git a/vendor/github.com/exoscale/egoscale/apis.go b/vendor/github.com/exoscale/egoscale/apis.go index 18083d1a1..a5213c9f2 100644 --- a/vendor/github.com/exoscale/egoscale/apis.go +++ b/vendor/github.com/exoscale/egoscale/apis.go @@ -2,14 +2,14 @@ package egoscale // API represents an API service type API struct { - Description string `json:"description"` - IsAsync bool `json:"isasync"` - Name string `json:"name"` - Related string `json:"related"` // comma separated - Since string `json:"since"` - Type string `json:"type"` - Params []APIParam `json:"params"` - Response []APIResponse `json:"responses"` + Description string `json:"description,omitempty" doc:"description of the api"` + IsAsync bool `json:"isasync,omitempty" doc:"true if api is asynchronous"` + Name string `json:"name,omitempty" doc:"the name of the api command"` + Related string `json:"related,omitempty" doc:"comma separated related apis"` + Since string `json:"since,omitempty" doc:"version of CloudStack the api was introduced in"` + Type string `json:"type,omitempty" doc:"response field type"` + Params []APIParam `json:"params,omitempty" doc:"the list params the api accepts"` + Response []APIField `json:"response,omitempty" doc:"api response fields"` } // APIParam represents an API parameter field @@ -17,32 +17,23 @@ type APIParam struct { Description string `json:"description"` Length int64 `json:"length"` Name string `json:"name"` - Related string `json:"related"` // comma separated - Since string `json:"since"` + Required bool `json:"required"` + Since string `json:"since,omitempty"` Type string `json:"type"` } -// APIResponse represents an API response field -type APIResponse struct { - Description string `json:"description"` - Name string `json:"name"` - Response []APIResponse `json:"response"` - Type string `json:"type"` +// APIField represents an API response field +type APIField struct { + Description string `json:"description"` + Name string `json:"name"` + Response []APIField `json:"response,omitempty"` + Type string `json:"type"` } // ListAPIs represents a query to list the api -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listApis.html type ListAPIs struct { - Name string `json:"name,omitempty"` -} - -func (*ListAPIs) name() string { - return "listApis" -} - -func (*ListAPIs) response() interface{} { - return new(ListAPIsResponse) + Name string `json:"name,omitempty" doc:"API name"` + _ bool `name:"listApis" description:"lists all available apis on the server"` } // ListAPIsResponse represents a list of API @@ -50,3 +41,7 @@ type ListAPIsResponse struct { Count int `json:"count"` API []API `json:"api"` } + +func (*ListAPIs) response() interface{} { + return new(ListAPIsResponse) +} diff --git a/vendor/github.com/exoscale/egoscale/async_jobs.go b/vendor/github.com/exoscale/egoscale/async_jobs.go index 906fd2f38..d6cdf78d1 100644 --- a/vendor/github.com/exoscale/egoscale/async_jobs.go +++ b/vendor/github.com/exoscale/egoscale/async_jobs.go @@ -2,61 +2,54 @@ package egoscale import ( "encoding/json" + "errors" ) // AsyncJobResult represents an asynchronous job result type AsyncJobResult struct { - AccountID string `json:"accountid"` - Cmd string `json:"cmd"` - Created string `json:"created"` - JobInstanceID string `json:"jobinstanceid"` - JobInstanceType string `json:"jobinstancetype"` - JobProcStatus int `json:"jobprocstatus"` - JobResult *json.RawMessage `json:"jobresult"` - JobResultCode int `json:"jobresultcode"` - JobResultType string `json:"jobresulttype"` - JobStatus JobStatusType `json:"jobstatus"` - UserID string `json:"userid"` - JobID string `json:"jobid"` + AccountID *UUID `json:"accountid,omitempty" doc:"the account that executed the async command"` + Cmd string `json:"cmd,omitempty" doc:"the async command executed"` + Created string `json:"created,omitempty" doc:"the created date of the job"` + JobID *UUID `json:"jobid,omitempty" doc:"extra field for the initial async call"` + JobInstanceID *UUID `json:"jobinstanceid,omitempty" doc:"the unique ID of the instance/entity object related to the job"` + JobInstanceType string `json:"jobinstancetype,omitempty" doc:"the instance/entity object related to the job"` + JobProcStatus int `json:"jobprocstatus,omitempty" doc:"the progress information of the PENDING job"` + JobResult *json.RawMessage `json:"jobresult,omitempty" doc:"the result reason"` + JobResultCode int `json:"jobresultcode,omitempty" doc:"the result code for the job"` + JobResultType string `json:"jobresulttype,omitempty" doc:"the result type"` + JobStatus JobStatusType `json:"jobstatus,omitempty" doc:"the current job status-should be 0 for PENDING"` + UserID *UUID `json:"userid,omitempty" doc:"the user that executed the async command"` +} + +func (a AsyncJobResult) Error() error { + r := new(ErrorResponse) + if e := json.Unmarshal(*a.JobResult, r); e != nil { + return e + } + return r } // QueryAsyncJobResult represents a query to fetch the status of async job -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/queryAsyncJobResult.html type QueryAsyncJobResult struct { - JobID string `json:"jobid"` + JobID *UUID `json:"jobid" doc:"the ID of the asynchronous job"` + _ bool `name:"queryAsyncJobResult" description:"Retrieves the current status of asynchronous job."` } -func (*QueryAsyncJobResult) name() string { - return "queryAsyncJobResult" +func (QueryAsyncJobResult) response() interface{} { + return new(AsyncJobResult) } -func (*QueryAsyncJobResult) response() interface{} { - return new(QueryAsyncJobResultResponse) -} - -// QueryAsyncJobResultResponse represents the current status of an asynchronous job -type QueryAsyncJobResultResponse AsyncJobResult - // ListAsyncJobs list the asynchronous jobs -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listAsyncJobs.html type ListAsyncJobs struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - StartDate string `json:"startdate,omitempty"` -} - -func (*ListAsyncJobs) name() string { - return "listAsyncJobs" -} - -func (*ListAsyncJobs) response() interface{} { - return new(ListAsyncJobsResponse) + StartDate string `json:"startdate,omitempty" doc:"the start date of the async job"` + _ bool `name:"listAsyncJobs" description:"Lists all pending asynchronous jobs for the account."` } // ListAsyncJobsResponse represents a list of job results @@ -64,3 +57,35 @@ type ListAsyncJobsResponse struct { Count int `json:"count"` AsyncJobs []AsyncJobResult `json:"asyncjobs"` } + +func (ListAsyncJobs) response() interface{} { + return new(ListAsyncJobsResponse) +} + +// Result unmarshals the result of an AsyncJobResult into the given interface +func (a AsyncJobResult) Result(i interface{}) error { + if a.JobStatus == Failure { + return a.Error() + } + + if a.JobStatus == Success { + m := map[string]json.RawMessage{} + err := json.Unmarshal(*(a.JobResult), &m) + + if err == nil { + if len(m) >= 1 { + if _, ok := m["success"]; ok { + return json.Unmarshal(*(a.JobResult), i) + } + + // otherwise, pick the first key + for k := range m { + return json.Unmarshal(m[k], i) + } + } + return errors.New("empty response") + } + } + + return nil +} diff --git a/vendor/github.com/exoscale/egoscale/cidr.go b/vendor/github.com/exoscale/egoscale/cidr.go new file mode 100644 index 000000000..74c054b71 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/cidr.go @@ -0,0 +1,62 @@ +package egoscale + +import ( + "bytes" + "encoding/json" + "fmt" + "net" +) + +// CIDR represents a nicely JSON serializable net.IPNet +type CIDR struct { + net.IPNet +} + +// UnmarshalJSON unmarshals the raw JSON into the MAC address +func (cidr *CIDR) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + c, err := ParseCIDR(s) + if err != nil { + return err + } + *cidr = CIDR{c.IPNet} + + return nil +} + +// MarshalJSON converts the CIDR to a string representation +func (cidr CIDR) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", cidr)), nil +} + +// String returns the string representation of a CIDR +func (cidr CIDR) String() string { + return cidr.IPNet.String() +} + +// ParseCIDR parses a CIDR from a string +func ParseCIDR(s string) (*CIDR, error) { + _, net, err := net.ParseCIDR(s) + if err != nil { + return nil, err + } + return &CIDR{*net}, nil +} + +// MustParseCIDR forces parseCIDR or panics +func MustParseCIDR(s string) *CIDR { + cidr, err := ParseCIDR(s) + if err != nil { + panic(err) + } + + return cidr +} + +// Equal compare two CIDR +func (cidr CIDR) Equal(c CIDR) bool { + return (cidr.IPNet.IP.Equal(c.IPNet.IP) && bytes.Equal(cidr.IPNet.Mask, c.IPNet.Mask)) +} diff --git a/vendor/github.com/exoscale/egoscale/client.go b/vendor/github.com/exoscale/egoscale/client.go new file mode 100644 index 000000000..eaf102e56 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/client.go @@ -0,0 +1,385 @@ +package egoscale + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httputil" + "os" + "reflect" + "strings" + "time" +) + +// Taggable represents a resource which can have tags attached +// +// This is a helper to fill the resourcetype of a CreateTags call +type Taggable interface { + // CloudStack resource type of the Taggable type + ResourceType() string +} + +// Deletable represents an Interface that can be "Delete" by the client +type Deletable interface { + // Delete removes the given resource(s) or throws + Delete(context context.Context, client *Client) error +} + +// Listable represents an Interface that can be "List" by the client +type Listable interface { + // ListRequest builds the list command + ListRequest() (ListCommand, error) +} + +// Gettable represents an Interface that can be "Get" by the client +type Gettable interface { + Listable +} + +// Client represents the CloudStack API client +type Client struct { + // HTTPClient holds the HTTP client + HTTPClient *http.Client + // Endpoints is CloudStack API + Endpoint string + // APIKey is the API identifier + APIKey string + // apisecret is the API secret, hence non exposed + apiSecret string + // PageSize represents the default size for a paginated result + PageSize int + // Timeout represents the default timeout for the async requests + Timeout time.Duration + // RetryStrategy represents the waiting strategy for polling the async requests + RetryStrategy RetryStrategyFunc + // Logger contains any log, plug your own + Logger *log.Logger +} + +// RetryStrategyFunc represents a how much time to wait between two calls to CloudStack +type RetryStrategyFunc func(int64) time.Duration + +// IterateItemFunc represents the callback to iterate a list of results, if false stops +type IterateItemFunc func(interface{}, error) bool + +// WaitAsyncJobResultFunc represents the callback to wait a results of an async request, if false stops +type WaitAsyncJobResultFunc func(*AsyncJobResult, error) bool + +// NewClient creates a CloudStack API client with default timeout (60) +// +// Timeout is set to both the HTTP client and the client itself. +func NewClient(endpoint, apiKey, apiSecret string) *Client { + timeout := 60 * time.Second + + httpClient := &http.Client{ + Transport: http.DefaultTransport, + } + + client := &Client{ + HTTPClient: httpClient, + Endpoint: endpoint, + APIKey: apiKey, + apiSecret: apiSecret, + PageSize: 50, + Timeout: timeout, + RetryStrategy: MonotonicRetryStrategyFunc(2), + Logger: log.New(ioutil.Discard, "", 0), + } + + if prefix, ok := os.LookupEnv("EXOSCALE_TRACE"); ok { + client.Logger = log.New(os.Stderr, prefix, log.LstdFlags) + client.TraceOn() + } + + return client +} + +// Get populates the given resource or fails +func (client *Client) Get(g Gettable) error { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + return client.GetWithContext(ctx, g) +} + +// GetWithContext populates the given resource or fails +func (client *Client) GetWithContext(ctx context.Context, g Gettable) error { + gs, err := client.ListWithContext(ctx, g) + if err != nil { + return err + } + + count := len(gs) + if count != 1 { + req, err := g.ListRequest() + if err != nil { + return err + } + params, err := client.Payload(req) + if err != nil { + return err + } + + // formatting the query string nicely + payload := params.Encode() + payload = strings.Replace(payload, "&", ", ", -1) + + if count == 0 { + return &ErrorResponse{ + ErrorCode: ParamError, + ErrorText: fmt.Sprintf("not found, query: %s", payload), + } + } + return fmt.Errorf("more than one element found: %s", payload) + } + + return Copy(g, gs[0]) +} + +// Delete removes the given resource of fails +func (client *Client) Delete(g Deletable) error { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + return client.DeleteWithContext(ctx, g) +} + +// DeleteWithContext removes the given resource of fails +func (client *Client) DeleteWithContext(ctx context.Context, g Deletable) error { + return g.Delete(ctx, client) +} + +// List lists the given resource (and paginate till the end) +func (client *Client) List(g Listable) ([]interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + return client.ListWithContext(ctx, g) +} + +// ListWithContext lists the given resources (and paginate till the end) +func (client *Client) ListWithContext(ctx context.Context, g Listable) ([]interface{}, error) { + s := make([]interface{}, 0) + + if g == nil || reflect.ValueOf(g).IsNil() { + return s, fmt.Errorf("g Listable shouldn't be nil, got %#v", g) + } + + req, err := g.ListRequest() + if err != nil { + return s, err + } + + client.PaginateWithContext(ctx, req, func(item interface{}, e error) bool { + if item != nil { + s = append(s, item) + return true + } + err = e + return false + }) + + return s, err +} + +// AsyncListWithContext lists the given resources (and paginate till the end) +// +// +// // NB: goroutine may leak if not read until the end. Create a proper context! +// ctx, cancel := context.WithCancel(context.Background()) +// defer cancel() +// +// outChan, errChan := client.AsyncListWithContext(ctx, new(egoscale.VirtualMachine)) +// +// for { +// select { +// case i, ok := <- outChan: +// if ok { +// vm := i.(egoscale.VirtualMachine) +// // ... +// } else { +// outChan = nil +// } +// case err, ok := <- errChan: +// if ok { +// // do something +// } +// // Once an error has been received, you can expect the channels to be closed. +// errChan = nil +// } +// if errChan == nil && outChan == nil { +// break +// } +// } +// +func (client *Client) AsyncListWithContext(ctx context.Context, g Listable) (<-chan interface{}, <-chan error) { + outChan := make(chan interface{}, client.PageSize) + errChan := make(chan error) + + go func() { + defer close(outChan) + defer close(errChan) + + req, err := g.ListRequest() + if err != nil { + errChan <- err + return + } + + client.PaginateWithContext(ctx, req, func(item interface{}, e error) bool { + if item != nil { + outChan <- item + return true + } + errChan <- e + return false + }) + }() + + return outChan, errChan +} + +// Paginate runs the ListCommand and paginates +func (client *Client) Paginate(req ListCommand, callback IterateItemFunc) { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + client.PaginateWithContext(ctx, req, callback) +} + +// PaginateWithContext runs the ListCommand as long as the ctx is valid +func (client *Client) PaginateWithContext(ctx context.Context, req ListCommand, callback IterateItemFunc) { + pageSize := client.PageSize + + page := 1 + + for { + req.SetPage(page) + req.SetPageSize(pageSize) + resp, err := client.RequestWithContext(ctx, req) + if err != nil { + callback(nil, err) + break + } + + size := 0 + didErr := false + req.each(resp, func(element interface{}, err error) bool { + // If the context was cancelled, kill it in flight + if e := ctx.Err(); e != nil { + element = nil + err = e + } + + if callback(element, err) { + size++ + return true + } + + didErr = true + return false + }) + + if size < pageSize || didErr { + break + } + + page++ + } +} + +// APIName returns the CloudStack name of the given command +func (client *Client) APIName(command Command) string { + // This is due to a limitation of Go<=1.7 + if _, ok := command.(*AuthorizeSecurityGroupEgress); ok { + return "authorizeSecurityGroupEgress" + } + + info, err := info(command) + if err != nil { + panic(err) + } + return info.Name +} + +// APIDescription returns the description of the given CloudStack command +func (client *Client) APIDescription(command Command) string { + info, err := info(command) + if err != nil { + return "*missing description*" + } + return info.Description +} + +// Response returns the response structure of the given command +func (client *Client) Response(command Command) interface{} { + switch command.(type) { + case AsyncCommand: + return (command.(AsyncCommand)).asyncResponse() + default: + return command.response() + } +} + +// TraceOn activates the HTTP tracer +func (client *Client) TraceOn() { + if _, ok := client.HTTPClient.Transport.(*traceTransport); !ok { + client.HTTPClient.Transport = &traceTransport{ + transport: client.HTTPClient.Transport, + logger: client.Logger, + } + } +} + +// TraceOff deactivates the HTTP tracer +func (client *Client) TraceOff() { + if rt, ok := client.HTTPClient.Transport.(*traceTransport); ok { + client.HTTPClient.Transport = rt.transport + } +} + +// traceTransport contains the original HTTP transport to enable it to be reverted +type traceTransport struct { + transport http.RoundTripper + logger *log.Logger +} + +// RoundTrip executes a single HTTP transaction +func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) { + if dump, err := httputil.DumpRequest(req, true); err == nil { + t.logger.Printf("%s", dump) + } + + resp, err := t.transport.RoundTrip(req) + if err != nil { + return nil, err + } + + if dump, err := httputil.DumpResponse(resp, true); err == nil { + t.logger.Printf("%s", dump) + } + + return resp, nil +} + +// MonotonicRetryStrategyFunc returns a function that waits for n seconds for each iteration +func MonotonicRetryStrategyFunc(seconds int) RetryStrategyFunc { + return func(iteration int64) time.Duration { + return time.Duration(seconds) * time.Second + } +} + +// FibonacciRetryStrategy waits for an increasing amount of time following the Fibonacci sequence +func FibonacciRetryStrategy(iteration int64) time.Duration { + var a, b, i, tmp int64 + a = 0 + b = 1 + for i = 0; i < iteration; i++ { + tmp = a + b + a = b + b = tmp + } + return time.Duration(a) * time.Second +} diff --git a/vendor/github.com/exoscale/egoscale/copier.go b/vendor/github.com/exoscale/egoscale/copier.go new file mode 100644 index 000000000..adeb981e7 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/copier.go @@ -0,0 +1,37 @@ +package egoscale + +import ( + "fmt" + "reflect" +) + +// Copy copies the value from from into to. The type of "from" must be convertible into the type of "to". +func Copy(to, from interface{}) error { + tt := reflect.TypeOf(to) + tv := reflect.ValueOf(to) + + ft := reflect.TypeOf(from) + fv := reflect.ValueOf(from) + + if tt.Kind() != reflect.Ptr { + return fmt.Errorf("must copy to a pointer, got %q", tt.Name()) + } + + tt = tt.Elem() + tv = tv.Elem() + + for { + if ft.ConvertibleTo(tt) { + break + } + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + fv = fv.Elem() + } else { + return fmt.Errorf("cannot convert %q into %q", tt.Name(), ft.Name()) + } + } + + tv.Set(fv.Convert(tt)) + return nil +} diff --git a/vendor/github.com/exoscale/egoscale/cserrorcode_string.go b/vendor/github.com/exoscale/egoscale/cserrorcode_string.go new file mode 100644 index 000000000..4711d5425 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/cserrorcode_string.go @@ -0,0 +1,47 @@ +// Code generated by "stringer -type CSErrorCode"; DO NOT EDIT. + +package egoscale + +import "strconv" + +const _CSErrorCode_name = "CloudRuntimeExceptionExecutionExceptionHypervisorVersionChangedExceptionCloudExceptionAccountLimitExceptionAgentUnavailableExceptionCloudAuthenticationExceptionConcurrentOperationExceptionConflictingNetworkSettingsExceptionDiscoveredWithErrorExceptionHAStateExceptionInsufficientAddressCapacityExceptionInsufficientCapacityExceptionInsufficientNetworkCapacityExceptionInsufficientServerCapacityExceptionInsufficientStorageCapacityExceptionInternalErrorExceptionInvalidParameterValueExceptionManagementServerExceptionNetworkRuleConflictExceptionPermissionDeniedExceptionResourceAllocationExceptionResourceInUseExceptionResourceUnavailableExceptionStorageUnavailableExceptionUnsupportedServiceExceptionVirtualMachineMigrationExceptionAsyncCommandQueuedRequestLimitExceptionServerAPIException" + +var _CSErrorCode_map = map[CSErrorCode]string{ + 4250: _CSErrorCode_name[0:21], + 4260: _CSErrorCode_name[21:39], + 4265: _CSErrorCode_name[39:72], + 4275: _CSErrorCode_name[72:86], + 4280: _CSErrorCode_name[86:107], + 4285: _CSErrorCode_name[107:132], + 4290: _CSErrorCode_name[132:160], + 4300: _CSErrorCode_name[160:188], + 4305: _CSErrorCode_name[188:223], + 4310: _CSErrorCode_name[223:251], + 4315: _CSErrorCode_name[251:267], + 4320: _CSErrorCode_name[267:303], + 4325: _CSErrorCode_name[303:332], + 4330: _CSErrorCode_name[332:368], + 4335: _CSErrorCode_name[368:403], + 4340: _CSErrorCode_name[403:439], + 4345: _CSErrorCode_name[439:461], + 4350: _CSErrorCode_name[461:491], + 4355: _CSErrorCode_name[491:516], + 4360: _CSErrorCode_name[516:544], + 4365: _CSErrorCode_name[544:569], + 4370: _CSErrorCode_name[569:596], + 4375: _CSErrorCode_name[596:618], + 4380: _CSErrorCode_name[618:646], + 4385: _CSErrorCode_name[646:673], + 4390: _CSErrorCode_name[673:700], + 4395: _CSErrorCode_name[700:732], + 4540: _CSErrorCode_name[732:750], + 4545: _CSErrorCode_name[750:771], + 9999: _CSErrorCode_name[771:789], +} + +func (i CSErrorCode) String() string { + if str, ok := _CSErrorCode_map[i]; ok { + return str + } + return "CSErrorCode(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/github.com/exoscale/egoscale/dns.go b/vendor/github.com/exoscale/egoscale/dns.go index 11cb52051..9ce8a907b 100644 --- a/vendor/github.com/exoscale/egoscale/dns.go +++ b/vendor/github.com/exoscale/egoscale/dns.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "strconv" "strings" ) @@ -53,27 +54,81 @@ type DNSRecordResponse struct { Record DNSRecord `json:"record"` } +// UpdateDNSRecord represents a DNS record +type UpdateDNSRecord struct { + ID int64 `json:"id,omitempty"` + DomainID int64 `json:"domain_id,omitempty"` + Name string `json:"name,omitempty"` + TTL int `json:"ttl,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Content string `json:"content,omitempty"` + RecordType string `json:"record_type,omitempty"` + Prio int `json:"prio,omitempty"` +} + +// UpdateDNSRecordResponse represents the creation of a DNS record +type UpdateDNSRecordResponse struct { + Record UpdateDNSRecord `json:"record"` +} + // DNSErrorResponse represents an error in the API type DNSErrorResponse struct { - Message string `json:"message,omitempty"` - Errors *DNSError `json:"errors"` + Message string `json:"message,omitempty"` + Errors map[string][]string `json:"errors"` } -// DNSError represents an error -type DNSError struct { - Name []string `json:"name"` -} +// Record represent record type +type Record int + +//go:generate stringer -type=Record +const ( + // A record type + A Record = iota + // AAAA record type + AAAA + // ALIAS record type + ALIAS + // CNAME record type + CNAME + // HINFO record type + HINFO + // MX record type + MX + // NAPTR record type + NAPTR + // NS record type + NS + // POOL record type + POOL + // SPF record type + SPF + // SRV record type + SRV + // SSHFP record type + SSHFP + // TXT record type + TXT + // URL record type + URL +) // Error formats the DNSerror into a string -func (req *DNSErrorResponse) Error() error { - if req.Errors != nil { - return fmt.Errorf("DNS error: %s", strings.Join(req.Errors.Name, ", ")) +func (req *DNSErrorResponse) Error() string { + if len(req.Errors) > 0 { + errs := []string{} + for name, ss := range req.Errors { + if len(ss) > 0 { + errs = append(errs, fmt.Sprintf("%s: %s", name, strings.Join(ss, ", "))) + } + } + return fmt.Sprintf("dns error: %s (%s)", req.Message, strings.Join(errs, "; ")) } - return fmt.Errorf("DNS error: %s", req.Message) + return fmt.Sprintf("dns error: %s", req.Message) } // CreateDomain creates a DNS domain -func (exo *Client) CreateDomain(name string) (*DNSDomain, error) { +func (client *Client) CreateDomain(name string) (*DNSDomain, error) { m, err := json.Marshal(DNSDomainResponse{ Domain: &DNSDomain{ Name: name, @@ -83,7 +138,7 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) { return nil, err } - resp, err := exo.dnsRequest("/v1/domains", string(m), "POST") + resp, err := client.dnsRequest("/v1/domains", nil, string(m), "POST") if err != nil { return nil, err } @@ -97,8 +152,8 @@ func (exo *Client) CreateDomain(name string) (*DNSDomain, error) { } // GetDomain gets a DNS domain -func (exo *Client) GetDomain(name string) (*DNSDomain, error) { - resp, err := exo.dnsRequest("/v1/domains/"+name, "", "GET") +func (client *Client) GetDomain(name string) (*DNSDomain, error) { + resp, err := client.dnsRequest("/v1/domains/"+name, nil, "", "GET") if err != nil { return nil, err } @@ -111,20 +166,35 @@ func (exo *Client) GetDomain(name string) (*DNSDomain, error) { return d.Domain, nil } -// DeleteDomain delets a DNS domain -func (exo *Client) DeleteDomain(name string) error { - _, err := exo.dnsRequest("/v1/domains/"+name, "", "DELETE") +// GetDomains gets DNS domains +func (client *Client) GetDomains() ([]DNSDomain, error) { + resp, err := client.dnsRequest("/v1/domains", nil, "", "GET") if err != nil { - return err + return nil, err } - return nil + var d []DNSDomainResponse + if err := json.Unmarshal(resp, &d); err != nil { + return nil, err + } + + domains := make([]DNSDomain, len(d)) + for i := range d { + domains[i] = *d[i].Domain + } + return domains, nil +} + +// DeleteDomain delets a DNS domain +func (client *Client) DeleteDomain(name string) error { + _, err := client.dnsRequest("/v1/domains/"+name, nil, "", "DELETE") + return err } // GetRecord returns a DNS record -func (exo *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error) { +func (client *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error) { id := strconv.FormatInt(recordID, 10) - resp, err := exo.dnsRequest("/v1/domains/"+domain+"/records/"+id, "", "GET") + resp, err := client.dnsRequest("/v1/domains/"+domain+"/records/"+id, nil, "", "GET") if err != nil { return nil, err } @@ -138,8 +208,37 @@ func (exo *Client) GetRecord(domain string, recordID int64) (*DNSRecord, error) } // GetRecords returns the DNS records -func (exo *Client) GetRecords(name string) ([]DNSRecord, error) { - resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", "", "GET") +func (client *Client) GetRecords(domain string) ([]DNSRecord, error) { + resp, err := client.dnsRequest("/v1/domains/"+domain+"/records", nil, "", "GET") + if err != nil { + return nil, err + } + + var r []DNSRecordResponse + if err = json.Unmarshal(resp, &r); err != nil { + return nil, err + } + + records := make([]DNSRecord, 0, len(r)) + for _, rec := range r { + records = append(records, rec.Record) + } + + return records, nil +} + +// GetRecordsWithFilters returns the DNS records (filters can be empty) +func (client *Client) GetRecordsWithFilters(domain, name, recordType string) ([]DNSRecord, error) { + + filters := url.Values{} + if name != "" { + filters.Add("name", name) + } + if recordType != "" { + filters.Add("record_type", recordType) + } + + resp, err := client.dnsRequest("/v1/domains/"+domain+"/records", filters, "", "GET") if err != nil { return nil, err } @@ -158,7 +257,7 @@ func (exo *Client) GetRecords(name string) ([]DNSRecord, error) { } // CreateRecord creates a DNS record -func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) { +func (client *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) { body, err := json.Marshal(DNSRecordResponse{ Record: rec, }) @@ -166,7 +265,7 @@ func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) return nil, err } - resp, err := exo.dnsRequest("/v1/domains/"+name+"/records", string(body), "POST") + resp, err := client.dnsRequest("/v1/domains/"+name+"/records", nil, string(body), "POST") if err != nil { return nil, err } @@ -180,8 +279,8 @@ func (exo *Client) CreateRecord(name string, rec DNSRecord) (*DNSRecord, error) } // UpdateRecord updates a DNS record -func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error) { - body, err := json.Marshal(DNSRecordResponse{ +func (client *Client) UpdateRecord(name string, rec UpdateDNSRecord) (*DNSRecord, error) { + body, err := json.Marshal(UpdateDNSRecordResponse{ Record: rec, }) if err != nil { @@ -189,7 +288,7 @@ func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error) } id := strconv.FormatInt(rec.ID, 10) - resp, err := exo.dnsRequest("/v1/domains/"+name+"/records/"+id, string(body), "PUT") + resp, err := client.dnsRequest("/v1/domains/"+name+"/records/"+id, nil, string(body), "PUT") if err != nil { return nil, err } @@ -203,45 +302,64 @@ func (exo *Client) UpdateRecord(name string, rec DNSRecord) (*DNSRecord, error) } // DeleteRecord deletes a record -func (exo *Client) DeleteRecord(name string, recordID int64) error { +func (client *Client) DeleteRecord(name string, recordID int64) error { id := strconv.FormatInt(recordID, 10) - _, err := exo.dnsRequest("/v1/domains/"+name+"/records/"+id, "", "DELETE") + _, err := client.dnsRequest("/v1/domains/"+name+"/records/"+id, nil, "", "DELETE") return err } -func (exo *Client) dnsRequest(uri string, params string, method string) (json.RawMessage, error) { - url := exo.endpoint + uri - req, err := http.NewRequest(method, url, strings.NewReader(params)) +func (client *Client) dnsRequest(uri string, urlValues url.Values, params, method string) (json.RawMessage, error) { + rawURL := client.Endpoint + uri + url, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + + q := url.Query() + for k, vs := range urlValues { + for _, v := range vs { + q.Add(k, v) + } + } + url.RawQuery = q.Encode() + + req, err := http.NewRequest(method, url.String(), strings.NewReader(params)) if err != nil { return nil, err } var hdr = make(http.Header) - hdr.Add("X-DNS-TOKEN", exo.apiKey+":"+exo.apiSecret) + hdr.Add("X-DNS-TOKEN", client.APIKey+":"+client.apiSecret) + hdr.Add("User-Agent", fmt.Sprintf("exoscale/egoscale (%v)", Version)) hdr.Add("Accept", "application/json") if params != "" { hdr.Add("Content-Type", "application/json") } req.Header = hdr - response, err := exo.client.Do(req) + resp, err := client.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() // nolint: errcheck + + contentType := resp.Header.Get("content-type") + if !strings.Contains(contentType, "application/json") { + return nil, fmt.Errorf(`response content-type expected to be "application/json", got %q`, contentType) + } + + b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } - defer response.Body.Close() - b, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - if response.StatusCode >= 400 { - var e DNSErrorResponse - if err := json.Unmarshal(b, &e); err != nil { + if resp.StatusCode >= 400 { + e := new(DNSErrorResponse) + if err := json.Unmarshal(b, e); err != nil { return nil, err } - return nil, e.Error() + return nil, e } return b, nil diff --git a/vendor/github.com/exoscale/egoscale/doc.go b/vendor/github.com/exoscale/egoscale/doc.go index e53861ba0..6a08834b9 100644 --- a/vendor/github.com/exoscale/egoscale/doc.go +++ b/vendor/github.com/exoscale/egoscale/doc.go @@ -1,16 +1,76 @@ /* -Package egoscale is a mapping for with the CloudStack API (http://cloudstack.apache.org/api.html) from Go. It has been designed against the Exoscale (https://www.exoscale.ch/) infrastructure but should fit other CloudStack services. +Package egoscale is a mapping for with the CloudStack API (http://cloudstack.apache.org/api.html) from Go. It has been designed against the Exoscale (https://www.exoscale.com/) infrastructure but should fit other CloudStack services. Requests and Responses -The paradigm used in this library is that CloudStack defines two types of requests synchronous (client.Request) and asynchronous (client.AsyncRequest). And when the expected responses is a success message, you may use the boolean requests variants (client.BooleanRequest, client.BooleanAsyncRequest). To build a request, construct the adequate struct. This library expects a pointer for efficiency reasons only. The response is a struct corresponding to the request itself. E.g. DeployVirtualMachine gives DeployVirtualMachineResponse, as a pointer as well to avoid big copies. +To build a request, construct the adequate struct. This library expects a pointer for efficiency reasons only. The response is a struct corresponding to the data at stake. E.g. DeployVirtualMachine gives a VirtualMachine, as a pointer as well to avoid big copies. -Then everything within the struct is not a pointer. +Then everything within the struct is not a pointer. Find below some examples of how egoscale may be used to interact with a CloudStack endpoint, especially Exoscale itself. If anything feels odd or unclear, please let us know: https://github.com/exoscale/egoscale/issues -Affinity and Anti-Affinity groups + req := &egoscale.DeployVirtualMachine{ + Size: 10, + ServiceOfferingID: "...", + TemplateID: "...", + ZoneID: "...", + } + + fmt.Println("Deployment started") + resp, err := cs.Request(req) + if err != nil { + panic(err) + } + + vm := resp.(*egoscale.VirtualMachine) + fmt.Printf("Virtual Machine ID: %s\n", vm.ID) + +This example deploys a virtual machine while controlling the job status as it goes. It enables a finer control over errors, e.g. HTTP timeout, and eventually a way to kill it of (from the client side). + + req := &egoscale.DeployVirtualMachine{ + Size: 10, + ServiceOfferingID: "...", + TemplateID: "...", + ZoneID: "...", + } + vm := &egoscale.VirtualMachine{} + + fmt.Println("Deployment started") + cs.AsyncRequest(req, func(jobResult *egoscale.AsyncJobResult, err error) bool { + if err != nil { + // any kind of error + panic(err) + } + + // Keep waiting + if jobResult.JobStatus == egoscale.Pending { + fmt.Println("wait...") + return true + } + + // Unmarshal the response into the response struct + if err := jobResult.Response(vm); err != nil { + // JSON unmarshaling error + panic(err) + } + + // Stop waiting + return false + }) + + fmt.Printf("Virtual Machine ID: %s\n", vm.ID) + +Debugging and traces + +As this library is mostly an HTTP client, you can reuse all the existing tools around it. + + cs := egoscale.NewClient("https://api.exoscale.ch/compute", "EXO...", "...") + // sets a logger on stderr + cs.Logger = log.Newos.Stderr, "prefix", log.LstdFlags) + // activates the HTTP traces + cs.TraceOn() + +Nota bene: when running the tests or the egoscale library via another tool, e.g. the exo cli (or the cs cli), the environment variable EXOSCALE_TRACE=prefix does the above configuration for you. As a developer using egoscale as a library, you'll find it more convenient to plug your favorite io.Writer as it's Logger. -Affinity and Anti-Affinity groups provide a way to influence where VMs should run. See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#affinity-groups APIs @@ -24,70 +84,94 @@ All the available APIs on the server and provided by the API Discovery plugin } for _, api := range resp.(*egoscale.ListAPIsResponse).API { - fmt.Println("%s %s", api.Name, api.Description) + fmt.Printf("%s %s\n", api.Name, api.Description) } // Output: // listNetworks Lists all available networks // ... - -Elastic IPs - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#about-elastic-ips - -Networks - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.8/networking_and_traffic.html - -NICs - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#configuring-multiple-ip-addresses-on-a-single-nic - - Security Groups -Security Groups provide a way to isolate traffic to VMs. +Security Groups provide a way to isolate traffic to VMs. Rules are added via the two Authorization commands. resp, err := cs.Request(&egoscale.CreateSecurityGroup{ Name: "Load balancer", Description: "Opens HTTP/HTTPS ports from the outside world", }) - securityGroup := resp.(*egoscale.CreateSecurityGroupResponse).SecurityGroup + securityGroup := resp.(*egoscale.SecurityGroup) + + resp, err = cs.Request(&egoscale.AuthorizeSecurityGroupIngress{ + Description: "SSH traffic", + SecurityGroupID: securityGroup.ID, + CidrList: []string{"0.0.0.0/0"}, + Protocol: "tcp", + StartPort: 22, + EndPort: 22, + }) + // The modified SecurityGroup is returned + securityGroup := resp.(*egoscale.SecurityGroup) + // ... err = client.BooleanRequest(&egoscale.DeleteSecurityGroup{ ID: securityGroup.ID, }) // ... +Security Group also implement the generic List, Get and Delete interfaces (Listable, Gettable and Deletable). + + // List all Security Groups + sgs, err := cs.List(new(egoscale.SecurityGroup)) + for _, s := range sgs { + sg := s.(egoscale.SecurityGroup) + // ... + } + + // Get a Security Group + sg := &egoscale.SecurityGroup{Name: "Load balancer"} + if err := cs.Get(sg); err != nil { + ... + } + // The SecurityGroup struct has been loaded with the SecurityGroup informations + + if err := cs.Delete(sg); err != nil { + ... + } + // The SecurityGroup has been deleted + See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/networking_and_traffic.html#security-groups -Service Offerings - -A service offering correspond to some hardware features (CPU, RAM). - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/service_offerings.html - -SSH Key Pairs - -In addition to username and password (disabled on Exoscale), SSH keys are used to log into the infrastructure. - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#creating-the-ssh-keypair - -Virtual Machines - -... todo ... - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html - -Templates - -... todo ... - -See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/templates.html - Zones -A Zone corresponds to a Data Center. +A Zone corresponds to a Data Center. You may list them. Zone implements the Listable interface, which let you perform a list in two different ways. The first exposes the underlying CloudStack request while the second one hide them and you only manipulate the structs of your interest. + + // Using ListZones request + req := &egoscale.ListZones{} + resp, err := client.Request(req) + if err != nil { + panic(err) + } + + for _, zone := range resp.(*egoscale.ListZonesResponse) { + ... + } + + // Using client.List + zone := &egoscale.Zone{} + zones, err := client.List(zone) + if err != nil { + panic(err) + } + + for _, z := range zones { + zone := z.(egoscale.Zone) + ... + } + +Elastic IPs + +An Elastic IP is a way to attach an IP address to many Virtual Machines. The API side of the story configures the external environment, like the routing. Some work is required within the machine to properly configure the interfaces. + +See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#about-elastic-ips */ package egoscale diff --git a/vendor/github.com/exoscale/egoscale/errorcode_string.go b/vendor/github.com/exoscale/egoscale/errorcode_string.go new file mode 100644 index 000000000..19711257e --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/errorcode_string.go @@ -0,0 +1,37 @@ +// Code generated by "stringer -type ErrorCode"; DO NOT EDIT. + +package egoscale + +import "strconv" + +const ( + _ErrorCode_name_0 = "Unauthorized" + _ErrorCode_name_1 = "MethodNotAllowed" + _ErrorCode_name_2 = "UnsupportedActionError" + _ErrorCode_name_3 = "APILimitExceededMalformedParameterErrorParamError" + _ErrorCode_name_4 = "InternalErrorAccountErrorAccountResourceLimitErrorInsufficientCapacityErrorResourceUnavailableErrorResourceAllocationErrorResourceInUseErrorNetworkRuleConflictError" +) + +var ( + _ErrorCode_index_3 = [...]uint8{0, 16, 39, 49} + _ErrorCode_index_4 = [...]uint8{0, 13, 25, 50, 75, 99, 122, 140, 164} +) + +func (i ErrorCode) String() string { + switch { + case i == 401: + return _ErrorCode_name_0 + case i == 405: + return _ErrorCode_name_1 + case i == 422: + return _ErrorCode_name_2 + case 429 <= i && i <= 431: + i -= 429 + return _ErrorCode_name_3[_ErrorCode_index_3[i]:_ErrorCode_index_3[i+1]] + case 530 <= i && i <= 537: + i -= 530 + return _ErrorCode_name_4[_ErrorCode_index_4[i]:_ErrorCode_index_4[i+1]] + default: + return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/exoscale/egoscale/events.go b/vendor/github.com/exoscale/egoscale/events.go index 3eaaf48da..fd99d74b6 100644 --- a/vendor/github.com/exoscale/egoscale/events.go +++ b/vendor/github.com/exoscale/egoscale/events.go @@ -2,53 +2,41 @@ package egoscale // Event represents an event in the system type Event struct { - ID string `json:"id"` - Account string `json:"account"` - Created string `json:"created"` - Description string `json:"description,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Level string `json:"level"` // INFO, WARN, ERROR - ParentID string `json:"parentid,omitempty"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - State string `json:"state,omitempty"` - Type string `json:"type"` - UserName string `json:"username,omitempty"` + Account string `json:"account,omitempty" doc:"the account name for the account that owns the object being acted on in the event (e.g. the owner of the virtual machine, ip address, or security group)"` + Created string `json:"created,omitempty" doc:"the date the event was created"` + Description string `json:"description,omitempty" doc:"a brief description of the event"` + Domain string `json:"domain,omitempty" doc:"the name of the account's domain"` + DomainID *UUID `json:"domainid,omitempty" doc:"the id of the account's domain"` + ID *UUID `json:"id,omitempty" doc:"the ID of the event"` + Level string `json:"level,omitempty" doc:"the event level (INFO, WARN, ERROR)"` + ParentID *UUID `json:"parentid,omitempty" doc:"whether the event is parented"` + State string `json:"state,omitempty" doc:"the state of the event"` + Type string `json:"type,omitempty" doc:"the type of the event (see event types)"` + UserName string `json:"username,omitempty" doc:"the name of the user who performed the action (can be different from the account if an admin is performing an action for a user, e.g. starting/stopping a user's virtual machine)"` } // EventType represent a type of event type EventType struct { - Name string `json:"name"` + Name string `json:"name,omitempty" doc:"Event Type"` } // ListEvents list the events -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEvents.html type ListEvents struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - Duration int `json:"duration,omitempty"` - EndDate string `json:"enddate,omitempty"` - EntryTime int `json:"entrytime,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - Level string `json:"level,omitempty"` // INFO, WARN, ERROR - ListAll bool `json:"listall,omitempty"` - Page int `json:"page,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + Duration int `json:"duration,omitempty" doc:"the duration of the event"` + EndDate string `json:"enddate,omitempty" doc:"the end date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")"` + EntryTime int `json:"entrytime,omitempty" doc:"the time the event was entered"` + ID *UUID `json:"id,omitempty" doc:"the ID of the event"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves." doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Level string `json:"level,omitempty" doc:"the event level (INFO, WARN, ERROR)"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Page int `json:"page,omitempty" ` PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` - StartDate string `json:"startdate,omitempty"` - Type string `json:"type,omitempty"` -} - -func (*ListEvents) name() string { - return "listEvents" -} - -func (*ListEvents) response() interface{} { - return new(ListEventsResponse) + StartDate string `json:"startdate,omitempty" doc:"the start date range of the list you want to retrieve (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")"` + Type string `json:"type,omitempty" doc:"the event type (see event types)"` + _ bool `name:"listEvents" description:"A command to list events."` } // ListEventsResponse represents a response of a list query @@ -57,21 +45,22 @@ type ListEventsResponse struct { Event []Event `json:"event"` } -// ListEventTypes list the event types -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listEventTypes.html -type ListEventTypes struct{} - -func (*ListEventTypes) name() string { - return "listEventTypes" +func (ListEvents) response() interface{} { + return new(ListEventsResponse) } -func (*ListEventTypes) response() interface{} { - return new(ListEventTypesResponse) +// ListEventTypes list the event types +type ListEventTypes struct { + _ bool `name:"listEventTypes" description:"List Event Types"` } // ListEventTypesResponse represents a response of a list query type ListEventTypesResponse struct { Count int `json:"count"` EventType []EventType `json:"eventtype"` + _ bool `name:"listEventTypes" description:"List Event Types"` +} + +func (ListEventTypes) response() interface{} { + return new(ListEventTypesResponse) } diff --git a/vendor/github.com/exoscale/egoscale/hosts.go b/vendor/github.com/exoscale/egoscale/hosts.go new file mode 100644 index 000000000..bd55d855b --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/hosts.go @@ -0,0 +1,99 @@ +package egoscale + +import ( + "net" +) + +// Host represents the Hypervisor +type Host struct { + Capabilities string `json:"capabilities,omitempty" doc:"capabilities of the host"` + ClusterID *UUID `json:"clusterid,omitempty" doc:"the cluster ID of the host"` + ClusterName string `json:"clustername,omitempty" doc:"the cluster name of the host"` + ClusterType string `json:"clustertype,omitempty" doc:"the cluster type of the cluster that host belongs to"` + CPUAllocated int64 `json:"cpuallocated,omitempty" doc:"the amount of the host's CPU currently allocated"` + CPUNumber int `json:"cpunumber,omitempty" doc:"the CPU number of the host"` + CPUSockets int `json:"cpusockets,omitempty" doc:"the number of CPU sockets on the host"` + CPUSpeed int64 `json:"cpuspeed,omitempty" doc:"the CPU speed of the host"` + CPUUsed int64 `json:"cpuused,omitempty" doc:"the amount of the host's CPU currently used"` + CPUWithOverProvisioning int64 `json:"cpuwithoverprovisioning,omitempty" doc:"the amount of the host's CPU after applying the cpu.overprovisioning.factor"` + Created string `json:"created,omitempty" doc:"the date and time the host was created"` + Disconnected string `json:"disconnected,omitempty" doc:"true if the host is disconnected. False otherwise."` + DiskSizeAllocated int64 `json:"disksizeallocated,omitempty" doc:"the host's or host storage pool's currently allocated disk size"` + DiskSizeTotal int64 `json:"disksizetotal,omitempty" doc:"the total disk size of the host or host storage pool"` + DiskSizeUsed int64 `json:"disksizeused,omitempty" doc:"the host's or host storage pool's currently used disk size"` + DiskWithOverProvisioning int64 `json:"diskwithoverprovisioning,omitempty" doc:"the total disk size of the host or host storage pool with over provisioning factor"` + Events string `json:"events,omitempty" doc:"events available for the host"` + HAHost *bool `json:"hahost,omitempty" doc:"true if the host is Ha host (dedicated to vms started by HA process; false otherwise"` + HostTags string `json:"hosttags,omitempty" doc:"comma-separated list of tags for the host"` + Hypervisor string `json:"hypervisor,omitempty" doc:"the host hypervisor"` + HypervisorVersion string `json:"hypervisorversion,omitempty" doc:"the hypervisor version"` + ID *UUID `json:"id,omitempty" doc:"the ID of the host"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"the IP address of the host"` + IsLocalstorageActive *bool `json:"islocalstorageactive,omitempty" doc:"true if local storage is active, false otherwise"` + LastPinged string `json:"lastpinged,omitempty" doc:"the date and time the host was last pinged"` + ManagementServerID *UUID `json:"managementserverid,omitempty" doc:"the management server ID of the host"` + MemoryAllocated int64 `json:"memoryallocated,omitempty" doc:"the amount of VM's memory allocated onto the host"` + MemoryPhysical int64 `json:"memoryphysical,omitempty" doc:"the total physical memory of the host"` + MemoryReserved int64 `json:"memoryreserved,omitempty" doc:"the amount of the host's memory reserved"` + MemoryTotal int64 `json:"memorytotal,omitempty" doc:"the total memory of the host available (must be physical - reserved)"` + MemoryUsed int64 `json:"memoryused,omitempty" doc:"the amount of the host's memory used by running vm"` + Name string `json:"name,omitempty" doc:"the name of the host"` + NetworkKbsRead int64 `json:"networkkbsread,omitempty" doc:"the incoming network traffic on the host"` + NetworkKbsWrite int64 `json:"networkkbswrite,omitempty" doc:"the outgoing network traffic on the host"` + OSCategoryID *UUID `json:"oscategoryid,omitempty" doc:"the OS category ID of the host"` + OSCategoryName string `json:"oscategoryname,omitempty" doc:"the OS category name of the host"` + PCIDevices []PCIDevice `json:"pcidevices,omitempty" doc:"PCI cards present in the host"` + PodID *UUID `json:"podid,omitempty" doc:"the Pod ID of the host"` + PodName string `json:"podname,omitempty" doc:"the Pod name of the host"` + Removed string `json:"removed,omitempty" doc:"the date and time the host was removed"` + ResourceState string `json:"resourcestate,omitempty" doc:"the resource state of the host"` + State string `json:"state,omitempty" doc:"the state of the host"` + StorageID *UUID `json:"storageid,omitempty" doc:"the host's storage pool id"` + Type string `json:"type,omitempty" doc:"the host type"` + Version string `json:"version,omitempty" doc:"the host version"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the Zone ID of the host"` + ZoneName string `json:"zonename,omitempty" doc:"the Zone name of the host"` +} + +// ListHosts lists hosts +type ListHosts struct { + ClusterID *UUID `json:"clusterid,omitempty" doc:"lists hosts existing in particular cluster"` + Details []string `json:"details,omitempty" doc:"comma separated list of host details requested, value can be a list of [ min, all, capacity, events, stats]"` + HAHost *bool `json:"hahost,omitempty" doc:"if true, list only hosts dedicated to HA"` + Hypervisor string `json:"hypervisor,omitempty" doc:"hypervisor type of host: KVM,Simulator"` + ID *UUID `json:"id,omitempty" doc:"the id of the host"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Name string `json:"name,omitempty" doc:"the name of the host"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + PodID *UUID `json:"podid,omitempty" doc:"the Pod ID for the host"` + ResourceState string `json:"resourcestate,omitempty" doc:"list hosts by resource state. Resource state represents current state determined by admin of host, value can be one of [Enabled, Disabled, Unmanaged, PrepareForMaintenance, ErrorInMaintenance, Maintenance, Error]"` + State string `json:"state,omitempty" doc:"the state of the host"` + Type string `json:"type,omitempty" doc:"the host type"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the Zone ID for the host"` + _ bool `name:"listHosts" description:"Lists hosts."` +} + +func (ListHosts) response() interface{} { + return new(ListHostsResponse) +} + +// ListHostsResponse represents a list of hosts +type ListHostsResponse struct { + Count int `json:"count"` + Host []Host `json:"host"` +} + +// UpdateHost changes the resources state of a host +type UpdateHost struct { + Allocationstate string `json:"allocationstate,omitempty" doc:"Change resource state of host, valid values are [Enable, Disable]. Operation may failed if host in states not allowing Enable/Disable"` + HostTags []string `json:"hosttags,omitempty" doc:"list of tags to be added to the host"` + ID *UUID `json:"id" doc:"the ID of the host to update"` + OSCategoryID *UUID `json:"oscategoryid,omitempty" doc:"the id of Os category to update the host with"` + URL string `json:"url,omitempty" doc:"the new uri for the secondary storage: nfs://host/path"` + _ bool `name:"updateHost" description:"Updates a host."` +} + +func (UpdateHost) response() interface{} { + return new(Host) +} diff --git a/vendor/github.com/exoscale/egoscale/init.go b/vendor/github.com/exoscale/egoscale/init.go deleted file mode 100644 index b0814cdeb..000000000 --- a/vendor/github.com/exoscale/egoscale/init.go +++ /dev/null @@ -1,22 +0,0 @@ -package egoscale - -import ( - "crypto/tls" - "net/http" -) - -// NewClient creates a CloudStack API client -func NewClient(endpoint string, apiKey string, apiSecret string) *Client { - cs := &Client{ - client: &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, - }, - }, - endpoint: endpoint, - apiKey: apiKey, - apiSecret: apiSecret, - } - return cs -} diff --git a/vendor/github.com/exoscale/egoscale/jobstatustype_string.go b/vendor/github.com/exoscale/egoscale/jobstatustype_string.go new file mode 100644 index 000000000..298561608 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/jobstatustype_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type JobStatusType"; DO NOT EDIT. + +package egoscale + +import "strconv" + +const _JobStatusType_name = "PendingSuccessFailure" + +var _JobStatusType_index = [...]uint8{0, 7, 14, 21} + +func (i JobStatusType) String() string { + if i < 0 || i >= JobStatusType(len(_JobStatusType_index)-1) { + return "JobStatusType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _JobStatusType_name[_JobStatusType_index[i]:_JobStatusType_index[i+1]] +} diff --git a/vendor/github.com/exoscale/egoscale/keypairs.go b/vendor/github.com/exoscale/egoscale/keypairs.go deleted file mode 100644 index 63f3f9b61..000000000 --- a/vendor/github.com/exoscale/egoscale/keypairs.go +++ /dev/null @@ -1,171 +0,0 @@ -package egoscale - -// SSHKeyPair represents an SSH key pair -type SSHKeyPair struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Fingerprint string `json:"fingerprint,omitempty"` - Name string `json:"name,omitempty"` - PrivateKey string `json:"privatekey,omitempty"` -} - -// CreateSSHKeyPair represents a new keypair to be created -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createSSHKeyPair.html -type CreateSSHKeyPair struct { - Name string `json:"name"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -func (*CreateSSHKeyPair) name() string { - return "createSSHKeyPair" -} - -func (*CreateSSHKeyPair) response() interface{} { - return new(CreateSSHKeyPairResponse) -} - -// CreateSSHKeyPairResponse represents the creation of an SSH Key Pair -type CreateSSHKeyPairResponse struct { - KeyPair SSHKeyPair `json:"keypair"` -} - -// DeleteSSHKeyPair represents a new keypair to be created -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteSSHKeyPair.html -type DeleteSSHKeyPair struct { - Name string `json:"name"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -func (*DeleteSSHKeyPair) name() string { - return "deleteSSHKeyPair" -} - -func (*DeleteSSHKeyPair) response() interface{} { - return new(booleanSyncResponse) -} - -// RegisterSSHKeyPair represents a new registration of a public key in a keypair -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/registerSSHKeyPair.html -type RegisterSSHKeyPair struct { - Name string `json:"name"` - PublicKey string `json:"publickey"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -func (*RegisterSSHKeyPair) name() string { - return "registerSSHKeyPair" -} - -func (*RegisterSSHKeyPair) response() interface{} { - return new(RegisterSSHKeyPairResponse) -} - -// RegisterSSHKeyPairResponse represents the creation of an SSH Key Pair -type RegisterSSHKeyPairResponse struct { - KeyPair SSHKeyPair `json:"keypair"` -} - -// ListSSHKeyPairs represents a query for a list of SSH KeyPairs -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listSSHKeyPairs.html -type ListSSHKeyPairs struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - Fingerprint string `json:"fingerprint,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Name string `json:"name,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -func (*ListSSHKeyPairs) name() string { - return "listSSHKeyPairs" -} - -func (*ListSSHKeyPairs) response() interface{} { - return new(ListSSHKeyPairsResponse) -} - -// ListSSHKeyPairsResponse represents a list of SSH key pairs -type ListSSHKeyPairsResponse struct { - Count int `json:"count"` - SSHKeyPair []SSHKeyPair `json:"sshkeypair"` -} - -// ResetSSHKeyForVirtualMachine (Async) represents a change for the key pairs -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/resetSSHKeyForVirtualMachine.html -type ResetSSHKeyForVirtualMachine struct { - ID string `json:"id"` - KeyPair string `json:"keypair"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -func (*ResetSSHKeyForVirtualMachine) name() string { - return "resetSSHKeyForVirtualMachine" -} - -func (*ResetSSHKeyForVirtualMachine) asyncResponse() interface{} { - return new(ResetSSHKeyForVirtualMachineResponse) -} - -// ResetSSHKeyForVirtualMachineResponse represents the modified VirtualMachine -type ResetSSHKeyForVirtualMachineResponse VirtualMachineResponse - -// CreateKeypair create a new SSH Key Pair -// -// Deprecated: will go away, use the API directly -func (exo *Client) CreateKeypair(name string) (*SSHKeyPair, error) { - req := &CreateSSHKeyPair{ - Name: name, - } - resp, err := exo.Request(req) - if err != nil { - return nil, err - } - - keypair := resp.(*CreateSSHKeyPairResponse).KeyPair - return &keypair, nil -} - -// DeleteKeypair deletes an SSH key pair -// -// Deprecated: will go away, use the API directly -func (exo *Client) DeleteKeypair(name string) error { - req := &DeleteSSHKeyPair{ - Name: name, - } - return exo.BooleanRequest(req) -} - -// RegisterKeypair registers a public key in a keypair -// -// Deprecated: will go away, use the API directly -func (exo *Client) RegisterKeypair(name string, publicKey string) (*SSHKeyPair, error) { - req := &RegisterSSHKeyPair{ - Name: name, - PublicKey: publicKey, - } - resp, err := exo.Request(req) - if err != nil { - return nil, err - } - - keypair := resp.(*RegisterSSHKeyPairResponse).KeyPair - return &keypair, nil -} diff --git a/vendor/github.com/exoscale/egoscale/limits.go b/vendor/github.com/exoscale/egoscale/limits.go index 8c788f6a5..3b52b1640 100644 --- a/vendor/github.com/exoscale/egoscale/limits.go +++ b/vendor/github.com/exoscale/egoscale/limits.go @@ -9,82 +9,91 @@ const ( // VirtualMachineTypeName is the resource type name of a VM VirtualMachineTypeName ResourceTypeName = "user_vm" // IPAddressTypeName is the resource type name of an IP address - IPAddressTypeName = "public_ip" + IPAddressTypeName ResourceTypeName = "public_ip" // VolumeTypeName is the resource type name of a volume - VolumeTypeName = "volume" + VolumeTypeName ResourceTypeName = "volume" // SnapshotTypeName is the resource type name of a snapshot - SnapshotTypeName = "snapshot" + SnapshotTypeName ResourceTypeName = "snapshot" // TemplateTypeName is the resource type name of a template - TemplateTypeName = "template" + TemplateTypeName ResourceTypeName = "template" // ProjectTypeName is the resource type name of a project - ProjectTypeName = "project" + ProjectTypeName ResourceTypeName = "project" // NetworkTypeName is the resource type name of a network - NetworkTypeName = "network" + NetworkTypeName ResourceTypeName = "network" // VPCTypeName is the resource type name of a VPC - VPCTypeName = "vpc" + VPCTypeName ResourceTypeName = "vpc" // CPUTypeName is the resource type name of a CPU - CPUTypeName = "cpu" + CPUTypeName ResourceTypeName = "cpu" // MemoryTypeName is the resource type name of Memory - MemoryTypeName = "memory" + MemoryTypeName ResourceTypeName = "memory" // PrimaryStorageTypeName is the resource type name of primary storage - PrimaryStorageTypeName = "primary_storage" + PrimaryStorageTypeName ResourceTypeName = "primary_storage" // SecondaryStorageTypeName is the resource type name of secondary storage - SecondaryStorageTypeName = "secondary_storage" + SecondaryStorageTypeName ResourceTypeName = "secondary_storage" ) // ResourceType represents the ID of a resource type (for limits) -type ResourceType int64 +type ResourceType string const ( // VirtualMachineType is the resource type ID of a VM - VirtualMachineType ResourceType = iota + VirtualMachineType ResourceType = "0" // IPAddressType is the resource type ID of an IP address - IPAddressType + IPAddressType ResourceType = "1" // VolumeType is the resource type ID of a volume - VolumeType + VolumeType ResourceType = "2" // SnapshotType is the resource type ID of a snapshot - SnapshotType + SnapshotType ResourceType = "3" // TemplateType is the resource type ID of a template - TemplateType + TemplateType ResourceType = "4" // ProjectType is the resource type ID of a project - ProjectType + ProjectType ResourceType = "5" // NetworkType is the resource type ID of a network - NetworkType + NetworkType ResourceType = "6" // VPCType is the resource type ID of a VPC - VPCType + VPCType ResourceType = "7" // CPUType is the resource type ID of a CPU - CPUType + CPUType ResourceType = "8" // MemoryType is the resource type ID of Memory - MemoryType + MemoryType ResourceType = "9" // PrimaryStorageType is the resource type ID of primary storage - PrimaryStorageType + PrimaryStorageType ResourceType = "10" // SecondaryStorageType is the resource type ID of secondary storage - SecondaryStorageType + SecondaryStorageType ResourceType = "11" ) +// ResourceLimit represents the limit on a particular resource +type ResourceLimit struct { + Account string `json:"account,omitempty" doc:"the account of the resource limit"` + Domain string `json:"domain,omitempty" doc:"the domain name of the resource limit"` + DomainID string `json:"domainid,omitempty" doc:"the domain ID of the resource limit"` + Max int64 `json:"max,omitempty" doc:"the maximum number of the resource. A -1 means the resource currently has no limit."` + ResourceType ResourceType `json:"resourcetype,omitempty" doc:"resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values."` + ResourceTypeName string `json:"resourcetypename,omitempty" doc:"resource type name. Values include user_vm, public_ip, volume, snapshot, template, project, network, vpc, cpu, memory, primary_storage, secondary_storage."` +} + +// APILimit represents the limit count +type APILimit struct { + Account string `json:"account,omitempty" doc:"the account name of the api remaining count"` + Accountid string `json:"accountid,omitempty" doc:"the account uuid of the api remaining count"` + APIAllowed int `json:"apiAllowed,omitempty" doc:"currently allowed number of apis"` + APIIssued int `json:"apiIssued,omitempty" doc:"number of api already issued"` + ExpireAfter int64 `json:"expireAfter,omitempty" doc:"seconds left to reset counters"` +} + // ListResourceLimits lists the resource limits -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listResourceLimits.html type ListResourceLimits struct { - Account string `json:"account,omittempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` - ResourceType ResourceType `json:"resourcetype,omitempty"` - ResourceTypeName ResourceTypeName `json:"resourcetypename,omitempty"` -} - -func (*ListResourceLimits) name() string { - return "listResourceLimits" -} - -func (*ListResourceLimits) response() interface{} { - return new(ListResourceLimitsResponse) + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID string `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID int64 `json:"id,omitempty" doc:"Lists resource limits by ID."` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + ResourceType ResourceType `json:"resourcetype,omitempty" doc:"Type of resource. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. 1 - IP. Number of public IP addresses an account can own. 2 - Volume. Number of disk volumes an account can own. 3 - Snapshot. Number of snapshots an account can own. 4 - Template. Number of templates an account can register/create. 5 - Project. Number of projects an account can own. 6 - Network. Number of networks an account can own. 7 - VPC. Number of VPC an account can own. 8 - CPU. Number of CPU an account can allocate for his resources. 9 - Memory. Amount of RAM an account can allocate for his resources. 10 - PrimaryStorage. Total primary storage space (in GiB) a user can use. 11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use. 12 - Elastic IP. Number of public elastic IP addresses an account can own. 13 - SMTP. If the account is allowed SMTP outbound traffic."` + ResourceTypeName string `json:"resourcetypename,omitempty" doc:"Type of resource (wins over resourceType if both are provided). Values are: user_vm - Instance. Number of instances a user can create. public_ip - IP. Number of public IP addresses an account can own. volume - Volume. Number of disk volumes an account can own. snapshot - Snapshot. Number of snapshots an account can own. template - Template. Number of templates an account can register/create. project - Project. Number of projects an account can own. network - Network. Number of networks an account can own. vpc - VPC. Number of VPC an account can own. cpu - CPU. Number of CPU an account can allocate for his resources. memory - Memory. Amount of RAM an account can allocate for his resources. primary_storage - PrimaryStorage. Total primary storage space (in GiB) a user can use. secondary_storage - SecondaryStorage. Total secondary storage space (in GiB) a user can use. public_elastic_ip - IP. Number of public elastic IP addresses an account can own. smtp - SG. If the account is allowed SMTP outbound traffic."` + _ bool `name:"listResourceLimits" description:"Lists resource limits."` } // ListResourceLimitsResponse represents a list of resource limits @@ -93,14 +102,38 @@ type ListResourceLimitsResponse struct { ResourceLimit []ResourceLimit `json:"resourcelimit"` } -// ResourceLimit represents the limit on a particular resource -type ResourceLimit struct { - Account string `json:"account,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Max int64 `json:"max,omitempty"` // -1 means the sky is the limit - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - ResourceType ResourceType `json:"resourcetype,omitempty"` - ResourceTypeName ResourceTypeName `json:"resourcetypename,omitempty"` +func (ListResourceLimits) response() interface{} { + return new(ListResourceLimitsResponse) +} + +// UpdateResourceLimit updates the resource limit +type UpdateResourceLimit struct { + Account string `json:"account,omitempty" doc:"Update resource for a specified account. Must be used with the domainId parameter."` + DomainID string `json:"domainid,omitempty" doc:"Update resource limits for all accounts in specified domain. If used with the account parameter, updates resource limits for a specified account in specified domain."` + Max int64 `json:"max,omitempty" doc:"Maximum resource limit."` + ResourceType ResourceType `json:"resourcetype" doc:"Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. 1 - IP. Number of public IP addresses a user can own. 2 - Volume. Number of disk volumes a user can create. 3 - Snapshot. Number of snapshots a user can create. 4 - Template. Number of templates that a user can register/create. 6 - Network. Number of guest network a user can create. 7 - VPC. Number of VPC a user can create. 8 - CPU. Total number of CPU cores a user can use. 9 - Memory. Total Memory (in MB) a user can use. 10 - PrimaryStorage. Total primary storage space (in GiB) a user can use. 11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use."` + _ bool `name:"updateResourceLimit" description:"Updates resource limits for an account or domain."` +} + +// UpdateResourceLimitResponse represents an updated resource limit +type UpdateResourceLimitResponse struct { + ResourceLimit ResourceLimit `json:"resourcelimit"` +} + +func (UpdateResourceLimit) response() interface{} { + return new(UpdateResourceLimitResponse) +} + +// GetAPILimit gets API limit count for the caller +type GetAPILimit struct { + _ bool `name:"getApiLimit" description:"Get API limit count for the caller"` +} + +// GetAPILimitResponse represents the limits towards the API call +type GetAPILimitResponse struct { + APILimit APILimit `json:"apilimit"` +} + +func (GetAPILimit) response() interface{} { + return new(GetAPILimitResponse) } diff --git a/vendor/github.com/exoscale/egoscale/macaddress.go b/vendor/github.com/exoscale/egoscale/macaddress.go new file mode 100644 index 000000000..3c1dcaea4 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/macaddress.go @@ -0,0 +1,63 @@ +package egoscale + +import ( + "encoding/json" + "fmt" + "net" +) + +// MACAddress is a nicely JSON serializable net.HardwareAddr +type MACAddress net.HardwareAddr + +// String returns the MAC address in standard format +func (mac MACAddress) String() string { + return (net.HardwareAddr)(mac).String() +} + +// MAC48 builds a MAC-48 MACAddress +func MAC48(a, b, c, d, e, f byte) MACAddress { + m := make(MACAddress, 6) + m[0] = a + m[1] = b + m[2] = c + m[3] = d + m[4] = e + m[5] = f + return m +} + +// UnmarshalJSON unmarshals the raw JSON into the MAC address +func (mac *MACAddress) UnmarshalJSON(b []byte) error { + var addr string + if err := json.Unmarshal(b, &addr); err != nil { + return err + } + hw, err := ParseMAC(addr) + if err != nil { + return err + } + + *mac = make(MACAddress, 6) + copy(*mac, hw) + return nil +} + +// MarshalJSON converts the MAC Address to a string representation +func (mac MACAddress) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", mac.String())), nil +} + +// ParseMAC converts a string into a MACAddress +func ParseMAC(s string) (MACAddress, error) { + hw, err := net.ParseMAC(s) + return (MACAddress)(hw), err +} + +// MustParseMAC acts like ParseMAC but panics if in case of an error +func MustParseMAC(s string) MACAddress { + mac, err := ParseMAC(s) + if err != nil { + panic(err) + } + return mac +} diff --git a/vendor/github.com/exoscale/egoscale/network_offerings.go b/vendor/github.com/exoscale/egoscale/network_offerings.go index 1fd0a0265..8e7b72fed 100644 --- a/vendor/github.com/exoscale/egoscale/network_offerings.go +++ b/vendor/github.com/exoscale/egoscale/network_offerings.go @@ -2,63 +2,51 @@ package egoscale // NetworkOffering corresponds to the Compute Offerings type NetworkOffering struct { - ID string `json:"id"` - Availability string `json:"availability,omitempty"` - ConserveMode bool `json:"conservemode,omitempty"` - Created string `json:"created"` - Details map[string]string `json:"details,omitempty"` - DisplayText string `json:"displaytext,omitempty"` - EgressDefaultPolicy bool `json:"egressdefaultpolicy,omitempty"` - ForVPC bool `json:"forvpc,omitempty"` - GuestIPType string `json:"guestiptype,omitempty"` - IsDefault bool `json:"isdefault,omitempty"` - IsPersistent bool `json:"ispersistent,omitempty"` - MaxConnections int `json:"maxconnections,omitempty"` - Name string `json:"name,omitempty"` - NetworkRate int `json:"networkrate,omitempty"` - ServiceOfferingID string `json:"serviceofferingid,omitempty"` - SpecifyIPRanges bool `json:"specifyipranges,omitempty"` - SpecifyVlan bool `json:"specifyvlan,omitempty"` - State string `json:"state"` // Disabled/Enabled/Inactive - SupportsPublicAccess bool `json:"supportspublicaccess,omitempty"` - SupportsStrechedL2Subnet bool `json:"supportsstrechedl2subnet,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - TrafficType string `json:"traffictype,omitempty"` // Public, Management, Control, ... - Service []Service `json:"service,omitempty"` + Availability string `json:"availability,omitempty" doc:"availability of the network offering"` + ConserveMode bool `json:"conservemode,omitempty" doc:"true if network offering is ip conserve mode enabled"` + Created string `json:"created,omitempty" doc:"the date this network offering was created"` + Details map[string]string `json:"details,omitempty" doc:"additional key/value details tied with network offering"` + DisplayText string `json:"displaytext,omitempty" doc:"an alternate display text of the network offering."` + EgressDefaultPolicy bool `json:"egressdefaultpolicy,omitempty" doc:"true if guest network default egress policy is allow; false if default egress policy is deny"` + GuestIPType string `json:"guestiptype,omitempty" doc:"guest type of the network offering, can be Shared or Isolated"` + ID *UUID `json:"id,omitempty" doc:"the id of the network offering"` + IsDefault bool `json:"isdefault,omitempty" doc:"true if network offering is default, false otherwise"` + IsPersistent bool `json:"ispersistent,omitempty" doc:"true if network offering supports persistent networks, false otherwise"` + MaxConnections int `json:"maxconnections,omitempty" doc:"maximum number of concurrents connections to be handled by lb"` + Name string `json:"name,omitempty" doc:"the name of the network offering"` + NetworkRate int `json:"networkrate,omitempty" doc:"data transfer rate in megabits per second allowed."` + Service []Service `json:"service,omitempty" doc:"the list of supported services"` + ServiceOfferingID *UUID `json:"serviceofferingid,omitempty" doc:"the ID of the service offering used by virtual router provider"` + SpecifyIPRanges bool `json:"specifyipranges,omitempty" doc:"true if network offering supports specifying ip ranges, false otherwise"` + SpecifyVlan bool `json:"specifyvlan,omitempty" doc:"true if network offering supports vlans, false otherwise"` + State string `json:"state,omitempty" doc:"state of the network offering. Can be Disabled/Enabled/Inactive"` + SupportsStrechedL2Subnet bool `json:"supportsstrechedl2subnet,omitempty" doc:"true if network offering supports network that span multiple zones"` + Tags string `json:"tags,omitempty" doc:"the tags for the network offering"` + TrafficType string `json:"traffictype,omitempty" doc:"the traffic type for the network offering, supported types are Public, Management, Control, Guest, Vlan or Storage."` } // ListNetworkOfferings represents a query for network offerings -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listNetworkOfferings.html type ListNetworkOfferings struct { - Availability string `json:"availability,omitempty"` - DisplayText string `json:"displaytext,omitempty"` - ForVPC bool `json:"forvpc,omitempty"` - GuestIPType string `json:"guestiptype,omitempty"` // shared of isolated - ID string `json:"id,omitempty"` - IsDefault bool `json:"isdefault,omitempty"` - IsTagged bool `json:"istagged,omitempty"` - Keyword string `json:"keyword,omitempty"` - Name string `json:"name,omitempty"` - NetworkID string `json:"networkid,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` - SourceNATSupported bool `json:"sourcenatsupported,omitempty"` - SpecifyIPRanges bool `json:"specifyipranges,omitempty"` - SpecifyVlan string `json:"specifyvlan,omitempty"` - State string `json:"state,omitempty"` - SupportedServices string `json:"supportedservices,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - TrafficType string `json:"traffictype,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListNetworkOfferings) name() string { - return "listNetworkOfferings" -} - -func (*ListNetworkOfferings) response() interface{} { - return new(ListNetworkOfferingsResponse) + Availability string `json:"availability,omitempty" doc:"the availability of network offering. Default value is Required"` + DisplayText string `json:"displaytext,omitempty" doc:"list network offerings by display text"` + GuestIPType string `json:"guestiptype,omitempty" doc:"list network offerings by guest type: Shared or Isolated"` + ID *UUID `json:"id,omitempty" doc:"list network offerings by id"` + IsDefault *bool `json:"isdefault,omitempty" doc:"true if need to list only default network offerings. Default value is false"` + IsTagged *bool `json:"istagged,omitempty" doc:"true if offering has tags specified"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Name string `json:"name,omitempty" doc:"list network offerings by name"` + NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the network. Pass this in if you want to see the available network offering that a network can be changed to."` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + SourceNATSupported *bool `json:"sourcenatsupported,omitempty" doc:"true if need to list only netwok offerings where source nat is supported, false otherwise"` + SpecifyIPRanges *bool `json:"specifyipranges,omitempty" doc:"true if need to list only network offerings which support specifying ip ranges"` + SpecifyVlan *bool `json:"specifyvlan,omitempty" doc:"the tags for the network offering."` + State string `json:"state,omitempty" doc:"list network offerings by state"` + SupportedServices []Service `json:"supportedservices,omitempty" doc:"list network offerings supporting certain services"` + Tags string `json:"tags,omitempty" doc:"list network offerings by tags"` + TrafficType string `json:"traffictype,omitempty" doc:"list by traffic type"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"list network offerings available for network creation in specific zone"` + _ bool `name:"listNetworkOfferings" description:"Lists all available network offerings."` } // ListNetworkOfferingsResponse represents a list of service offerings @@ -66,3 +54,24 @@ type ListNetworkOfferingsResponse struct { Count int `json:"count"` NetworkOffering []NetworkOffering `json:"networkoffering"` } + +func (ListNetworkOfferings) response() interface{} { + return new(ListNetworkOfferingsResponse) +} + +// UpdateNetworkOffering represents a modification of a network offering +type UpdateNetworkOffering struct { + Availability string `json:"availability,omitempty" doc:"the availability of network offering. Default value is Required for Guest Virtual network offering; Optional for Guest Direct network offering"` + DisplayText string `json:"displaytext,omitempty" doc:"the display text of the network offering"` + ID *UUID `json:"id,omitempty" doc:"the id of the network offering"` + KeepAliveEnabled *bool `json:"keepaliveenabled,omitempty" doc:"if true keepalive will be turned on in the loadbalancer. At the time of writing this has only an effect on haproxy; the mode http and httpclose options are unset in the haproxy conf file."` + MaxConnections int `json:"maxconnections,omitempty" doc:"maximum number of concurrent connections supported by the network offering"` + Name string `json:"name,omitempty" doc:"the name of the network offering"` + SortKey int `json:"sortkey,omitempty" doc:"sort key of the network offering, integer"` + State string `json:"state,omitempty" doc:"update state for the network offering"` + _ bool `name:"updateNetworkOffering" description:"Updates a network offering."` +} + +func (UpdateNetworkOffering) response() interface{} { + return new(NetworkOffering) +} diff --git a/vendor/github.com/exoscale/egoscale/networks.go b/vendor/github.com/exoscale/egoscale/networks.go index 2c4c8f0e8..89c8d0a1b 100644 --- a/vendor/github.com/exoscale/egoscale/networks.go +++ b/vendor/github.com/exoscale/egoscale/networks.go @@ -1,70 +1,93 @@ package egoscale import ( + "fmt" "net" "net/url" ) // Network represents a network +// +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html type Network struct { - ID string `json:"id"` - Account string `json:"account"` - ACLID string `json:"aclid,omitempty"` - ACLType string `json:"acltype,omitempty"` - BroadcastDomainType string `json:"broadcastdomaintype,omitempty"` - BroadcastURI string `json:"broadcasturi,omitempty"` - CanUseForDeploy bool `json:"canusefordeploy,omitempty"` - Cidr string `json:"cidr,omitempty"` - DisplayNetwork bool `json:"diplaynetwork,omitempty"` - DisplayText string `json:"displaytext"` - DNS1 net.IP `json:"dns1,omitempty"` - DNS2 net.IP `json:"dns2,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Gateway net.IP `json:"gateway,omitempty"` - IP6Cidr string `json:"ip6cidr,omitempty"` - IP6Gateway net.IP `json:"ip6gateway,omitempty"` - IsDefault bool `json:"isdefault,omitempty"` - IsPersistent bool `json:"ispersistent,omitempty"` - Name string `json:"name"` - Netmask net.IP `json:"netmask,omitempty"` - NetworkCidr string `json:"networkcidr,omitempty"` - NetworkDomain string `json:"networkdomain,omitempty"` - NetworkOfferingAvailability string `json:"networkofferingavailability,omitempty"` - NetworkOfferingConserveMode bool `json:"networkofferingconservemode,omitempty"` - NetworkOfferingDisplayText string `json:"networkofferingdisplaytext,omitempty"` - NetworkOfferingID string `json:"networkofferingid,omitempty"` - NetworkOfferingName string `json:"networkofferingname,omitempty"` - PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Related string `json:"related,omitempty"` - ReserveIPRange string `json:"reserveiprange,omitempty"` - RestartRequired bool `json:"restartrequired,omitempty"` - SpecifyIPRanges bool `json:"specifyipranges,omitempty"` - State string `json:"state"` - StrechedL2Subnet bool `json:"strechedl2subnet,omitempty"` - SubdomainAccess bool `json:"subdomainaccess,omitempty"` - TrafficType string `json:"traffictype"` - Type string `json:"type"` - Vlan string `json:"vlan,omitemtpy"` // root only - VpcID string `json:"vpcid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` - ZoneName string `json:"zonename,omitempty"` - ZonesNetworkSpans string `json:"zonesnetworkspans,omitempty"` - Service []Service `json:"service"` - Tags []ResourceTag `json:"tags"` + Account string `json:"account,omitempty" doc:"the owner of the network"` + AccountID *UUID `json:"accountid,omitempty" doc:"the owner ID of the network"` + BroadcastDomainType string `json:"broadcastdomaintype,omitempty" doc:"Broadcast domain type of the network"` + BroadcastURI string `json:"broadcasturi,omitempty" doc:"broadcast uri of the network."` + CanUseForDeploy bool `json:"canusefordeploy,omitempty" doc:"list networks available for vm deployment"` + CIDR *CIDR `json:"cidr,omitempty" doc:"Cloudstack managed address space, all CloudStack managed VMs get IP address from CIDR"` + DisplayNetwork bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."` + DisplayText string `json:"displaytext,omitempty" doc:"the displaytext of the network"` + DNS1 net.IP `json:"dns1,omitempty" doc:"the first DNS for the network"` + DNS2 net.IP `json:"dns2,omitempty" doc:"the second DNS for the network"` + Domain string `json:"domain,omitempty" doc:"the domain name of the network owner"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain id of the network owner"` + Gateway net.IP `json:"gateway,omitempty" doc:"the network's gateway"` + ID *UUID `json:"id,omitempty" doc:"the id of the network"` + IP6CIDR *CIDR `json:"ip6cidr,omitempty" doc:"the cidr of IPv6 network"` + IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of IPv6 network"` + IsDefault bool `json:"isdefault,omitempty" doc:"true if network is default, false otherwise"` + IsPersistent bool `json:"ispersistent,omitempty" doc:"list networks that are persistent"` + IsSystem bool `json:"issystem,omitempty" doc:"true if network is system, false otherwise"` + Name string `json:"name,omitempty" doc:"the name of the network"` + Netmask net.IP `json:"netmask,omitempty" doc:"the network's netmask"` + NetworkCIDR *CIDR `json:"networkcidr,omitempty" doc:"the network CIDR of the guest network configured with IP reservation. It is the summation of CIDR and RESERVED_IP_RANGE"` + NetworkDomain string `json:"networkdomain,omitempty" doc:"the network domain"` + NetworkOfferingAvailability string `json:"networkofferingavailability,omitempty" doc:"availability of the network offering the network is created from"` + NetworkOfferingConserveMode bool `json:"networkofferingconservemode,omitempty" doc:"true if network offering is ip conserve mode enabled"` + NetworkOfferingDisplayText string `json:"networkofferingdisplaytext,omitempty" doc:"display text of the network offering the network is created from"` + NetworkOfferingID *UUID `json:"networkofferingid,omitempty" doc:"network offering id the network is created from"` + NetworkOfferingName string `json:"networkofferingname,omitempty" doc:"name of the network offering the network is created from"` + PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"the physical network id"` + Related string `json:"related,omitempty" doc:"related to what other network configuration"` + ReservedIPRange string `json:"reservediprange,omitempty" doc:"the network's IP range not to be used by CloudStack guest VMs and can be used for non CloudStack purposes"` + RestartRequired bool `json:"restartrequired,omitempty" doc:"true network requires restart"` + Service []Service `json:"service,omitempty" doc:"the list of services"` + SpecifyIPRanges bool `json:"specifyipranges,omitempty" doc:"true if network supports specifying ip ranges, false otherwise"` + State string `json:"state,omitempty" doc:"state of the network"` + StrechedL2Subnet bool `json:"strechedl2subnet,omitempty" doc:"true if network can span multiple zones"` + SubdomainAccess bool `json:"subdomainaccess,omitempty" doc:"true if users from subdomains can access the domain level network"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with network"` + TrafficType string `json:"traffictype,omitempty" doc:"the traffic type of the network"` + Type string `json:"type,omitempty" doc:"the type of the network"` + Vlan string `json:"vlan,omitemtpy" doc:"The vlan of the network. This parameter is visible to ROOT admins only"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"zone id of the network"` + ZoneName string `json:"zonename,omitempty" doc:"the name of the zone the network belongs to"` + ZonesNetworkSpans []Zone `json:"zonesnetworkspans,omitempty" doc:"If a network is enabled for 'streched l2 subnet' then represents zones on which network currently spans"` +} + +// ListRequest builds the ListNetworks request +func (network Network) ListRequest() (ListCommand, error) { + //TODO add tags support + req := &ListNetworks{ + Account: network.Account, + DomainID: network.DomainID, + ID: network.ID, + PhysicalNetworkID: network.PhysicalNetworkID, + TrafficType: network.TrafficType, + Type: network.Type, + ZoneID: network.ZoneID, + } + + if network.CanUseForDeploy { + req.CanUseForDeploy = &network.CanUseForDeploy + } + if network.RestartRequired { + req.RestartRequired = &network.RestartRequired + } + + return req, nil } // ResourceType returns the type of the resource -func (*Network) ResourceType() string { +func (Network) ResourceType() string { return "Network" } // Service is a feature of a network type Service struct { - Name string `json:"name"` Capability []ServiceCapability `json:"capability,omitempty"` + Name string `json:"name"` Provider []ServiceProvider `json:"provider,omitempty"` } @@ -77,58 +100,44 @@ type ServiceCapability struct { // ServiceProvider represents the provider of the service type ServiceProvider struct { - ID string `json:"id"` CanEnableIndividualService bool `json:"canenableindividualservice"` - DestinationPhysicalNetworkID string `json:"destinationphysicalnetworkid"` + DestinationPhysicalNetworkID *UUID `json:"destinationphysicalnetworkid"` + ID *UUID `json:"id"` Name string `json:"name"` - PhysicalNetworkID string `json:"physicalnetworkid"` + PhysicalNetworkID *UUID `json:"physicalnetworkid"` ServiceList []string `json:"servicelist,omitempty"` } -// NetworkResponse represents a network -type NetworkResponse struct { - Network Network `json:"network"` -} - // CreateNetwork creates a network -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createNetwork.html type CreateNetwork struct { - DisplayText string `json:"displaytext,omitempty"` - Name string `json:"name,omitempty"` - NetworkOfferingID string `json:"networkofferingid"` - ZoneID string `json:"zoneid"` - Account string `json:"account,omitempty"` - ACLID string `json:"aclid,omitempty"` - ACLType string `json:"acltype,omitempty"` // Account or Domain - DisplayNetwork bool `json:"displaynetwork,omitempty"` // root only - DomainID string `json:"domainid,omitempty"` - EndIP net.IP `json:"endip,omitempty"` - EndIpv6 net.IP `json:"endipv6,omitempty"` - Gateway net.IP `json:"gateway,omitempty"` - IP6Cidr string `json:"ip6cidr,omitempty"` - IP6Gateway net.IP `json:"ip6gateway,omitempty"` - IsolatedPVlan string `json:"isolatedpvlan,omitempty"` - Netmask net.IP `json:"netmask,omitempty"` - NetworkDomain string `json:"networkdomain,omitempty"` - PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - StartIP net.IP `json:"startip,omitempty"` - StartIpv6 net.IP `json:"startipv6,omitempty"` - SubdomainAccess string `json:"subdomainaccess,omitempty"` - Vlan string `json:"vlan,omitempty"` - VpcID string `json:"vpcid,omitempty"` + Account string `json:"account,omitempty" doc:"account who will own the network"` + DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."` + DisplayText string `json:"displaytext,omitempty" doc:"the display text of the network"` // This field is required but might be empty + DomainID *UUID `json:"domainid,omitempty" doc:"domain ID of the account owning a network"` + EndIP net.IP `json:"endip,omitempty" doc:"the ending IP address in the network IP range. If not specified, will be defaulted to startIP"` + EndIpv6 net.IP `json:"endipv6,omitempty" doc:"the ending IPv6 address in the IPv6 network range"` + Gateway net.IP `json:"gateway,omitempty" doc:"the gateway of the network. Required for Shared networks and Isolated networks when it belongs to VPC"` + IP6CIDR *CIDR `json:"ip6cidr,omitempty" doc:"the CIDR of IPv6 network, must be at least /64"` + IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of the IPv6 network. Required for Shared networks and Isolated networks when it belongs to VPC"` + IsolatedPVlan string `json:"isolatedpvlan,omitempty" doc:"the isolated private vlan for this network"` + Name string `json:"name,omitempty" doc:"the name of the network"` // This field is required but might be empty + Netmask net.IP `json:"netmask,omitempty" doc:"the netmask of the network. Required for Shared networks and Isolated networks when it belongs to VPC"` + NetworkDomain string `json:"networkdomain,omitempty" doc:"network domain"` + NetworkOfferingID *UUID `json:"networkofferingid" doc:"the network offering id"` + PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"the Physical Network ID the network belongs to"` + StartIP net.IP `json:"startip,omitempty" doc:"the beginning IP address in the network IP range"` + StartIpv6 net.IP `json:"startipv6,omitempty" doc:"the beginning IPv6 address in the IPv6 network range"` + SubdomainAccess *bool `json:"subdomainaccess,omitempty" doc:"Defines whether to allow subdomains to use networks dedicated to their parent domain(s). Should be used with aclType=Domain, defaulted to allow.subdomain.network.access global config if not specified"` + Vlan string `json:"vlan,omitempty" doc:"the ID or VID of the network"` + ZoneID *UUID `json:"zoneid" doc:"the Zone ID for the network"` + _ bool `name:"createNetwork" description:"Creates a network"` } -func (*CreateNetwork) name() string { - return "createNetwork" +func (CreateNetwork) response() interface{} { + return new(Network) } -func (*CreateNetwork) response() interface{} { - return new(CreateNetworkResponse) -} - -func (req *CreateNetwork) onBeforeSend(params *url.Values) error { +func (req CreateNetwork) onBeforeSend(params url.Values) error { // Those fields are required but might be empty if req.Name == "" { params.Set("name", "") @@ -139,107 +148,80 @@ func (req *CreateNetwork) onBeforeSend(params *url.Values) error { return nil } -// CreateNetworkResponse represents a freshly created network -type CreateNetworkResponse NetworkResponse - -// UpdateNetwork updates a network -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateNetwork.html +// UpdateNetwork (Async) updates a network type UpdateNetwork struct { - ID string `json:"id"` - ChangeCidr bool `json:"changecidr,omitempty"` - CustomID string `json:"customid,omitempty"` // root only - DisplayNetwork string `json:"displaynetwork,omitempty"` - DisplayText string `json:"displaytext,omitempty"` - Forced bool `json:"forced,omitempty"` - GuestVMCidr string `json:"guestvmcidr,omitempty"` - Name string `json:"name,omitempty"` - NetworkDomain string `json:"networkdomain,omitempty"` - NetworkOfferingID string `json:"networkofferingid,omitempty"` - UpdateInSequence bool `json:"updateinsequence,omitempty"` + ID *UUID `json:"id" doc:"the ID of the network"` + ChangeCIDR *bool `json:"changecidr,omitempty" doc:"Force update even if cidr type is different"` + CustomID *UUID `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"` + DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"an optional field, whether to the display the network to the end user or not."` + DisplayText string `json:"displaytext,omitempty" doc:"the new display text for the network"` + GuestVMCIDR *CIDR `json:"guestvmcidr,omitempty" doc:"CIDR for Guest VMs,Cloudstack allocates IPs to Guest VMs only from this CIDR"` + Name string `json:"name,omitempty" doc:"the new name for the network"` + NetworkDomain string `json:"networkdomain,omitempty" doc:"network domain"` + NetworkOfferingID *UUID `json:"networkofferingid,omitempty" doc:"network offering ID"` + _ bool `name:"updateNetwork" description:"Updates a network"` } -func (*UpdateNetwork) name() string { - return "updateNetwork" +func (UpdateNetwork) response() interface{} { + return new(AsyncJobResult) } -func (*UpdateNetwork) asyncResponse() interface{} { - return new(UpdateNetworkResponse) +func (UpdateNetwork) asyncResponse() interface{} { + return new(Network) } -// UpdateNetworkResponse represents a freshly created network -type UpdateNetworkResponse NetworkResponse - -// RestartNetwork updates a network -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/restartNetwork.html +// RestartNetwork (Async) updates a network type RestartNetwork struct { - ID string `json:"id"` - Cleanup bool `json:"cleanup,omitempty"` + ID *UUID `json:"id" doc:"The id of the network to restart."` + Cleanup *bool `json:"cleanup,omitempty" doc:"If cleanup old network elements"` + _ bool `name:"restartNetwork" description:"Restarts the network; includes 1) restarting network elements - virtual routers, dhcp servers 2) reapplying all public ips 3) reapplying loadBalancing/portForwarding rules"` } -func (*RestartNetwork) name() string { - return "restartNetwork" +func (RestartNetwork) response() interface{} { + return new(AsyncJobResult) } -func (*RestartNetwork) asyncResponse() interface{} { - return new(RestartNetworkResponse) +func (RestartNetwork) asyncResponse() interface{} { + return new(Network) } -// RestartNetworkResponse represents a freshly created network -type RestartNetworkResponse NetworkResponse - // DeleteNetwork deletes a network -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteNetwork.html type DeleteNetwork struct { - ID string `json:"id"` - Forced bool `json:"forced,omitempty"` + ID *UUID `json:"id" doc:"the ID of the network"` + Forced *bool `json:"forced,omitempty" doc:"Force delete a network. Network will be marked as 'Destroy' even when commands to shutdown and cleanup to the backend fails."` + _ bool `name:"deleteNetwork" description:"Deletes a network"` } -func (*DeleteNetwork) name() string { - return "deleteNetwork" +func (DeleteNetwork) response() interface{} { + return new(AsyncJobResult) } -func (*DeleteNetwork) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (DeleteNetwork) asyncResponse() interface{} { + return new(booleanResponse) } // ListNetworks represents a query to a network -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listNetworks.html type ListNetworks struct { - Account string `json:"account,omitempty"` - ACLType string `json:"acltype,omitempty"` // Account or Domain - CanUseForDeploy bool `json:"canusefordeploy,omitempty"` - DisplayNetwork bool `json:"displaynetwork,omitempty"` // root only - DomainID string `json:"domainid,omitempty"` - ForVpc string `json:"forvpc,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - IsSystem bool `json:"issystem,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + CanUseForDeploy *bool `json:"canusefordeploy,omitempty" doc:"list networks available for vm deployment"` + DisplayNetwork *bool `json:"displaynetwork,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"list networks by id"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + IsSystem *bool `json:"issystem,omitempty" doc:"true if network is system, false otherwise"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - PhysicalNetworkID string `json:"physicalnetworkid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - RestartRequired bool `json:"restartrequired,omitempty"` - SpecifyRanges bool `json:"specifyranges,omitempty"` - SupportedServices []Service `json:"supportedservices,omitempty"` - Tags []ResourceTag `json:"resourcetag,omitempty"` - TrafficType string `json:"traffictype,omitempty"` - Type string `json:"type,omitempty"` - VpcID string `json:"vpcid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListNetworks) name() string { - return "listNetworks" -} - -func (*ListNetworks) response() interface{} { - return new(ListNetworksResponse) + PhysicalNetworkID *UUID `json:"physicalnetworkid,omitempty" doc:"list networks by physical network id"` + RestartRequired *bool `json:"restartrequired,omitempty" doc:"list networks by restartRequired"` + SpecifyIPRanges *bool `json:"specifyipranges,omitempty" doc:"true if need to list only networks which support specifying ip ranges"` + SupportedServices []Service `json:"supportedservices,omitempty" doc:"list networks supporting certain services"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + TrafficType string `json:"traffictype,omitempty" doc:"type of the traffic"` + Type string `json:"type,omitempty" doc:"the type of the network. Supported values are: Isolated and Shared"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the Zone ID of the network"` + _ bool `name:"listNetworks" description:"Lists all available networks."` } // ListNetworksResponse represents the list of networks @@ -247,3 +229,31 @@ type ListNetworksResponse struct { Count int `json:"count"` Network []Network `json:"network"` } + +func (ListNetworks) response() interface{} { + return new(ListNetworksResponse) +} + +// SetPage sets the current page +func (listNetwork *ListNetworks) SetPage(page int) { + listNetwork.Page = page +} + +// SetPageSize sets the page size +func (listNetwork *ListNetworks) SetPageSize(pageSize int) { + listNetwork.PageSize = pageSize +} + +func (ListNetworks) each(resp interface{}, callback IterateItemFunc) { + networks, ok := resp.(*ListNetworksResponse) + if !ok { + callback(nil, fmt.Errorf("type error: ListNetworksResponse expected, got %T", resp)) + return + } + + for i := range networks.Network { + if !callback(&networks.Network[i], nil) { + break + } + } +} diff --git a/vendor/github.com/exoscale/egoscale/nics.go b/vendor/github.com/exoscale/egoscale/nics.go index b8b405b7f..64f498232 100644 --- a/vendor/github.com/exoscale/egoscale/nics.go +++ b/vendor/github.com/exoscale/egoscale/nics.go @@ -1,57 +1,69 @@ package egoscale import ( - "fmt" + "errors" "net" ) // Nic represents a Network Interface Controller (NIC) +// +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/networking_and_traffic.html#configuring-multiple-ip-addresses-on-a-single-nic type Nic struct { - ID string `json:"id,omitempty"` - BroadcastURI string `json:"broadcasturi,omitempty"` - Gateway net.IP `json:"gateway,omitempty"` - IP6Address net.IP `json:"ip6address,omitempty"` - IP6Cidr string `json:"ip6cidr,omitempty"` - IP6Gateway net.IP `json:"ip6gateway,omitempty"` - IPAddress net.IP `json:"ipaddress,omitempty"` - IsDefault bool `json:"isdefault,omitempty"` - IsolationURI string `json:"isolationuri,omitempty"` - MacAddress string `json:"macaddress,omitempty"` - Netmask net.IP `json:"netmask,omitempty"` - NetworkID string `json:"networkid,omitempty"` - NetworkName string `json:"networkname,omitempty"` - SecondaryIP []NicSecondaryIP `json:"secondaryip,omitempty"` - Traffictype string `json:"traffictype,omitempty"` - Type string `json:"type,omitempty"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` + BroadcastURI string `json:"broadcasturi,omitempty" doc:"the broadcast uri of the nic"` + DeviceID *UUID `json:"deviceid,omitempty" doc:"device id for the network when plugged into the virtual machine"` + Gateway net.IP `json:"gateway,omitempty" doc:"the gateway of the nic"` + ID *UUID `json:"id,omitempty" doc:"the ID of the nic"` + IP6Address net.IP `json:"ip6address,omitempty" doc:"the IPv6 address of network"` + IP6CIDR *CIDR `json:"ip6cidr,omitempty" doc:"the cidr of IPv6 network"` + IP6Gateway net.IP `json:"ip6gateway,omitempty" doc:"the gateway of IPv6 network"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"the ip address of the nic"` + IsDefault bool `json:"isdefault,omitempty" doc:"true if nic is default, false otherwise"` + IsolationURI string `json:"isolationuri,omitempty" doc:"the isolation uri of the nic"` + MACAddress MACAddress `json:"macaddress,omitempty" doc:"true if nic is default, false otherwise"` + Netmask net.IP `json:"netmask,omitempty" doc:"the netmask of the nic"` + NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the corresponding network"` + NetworkName string `json:"networkname,omitempty" doc:"the name of the corresponding network"` + ReverseDNS []ReverseDNS `json:"reversedns,omitempty" doc:"the list of PTR record(s) associated with the virtual machine"` + SecondaryIP []NicSecondaryIP `json:"secondaryip,omitempty" doc:"the Secondary ipv4 addr of nic"` + TrafficType string `json:"traffictype,omitempty" doc:"the traffic type of the nic"` + Type string `json:"type,omitempty" doc:"the type of the nic"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"Id of the vm to which the nic belongs"` +} + +// ListRequest build a ListNics request from the given Nic +func (nic Nic) ListRequest() (ListCommand, error) { + if nic.VirtualMachineID == nil { + return nil, errors.New("command ListNics requires the VirtualMachineID field to be set") + } + + req := &ListNics{ + VirtualMachineID: nic.VirtualMachineID, + NicID: nic.ID, + NetworkID: nic.NetworkID, + } + + return req, nil } // NicSecondaryIP represents a link between NicID and IPAddress type NicSecondaryIP struct { - ID string `json:"id"` - IPAddress net.IP `json:"ipaddress"` - NetworkID string `json:"networkid"` - NicID string `json:"nicid"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` + ID *UUID `json:"id,omitempty" doc:"the ID of the secondary private IP addr"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"Secondary IP address"` + NetworkID *UUID `json:"networkid,omitempty" doc:"the ID of the network"` + NicID *UUID `json:"nicid,omitempty" doc:"the ID of the nic"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"the ID of the vm"` } // ListNics represents the NIC search type ListNics struct { - VirtualMachineID string `json:"virtualmachineid"` - ForDisplay bool `json:"fordisplay,omitempty"` - Keyword string `json:"keyword,omitempty"` - NetworkID string `json:"networkid,omitempty"` - NicID string `json:"nicid,omitempty"` + ForDisplay bool `json:"fordisplay,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + NetworkID *UUID `json:"networkid,omitempty" doc:"list nic of the specific vm's network"` + NicID *UUID `json:"nicid,omitempty" doc:"the ID of the nic to to list IPs"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` -} - -func (*ListNics) name() string { - return "listNics" -} - -func (*ListNics) response() interface{} { - return new(ListNicsResponse) + VirtualMachineID *UUID `json:"virtualmachineid" doc:"the ID of the vm"` + _ bool `name:"listNics" description:"list the vm nics IP to NIC"` } // ListNicsResponse represents a list of templates @@ -60,76 +72,70 @@ type ListNicsResponse struct { Nic []Nic `json:"nic"` } -// AddIPToNic represents the assignation of a secondary IP +func (ListNics) response() interface{} { + return new(ListNicsResponse) +} + +// SetPage sets the current page +func (ls *ListNics) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListNics) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListNics) each(resp interface{}, callback IterateItemFunc) { + nics := resp.(*ListNicsResponse) + for i := range nics.Nic { + if !callback(&(nics.Nic[i]), nil) { + break + } + } +} + +// AddIPToNic (Async) represents the assignation of a secondary IP type AddIPToNic struct { - NicID string `json:"nicid"` - IPAddress net.IP `json:"ipaddress"` + NicID *UUID `json:"nicid" doc:"the ID of the nic to which you want to assign private IP"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"Secondary IP Address"` + _ bool `name:"addIpToNic" description:"Assigns secondary IP to NIC"` } -func (*AddIPToNic) name() string { - return "addIpToNic" -} -func (*AddIPToNic) asyncResponse() interface{} { - return new(AddIPToNicResponse) +func (AddIPToNic) response() interface{} { + return new(AsyncJobResult) } -// AddIPToNicResponse represents the addition of an IP to a NIC -type AddIPToNicResponse struct { - NicSecondaryIP NicSecondaryIP `json:"nicsecondaryip"` +func (AddIPToNic) asyncResponse() interface{} { + return new(NicSecondaryIP) } -// RemoveIPFromNic represents a deletion request +// RemoveIPFromNic (Async) represents a deletion request type RemoveIPFromNic struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"the ID of the secondary ip address to nic"` + _ bool `name:"removeIpFromNic" description:"Removes secondary IP from the NIC."` } -func (*RemoveIPFromNic) name() string { - return "removeIpFromNic" +func (RemoveIPFromNic) response() interface{} { + return new(AsyncJobResult) } -func (*RemoveIPFromNic) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (RemoveIPFromNic) asyncResponse() interface{} { + return new(booleanResponse) } -// ListNics lists the NIC of a VM +// ActivateIP6 (Async) activates the IP6 on the given NIC // -// Deprecated: use the API directly -func (exo *Client) ListNics(req *ListNics) ([]Nic, error) { - resp, err := exo.Request(req) - if err != nil { - return nil, err - } - - return resp.(*ListNicsResponse).Nic, nil +// Exoscale specific API: https://community.exoscale.ch/api/compute/#activateip6_GET +type ActivateIP6 struct { + NicID *UUID `json:"nicid" doc:"the ID of the nic to which you want to assign the IPv6"` + _ bool `name:"activateIp6" description:"Activate the IPv6 on the VM's nic"` } -// AddIPToNic adds an IP to a NIC -// -// Deprecated: use the API directly -func (exo *Client) AddIPToNic(nicID string, ipAddress string, async AsyncInfo) (*NicSecondaryIP, error) { - ip := net.ParseIP(ipAddress) - if ip == nil { - return nil, fmt.Errorf("%s is not a valid IP address", ipAddress) - } - req := &AddIPToNic{ - NicID: nicID, - IPAddress: ip, - } - resp, err := exo.AsyncRequest(req, async) - if err != nil { - return nil, err - } - - nic := resp.(AddIPToNicResponse).NicSecondaryIP - return &nic, nil +func (ActivateIP6) response() interface{} { + return new(AsyncJobResult) } -// RemoveIPFromNic removes an IP from a NIC -// -// Deprecated: use the API directly -func (exo *Client) RemoveIPFromNic(secondaryNicID string, async AsyncInfo) error { - req := &RemoveIPFromNic{ - ID: secondaryNicID, - } - return exo.BooleanAsyncRequest(req, async) +func (ActivateIP6) asyncResponse() interface{} { + return new(Nic) } diff --git a/vendor/github.com/exoscale/egoscale/record_string.go b/vendor/github.com/exoscale/egoscale/record_string.go new file mode 100644 index 000000000..b84303bb7 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/record_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=Record"; DO NOT EDIT. + +package egoscale + +import "strconv" + +const _Record_name = "AAAAAALIASCNAMEHINFOMXNAPTRNSPOOLSPFSRVSSHFPTXTURL" + +var _Record_index = [...]uint8{0, 1, 5, 10, 15, 20, 22, 27, 29, 33, 36, 39, 44, 47, 50} + +func (i Record) String() string { + if i < 0 || i >= Record(len(_Record_index)-1) { + return "Record(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Record_name[_Record_index[i]:_Record_index[i+1]] +} diff --git a/vendor/github.com/exoscale/egoscale/request.go b/vendor/github.com/exoscale/egoscale/request.go index 641896552..e0003cf6c 100644 --- a/vendor/github.com/exoscale/egoscale/request.go +++ b/vendor/github.com/exoscale/egoscale/request.go @@ -2,334 +2,380 @@ package egoscale import ( "bytes" + "context" "crypto/hmac" "crypto/sha1" "encoding/base64" "encoding/json" "fmt" + "io" "io/ioutil" - "log" - "net" "net/http" "net/url" - "reflect" "sort" "strconv" "strings" "time" ) -// Command represent a CloudStack request -type Command interface { - // CloudStack API command name - name() string - // Response interface to Unmarshal the JSON into - response() interface{} -} - -// AsyncCommand represents a async CloudStack request -type AsyncCommand interface { - // CloudStack API command name - name() string - // Response interface to Unmarshal the JSON into - asyncResponse() interface{} -} - -// Command represents an action to be done on the params before sending them -// -// This little took helps with issue of relying on JSON serialization logic only. -// `omitempty` may make sense in some cases but not all the time. -type onBeforeHook interface { - onBeforeSend(params *url.Values) error -} - -const ( - // Pending represents a job in progress - Pending JobStatusType = iota - // Success represents a successfully completed job - Success - // Failure represents a job that has failed to complete - Failure -) - -// JobStatusType represents the status of a Job -type JobStatusType int - -const ( - // Unauthorized represents ... (TODO) - Unauthorized ErrorCode = 401 - // MethodNotAllowed represents ... (TODO) - MethodNotAllowed = 405 - // UnsupportedActionError represents ... (TODO) - UnsupportedActionError = 422 - // APILimitExceeded represents ... (TODO) - APILimitExceeded = 429 - // MalformedParameterError represents ... (TODO) - MalformedParameterError = 430 - // ParamError represents ... (TODO) - ParamError = 431 - - // InternalError represents a server error - InternalError = 530 - // AccountError represents ... (TODO) - AccountError = 531 - // AccountResourceLimitError represents ... (TODO) - AccountResourceLimitError = 532 - // InsufficientCapacityError represents ... (TODO) - InsufficientCapacityError = 533 - // ResourceUnavailableError represents ... (TODO) - ResourceUnavailableError = 534 - // ResourceAllocationError represents ... (TODO) - ResourceAllocationError = 535 - // ResourceInUseError represents ... (TODO) - ResourceInUseError = 536 - // NetworkRuleConflictError represents ... (TODO) - NetworkRuleConflictError = 537 -) - -// ErrorCode represents the CloudStack ApiErrorCode enum -// -// See: https://github.com/apache/cloudstack/blob/master/api/src/org/apache/cloudstack/api/ApiErrorCode.java -type ErrorCode int - -// JobResultResponse represents a generic response to a job task -type JobResultResponse struct { - AccountID string `json:"accountid,omitempty"` - Cmd string `json:"cmd"` - Created string `json:"created"` - JobID string `json:"jobid"` - JobProcStatus int `json:"jobprocstatus"` - JobResult *json.RawMessage `json:"jobresult"` - JobStatus JobStatusType `json:"jobstatus"` - JobResultType string `json:"jobresulttype"` - UserID string `json:"userid,omitempty"` -} - -// ErrorResponse represents the standard error response from CloudStack -type ErrorResponse struct { - ErrorCode ErrorCode `json:"errorcode"` - CsErrorCode int `json:"cserrorcode"` - ErrorText string `json:"errortext"` - UUIDList []string `json:"uuidList,omitempty"` // uuid*L*ist is not a typo -} - // Error formats a CloudStack error into a standard error -func (e *ErrorResponse) Error() string { - return fmt.Sprintf("API error %d (internal code: %d): %s", e.ErrorCode, e.CsErrorCode, e.ErrorText) -} - -// booleanAsyncResponse represents a boolean response (usually after a deletion) -type booleanAsyncResponse struct { - Success bool `json:"success"` - DisplayText string `json:"diplaytext,omitempty"` +func (e ErrorResponse) Error() string { + return fmt.Sprintf("API error %s %d (%s %d): %s", e.ErrorCode, e.ErrorCode, e.CSErrorCode, e.CSErrorCode, e.ErrorText) } // Error formats a CloudStack job response into a standard error -func (e *booleanAsyncResponse) Error() error { - if e.Success { - return nil - } - return fmt.Errorf("API error: %s", e.DisplayText) -} - -// booleanAsyncResponse represents a boolean response for sync calls -type booleanSyncResponse struct { - Success string `json:"success"` - DisplayText string `json:"displaytext,omitempty"` -} - -func (e *booleanSyncResponse) Error() error { - if e.Success == "true" { - return nil +func (e booleanResponse) Error() error { + if !e.Success { + return fmt.Errorf("API error: %s", e.DisplayText) } - return fmt.Errorf("API error: %s", e.DisplayText) + return nil } -// AsyncInfo represents the details for any async call -// -// It retries at most Retries time and waits for Delay between each retry -type AsyncInfo struct { - Retries int - Delay int +// XXX: addIpToNic, activateIp6, restorevmresponse are kind of special +var responseKeys = map[string]string{ + "addiptonicresponse": "addiptovmnicresponse", + "activateip6response": "activateip6nicresponse", + "restorevirtualmachineresponse": "restorevmresponse", } -func csQuotePlus(s string) string { - s = strings.Replace(s, "+", "%20", -1) - s = strings.Replace(s, "%5B", "[", -1) - s = strings.Replace(s, "%5D", "]", -1) - return s -} - -func csEncode(s string) string { - return csQuotePlus(url.QueryEscape(s)) -} - -func rawValue(b json.RawMessage) (json.RawMessage, error) { - var m map[string]json.RawMessage - - if err := json.Unmarshal(b, &m); err != nil { - return nil, err - } - for _, v := range m { - return v, nil - } - return nil, nil -} - -func rawValues(b json.RawMessage) (json.RawMessage, error) { - var i []json.RawMessage - - if err := json.Unmarshal(b, &i); err != nil { - return nil, nil - } - - return i[0], nil -} - -func (exo *Client) parseResponse(resp *http.Response) (json.RawMessage, error) { +func (client *Client) parseResponse(resp *http.Response, apiName string) (json.RawMessage, error) { b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } - a, err := rawValues(b) + m := map[string]json.RawMessage{} + if err := json.Unmarshal(b, &m); err != nil { + return nil, err + } - if a == nil { - b, err = rawValue(b) - if err != nil { - return nil, err + key := fmt.Sprintf("%sresponse", strings.ToLower(apiName)) + response, ok := m[key] + if !ok { + if resp.StatusCode >= 400 { + response, ok = m["errorresponse"] + } + + if !ok { + // try again with the special keys + value, ok := responseKeys[key] + if ok { + key = value + } + + response, ok = m[key] + + if !ok { + for k := range m { + return nil, fmt.Errorf("malformed JSON response, %q was expected, got %q", key, k) + } + } } } if resp.StatusCode >= 400 { - var e ErrorResponse - if err := json.Unmarshal(b, &e); err != nil { - return nil, err + errorResponse := new(ErrorResponse) + if e := json.Unmarshal(response, errorResponse); e != nil && errorResponse.ErrorCode <= 0 { + return nil, fmt.Errorf("%d %s", resp.StatusCode, b) } - return b, &e + return nil, errorResponse } - return b, nil + + n := map[string]json.RawMessage{} + if err := json.Unmarshal(response, &n); err != nil { + return nil, err + } + + // list response may contain only one key + if len(n) > 1 || strings.HasPrefix(key, "list") { + return response, nil + } + + if len(n) == 1 { + for k := range n { + // boolean response and asyncjob result may also contain + // only one key + if k == "success" || k == "jobid" { + return response, nil + } + return n[k], nil + } + } + + return response, nil } -// AsyncRequest performs an asynchronous request and polls it for retries * day [s] -func (exo *Client) AsyncRequest(req AsyncCommand, async AsyncInfo) (interface{}, error) { - body, err := exo.request(req.name(), req) +// asyncRequest perform an asynchronous job with a context +func (client *Client) asyncRequest(ctx context.Context, asyncCommand AsyncCommand) (interface{}, error) { + var err error + + resp := asyncCommand.asyncResponse() + client.AsyncRequestWithContext( + ctx, + asyncCommand, + func(j *AsyncJobResult, e error) bool { + if e != nil { + err = e + return false + } + if j.JobStatus != Pending { + if r := j.Result(resp); r != nil { + err = r + } + return false + } + return true + }, + ) + return resp, err +} + +// SyncRequestWithContext performs a sync request with a context +func (client *Client) SyncRequestWithContext(ctx context.Context, command Command) (interface{}, error) { + body, err := client.request(ctx, command) if err != nil { return nil, err } - // Is it a Job? - job := new(JobResultResponse) - if err := json.Unmarshal(body, &job); err != nil { + response := command.response() + b, ok := response.(*booleanResponse) + if ok { + m := make(map[string]interface{}) + if errUnmarshal := json.Unmarshal(body, &m); errUnmarshal != nil { + return nil, errUnmarshal + } + + b.DisplayText, _ = m["displaytext"].(string) + + if success, okSuccess := m["success"].(string); okSuccess { + b.Success = success == "true" + } + + if success, okSuccess := m["success"].(bool); okSuccess { + b.Success = success + } + + return b, nil + } + + if err := json.Unmarshal(body, response); err != nil { + errResponse := new(ErrorResponse) + if e := json.Unmarshal(body, errResponse); e == nil && errResponse.ErrorCode > 0 { + return errResponse, nil + } return nil, err } - // Error response - errorResponse := new(ErrorResponse) + return response, nil +} + +// BooleanRequest performs the given boolean command +func (client *Client) BooleanRequest(command Command) error { + resp, err := client.Request(command) + if err != nil { + return err + } + + if b, ok := resp.(*booleanResponse); ok { + return b.Error() + } + + panic(fmt.Errorf("command %q is not a proper boolean response. %#v", client.APIName(command), resp)) +} + +// BooleanRequestWithContext performs the given boolean command +func (client *Client) BooleanRequestWithContext(ctx context.Context, command Command) error { + resp, err := client.RequestWithContext(ctx, command) + if err != nil { + return err + } + + if b, ok := resp.(*booleanResponse); ok { + return b.Error() + } + + panic(fmt.Errorf("command %q is not a proper boolean response. %#v", client.APIName(command), resp)) +} + +// Request performs the given command +func (client *Client) Request(command Command) (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + return client.RequestWithContext(ctx, command) +} + +// RequestWithContext preforms a command with a context +func (client *Client) RequestWithContext(ctx context.Context, command Command) (interface{}, error) { + switch command.(type) { + case AsyncCommand: + return client.asyncRequest(ctx, command.(AsyncCommand)) + default: + return client.SyncRequestWithContext(ctx, command) + } +} + +// SyncRequest performs the command as is +func (client *Client) SyncRequest(command Command) (interface{}, error) { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + return client.SyncRequestWithContext(ctx, command) +} + +// AsyncRequest performs the given command +func (client *Client) AsyncRequest(asyncCommand AsyncCommand, callback WaitAsyncJobResultFunc) { + ctx, cancel := context.WithTimeout(context.Background(), client.Timeout) + defer cancel() + + client.AsyncRequestWithContext(ctx, asyncCommand, callback) +} + +// AsyncRequestWithContext preforms a request with a context +func (client *Client) AsyncRequestWithContext(ctx context.Context, asyncCommand AsyncCommand, callback WaitAsyncJobResultFunc) { + result, err := client.SyncRequestWithContext(ctx, asyncCommand) + if err != nil { + if !callback(nil, err) { + return + } + } + + jobResult, ok := result.(*AsyncJobResult) + if !ok { + callback(nil, fmt.Errorf("wrong type, AsyncJobResult was expected instead of %T", result)) + } + // Successful response - resp := req.asyncResponse() - if job.JobID == "" || job.JobStatus != Pending { - if err := json.Unmarshal(*job.JobResult, resp); err != nil { - return job, err + if jobResult.JobID == nil || jobResult.JobStatus != Pending { + callback(jobResult, nil) + // without a JobID, the next requests will only fail + return + } + + for iteration := 0; ; iteration++ { + time.Sleep(client.RetryStrategy(int64(iteration))) + + req := &QueryAsyncJobResult{JobID: jobResult.JobID} + resp, err := client.SyncRequestWithContext(ctx, req) + if err != nil && !callback(nil, err) { + return } - return resp, nil - } - // we've got a pending job - result := &QueryAsyncJobResultResponse{ - JobStatus: job.JobStatus, - } - for async.Retries > 0 && result.JobStatus == Pending { - time.Sleep(time.Duration(async.Delay) * time.Second) - - async.Retries-- - - req := &QueryAsyncJobResult{JobID: job.JobID} - resp, err := exo.Request(req) - if err != nil { - return nil, err + result, ok := resp.(*AsyncJobResult) + if !ok { + if !callback(nil, fmt.Errorf("wrong type. AsyncJobResult expected, got %T", resp)) { + return + } } - result = resp.(*QueryAsyncJobResultResponse) - } - if result.JobStatus == Failure { - if err := json.Unmarshal(*result.JobResult, &errorResponse); err != nil { - return nil, err + if !callback(result, nil) { + return } - return errorResponse, errorResponse } - - if result.JobStatus == Pending { - return result, fmt.Errorf("Maximum number of retries reached") - } - - if err := json.Unmarshal(*result.JobResult, resp); err != nil { - if err := json.Unmarshal(*result.JobResult, errorResponse); err != nil { - return nil, err - } - return errorResponse, errorResponse - } - - return resp, nil } -// BooleanRequest performs a sync request on a boolean call -func (exo *Client) BooleanRequest(req Command) error { - resp, err := exo.Request(req) +// Payload builds the HTTP request params from the given command +func (client *Client) Payload(command Command) (url.Values, error) { + params := url.Values{} + err := prepareValues("", params, command) if err != nil { - return err + return params, err } - - return resp.(*booleanSyncResponse).Error() -} - -// BooleanAsyncRequest performs a sync request on a boolean call -func (exo *Client) BooleanAsyncRequest(req AsyncCommand, async AsyncInfo) error { - resp, err := exo.AsyncRequest(req, async) - if err != nil { - return err - } - - return resp.(*booleanAsyncResponse).Error() -} - -// Request performs a sync request on a generic command -func (exo *Client) Request(req Command) (interface{}, error) { - body, err := exo.request(req.name(), req) - if err != nil { - return nil, err - } - - resp := req.response() - if err := json.Unmarshal(body, resp); err != nil { - r := new(ErrorResponse) - if e := json.Unmarshal(body, r); e != nil { - return nil, r + if hookReq, ok := command.(onBeforeHook); ok { + if err := hookReq.onBeforeSend(params); err != nil { + return params, err } - return nil, err + } + params.Set("apikey", client.APIKey) + params.Set("command", client.APIName(command)) + params.Set("response", "json") + + return params, nil +} + +// Sign signs the HTTP request and returns the signature as as base64 encoding +func (client *Client) Sign(params url.Values) (string, error) { + query := encodeValues(params) + query = strings.ToLower(query) + mac := hmac.New(sha1.New, []byte(client.apiSecret)) + _, err := mac.Write([]byte(query)) + if err != nil { + return "", err } - return resp, nil + signature := base64.StdEncoding.EncodeToString(mac.Sum(nil)) + return signature, nil } // request makes a Request while being close to the metal -func (exo *Client) request(command string, req interface{}) (json.RawMessage, error) { - params := url.Values{} - err := prepareValues("", ¶ms, req) +func (client *Client) request(ctx context.Context, command Command) (json.RawMessage, error) { + params, err := client.Payload(command) if err != nil { return nil, err } - if hookReq, ok := req.(onBeforeHook); ok { - hookReq.onBeforeSend(¶ms) + signature, err := client.Sign(params) + if err != nil { + return nil, err } - params.Set("apikey", exo.apiKey) - params.Set("command", command) - params.Set("response", "json") + params.Add("signature", signature) + method := "GET" + query := params.Encode() + url := fmt.Sprintf("%s?%s", client.Endpoint, query) + + var body io.Reader + // respect Internet Explorer limit of 2048 + if len(url) > 2048 { + url = client.Endpoint + method = "POST" + body = strings.NewReader(query) + } + + request, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + request = request.WithContext(ctx) + request.Header.Add("User-Agent", fmt.Sprintf("exoscale/egoscale (%v)", Version)) + + if method == "POST" { + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + request.Header.Add("Content-Length", strconv.Itoa(len(query))) + } + + resp, err := client.HTTPClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() // nolint: errcheck + + contentType := resp.Header.Get("content-type") + + if !strings.Contains(contentType, "application/json") { + return nil, fmt.Errorf(`body content-type response expected "application/json", got %q`, contentType) + } + + text, err := client.parseResponse(resp, client.APIName(command)) + if err != nil { + return nil, err + } + + return text, nil +} + +func encodeValues(params url.Values) string { // This code is borrowed from net/url/url.go // The way it's encoded by net/url doesn't match - // how CloudStack works. + // how CloudStack works to determine the signature. + // + // CloudStack only encodes the values of the query parameters + // and furthermore doesn't use '+' for whitespaces. Therefore + // after encoding the values all '+' are replaced with '%20'. + if params == nil { + return "" + } + var buf bytes.Buffer keys := make([]string, 0, len(params)) for k := range params { @@ -338,7 +384,7 @@ func (exo *Client) request(command string, req interface{}) (json.RawMessage, er sort.Strings(keys) for _, k := range keys { - prefix := csEncode(k) + "=" + prefix := k + "=" for _, v := range params[k] { if buf.Len() > 0 { buf.WriteByte('&') @@ -347,218 +393,5 @@ func (exo *Client) request(command string, req interface{}) (json.RawMessage, er buf.WriteString(csEncode(v)) } } - - query := buf.String() - - mac := hmac.New(sha1.New, []byte(exo.apiSecret)) - mac.Write([]byte(strings.ToLower(query))) - signature := csEncode(base64.StdEncoding.EncodeToString(mac.Sum(nil))) - - reader := strings.NewReader(fmt.Sprintf("%s&signature=%s", csQuotePlus(query), signature)) - resp, err := exo.client.Post(exo.endpoint, "application/x-www-form-urlencoded", reader) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := exo.parseResponse(resp) - if err != nil { - return nil, err - } - - return body, nil -} - -// prepareValues uses a command to build a POST request -// -// command is not a Command so it's easier to Test -func prepareValues(prefix string, params *url.Values, command interface{}) error { - value := reflect.ValueOf(command) - typeof := reflect.TypeOf(command) - // Going up the pointer chain to find the underlying struct - for typeof.Kind() == reflect.Ptr { - typeof = typeof.Elem() - value = value.Elem() - } - - for i := 0; i < typeof.NumField(); i++ { - field := typeof.Field(i) - val := value.Field(i) - tag := field.Tag - if json, ok := tag.Lookup("json"); ok { - n, required := extractJSONTag(field.Name, json) - name := prefix + n - - switch val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v := val.Int() - if v == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), field.Name, val.Kind()) - } - } else { - (*params).Set(name, strconv.FormatInt(v, 10)) - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - v := val.Uint() - if v == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), field.Name, val.Kind()) - } - } else { - (*params).Set(name, strconv.FormatUint(v, 10)) - } - case reflect.Float32, reflect.Float64: - v := val.Float() - if v == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), field.Name, val.Kind()) - } - } else { - (*params).Set(name, strconv.FormatFloat(v, 'f', -1, 64)) - } - case reflect.String: - v := val.String() - if v == "" { - if required { - return fmt.Errorf("%s.%s (%v) is required, got \"\"", typeof.Name(), field.Name, val.Kind()) - } - } else { - (*params).Set(name, v) - } - case reflect.Bool: - v := val.Bool() - if v == false { - if required { - params.Set(name, "false") - } - } else { - (*params).Set(name, "true") - } - case reflect.Slice: - switch field.Type.Elem().Kind() { - case reflect.Uint8: - switch field.Type { - case reflect.TypeOf(net.IPv4zero): - ip := (net.IP)(val.Bytes()) - if ip == nil || ip.Equal(net.IPv4zero) { - if required { - return fmt.Errorf("%s.%s (%v) is required, got zero IPv4 address", typeof.Name(), field.Name, val.Kind()) - } - } else { - (*params).Set(name, ip.String()) - } - default: - if val.Len() == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), field.Name, val.Kind()) - } - } else { - v := val.Bytes() - (*params).Set(name, base64.StdEncoding.EncodeToString(v)) - } - } - case reflect.String: - { - if val.Len() == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), field.Name, val.Kind()) - } - } else { - elems := make([]string, 0, val.Len()) - for i := 0; i < val.Len(); i++ { - // XXX what if the value contains a comma? Double encode? - s := val.Index(i).String() - elems = append(elems, s) - } - (*params).Set(name, strings.Join(elems, ",")) - } - } - default: - if val.Len() == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), field.Name, val.Kind()) - } - } else { - err := prepareList(name, params, val.Interface()) - if err != nil { - return err - } - } - } - case reflect.Map: - if val.Len() == 0 { - if required { - return fmt.Errorf("%s.%s (%v) is required, got empty map", typeof.Name(), field.Name, val.Kind()) - } - } else { - err := prepareMap(name, params, val.Interface()) - if err != nil { - return err - } - } - default: - if required { - return fmt.Errorf("Unsupported type %s.%s (%v)", typeof.Name(), field.Name, val.Kind()) - } - } - } else { - log.Printf("[SKIP] %s.%s no json label found", typeof.Name(), field.Name) - } - } - - return nil -} - -func prepareList(prefix string, params *url.Values, slice interface{}) error { - value := reflect.ValueOf(slice) - - for i := 0; i < value.Len(); i++ { - prepareValues(fmt.Sprintf("%s[%d].", prefix, i), params, value.Index(i).Interface()) - } - - return nil -} - -func prepareMap(prefix string, params *url.Values, m interface{}) error { - value := reflect.ValueOf(m) - - for i, key := range value.MapKeys() { - var keyName string - var keyValue string - - switch key.Kind() { - case reflect.String: - keyName = key.String() - default: - return fmt.Errorf("Only map[string]string are supported (XXX)") - } - - val := value.MapIndex(key) - switch val.Kind() { - case reflect.String: - keyValue = val.String() - default: - return fmt.Errorf("Only map[string]string are supported (XXX)") - } - params.Set(fmt.Sprintf("%s[%d].%s", prefix, i, keyName), keyValue) - } - return nil -} - -// extractJSONTag returns the variable name or defaultName as well as if the field is required (!omitempty) -func extractJSONTag(defaultName, jsonTag string) (string, bool) { - tags := strings.Split(jsonTag, ",") - name := tags[0] - required := true - for _, tag := range tags { - if tag == "omitempty" { - required = false - } - } - - if name == "" || name == "omitempty" { - name = defaultName - } - return name, required + return buf.String() } diff --git a/vendor/github.com/exoscale/egoscale/request_type.go b/vendor/github.com/exoscale/egoscale/request_type.go new file mode 100644 index 000000000..974a4f9ee --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/request_type.go @@ -0,0 +1,184 @@ +package egoscale + +import ( + "net/url" +) + +// Command represents a CloudStack request +type Command interface { + response() interface{} +} + +// AsyncCommand represents a async CloudStack request +type AsyncCommand interface { + Command + // Response interface to Unmarshal the JSON into + asyncResponse() interface{} +} + +// ListCommand represents a CloudStack list request +type ListCommand interface { + Command + // SetPage defines the current pages + SetPage(int) + // SetPageSize defines the size of the page + SetPageSize(int) + // each reads the data from the response and feeds channels, and returns true if we are on the last page + each(interface{}, IterateItemFunc) +} + +// onBeforeHook represents an action to be done on the params before sending them +// +// This little took helps with issue of relying on JSON serialization logic only. +// `omitempty` may make sense in some cases but not all the time. +type onBeforeHook interface { + onBeforeSend(params url.Values) error +} + +// CommandInfo represents the meta data related to a Command +type CommandInfo struct { + Name string + Description string + RootOnly bool +} + +// JobStatusType represents the status of a Job +type JobStatusType int + +//go:generate stringer -type JobStatusType +const ( + // Pending represents a job in progress + Pending JobStatusType = iota + // Success represents a successfully completed job + Success + // Failure represents a job that has failed to complete + Failure +) + +// ErrorCode represents the CloudStack ApiErrorCode enum +// +// See: https://github.com/apache/cloudstack/blob/master/api/src/org/apache/cloudstack/api/ApiErrorCode.java +type ErrorCode int + +//go:generate stringer -type ErrorCode +const ( + // Unauthorized represents ... (TODO) + Unauthorized ErrorCode = 401 + // MethodNotAllowed represents ... (TODO) + MethodNotAllowed ErrorCode = 405 + // UnsupportedActionError represents ... (TODO) + UnsupportedActionError ErrorCode = 422 + // APILimitExceeded represents ... (TODO) + APILimitExceeded ErrorCode = 429 + // MalformedParameterError represents ... (TODO) + MalformedParameterError ErrorCode = 430 + // ParamError represents ... (TODO) + ParamError ErrorCode = 431 + + // InternalError represents a server error + InternalError ErrorCode = 530 + // AccountError represents ... (TODO) + AccountError ErrorCode = 531 + // AccountResourceLimitError represents ... (TODO) + AccountResourceLimitError ErrorCode = 532 + // InsufficientCapacityError represents ... (TODO) + InsufficientCapacityError ErrorCode = 533 + // ResourceUnavailableError represents ... (TODO) + ResourceUnavailableError ErrorCode = 534 + // ResourceAllocationError represents ... (TODO) + ResourceAllocationError ErrorCode = 535 + // ResourceInUseError represents ... (TODO) + ResourceInUseError ErrorCode = 536 + // NetworkRuleConflictError represents ... (TODO) + NetworkRuleConflictError ErrorCode = 537 +) + +// CSErrorCode represents the CloudStack CSExceptionErrorCode enum +// +// See: https://github.com/apache/cloudstack/blob/master/utils/src/main/java/com/cloud/utils/exception/CSExceptionErrorCode.java +type CSErrorCode int + +//go:generate stringer -type CSErrorCode +const ( + // CloudRuntimeException ... (TODO) + CloudRuntimeException CSErrorCode = 4250 + // ExecutionException ... (TODO) + ExecutionException CSErrorCode = 4260 + // HypervisorVersionChangedException ... (TODO) + HypervisorVersionChangedException CSErrorCode = 4265 + // CloudException ... (TODO) + CloudException CSErrorCode = 4275 + // AccountLimitException ... (TODO) + AccountLimitException CSErrorCode = 4280 + // AgentUnavailableException ... (TODO) + AgentUnavailableException CSErrorCode = 4285 + // CloudAuthenticationException ... (TODO) + CloudAuthenticationException CSErrorCode = 4290 + // ConcurrentOperationException ... (TODO) + ConcurrentOperationException CSErrorCode = 4300 + // ConflictingNetworksException ... (TODO) + ConflictingNetworkSettingsException CSErrorCode = 4305 + // DiscoveredWithErrorException ... (TODO) + DiscoveredWithErrorException CSErrorCode = 4310 + // HAStateException ... (TODO) + HAStateException CSErrorCode = 4315 + // InsufficientAddressCapacityException ... (TODO) + InsufficientAddressCapacityException CSErrorCode = 4320 + // InsufficientCapacityException ... (TODO) + InsufficientCapacityException CSErrorCode = 4325 + // InsufficientNetworkCapacityException ... (TODO) + InsufficientNetworkCapacityException CSErrorCode = 4330 + // InsufficientServerCapaticyException ... (TODO) + InsufficientServerCapacityException CSErrorCode = 4335 + // InsufficientStorageCapacityException ... (TODO) + InsufficientStorageCapacityException CSErrorCode = 4340 + // InternalErrorException ... (TODO) + InternalErrorException CSErrorCode = 4345 + // InvalidParameterValueException ... (TODO) + InvalidParameterValueException CSErrorCode = 4350 + // ManagementServerException ... (TODO) + ManagementServerException CSErrorCode = 4355 + // NetworkRuleConflictException ... (TODO) + NetworkRuleConflictException CSErrorCode = 4360 + // PermissionDeniedException ... (TODO) + PermissionDeniedException CSErrorCode = 4365 + // ResourceAllocationException ... (TODO) + ResourceAllocationException CSErrorCode = 4370 + // ResourceInUseException ... (TODO) + ResourceInUseException CSErrorCode = 4375 + // ResourceUnavailableException ... (TODO) + ResourceUnavailableException CSErrorCode = 4380 + // StorageUnavailableException ... (TODO) + StorageUnavailableException CSErrorCode = 4385 + // UnsupportedServiceException ... (TODO) + UnsupportedServiceException CSErrorCode = 4390 + // VirtualMachineMigrationException ... (TODO) + VirtualMachineMigrationException CSErrorCode = 4395 + // AsyncCommandQueued ... (TODO) + AsyncCommandQueued CSErrorCode = 4540 + // RequestLimitException ... (TODO) + RequestLimitException CSErrorCode = 4545 + // ServerAPIException ... (TODO) + ServerAPIException CSErrorCode = 9999 +) + +// ErrorResponse represents the standard error response from CloudStack +type ErrorResponse struct { + CSErrorCode CSErrorCode `json:"cserrorcode"` + ErrorCode ErrorCode `json:"errorcode"` + ErrorText string `json:"errortext"` + UUIDList []UUIDItem `json:"uuidList,omitempty"` // uuid*L*ist is not a typo +} + +// UUIDItem represents an item of the UUIDList part of an ErrorResponse +type UUIDItem struct { + Description string `json:"description,omitempty"` + SerialVersionUID int64 `json:"serialVersionUID,omitempty"` + UUID string `json:"uuid"` +} + +// booleanResponse represents a boolean response (usually after a deletion) +type booleanResponse struct { + DisplayText string `json:"displaytext,omitempty"` + Success bool `json:"success"` +} diff --git a/vendor/github.com/exoscale/egoscale/resource_metadata.go b/vendor/github.com/exoscale/egoscale/resource_metadata.go new file mode 100644 index 000000000..31ebde609 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/resource_metadata.go @@ -0,0 +1,36 @@ +package egoscale + +// ListResourceDetails lists the resource tag(s) (but different from listTags...) +type ListResourceDetails struct { + ResourceType string `json:"resourcetype" doc:"list by resource type"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ForDisplay bool `json:"fordisplay,omitempty" doc:"if set to true, only details marked with display=true, are returned. False by default"` + Key string `json:"key,omitempty" doc:"list by key"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + ResourceID *UUID `json:"resourceid,omitempty" doc:"list by resource id"` + Value string `json:"value,omitempty" doc:"list by key, value. Needs to be passed only along with key"` + IsRecursive bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + _ bool `name:"listResourceDetails" description:"List resource detail(s)"` +} + +// ListResourceDetailsResponse represents a list of resource details +type ListResourceDetailsResponse struct { + Count int `json:"count"` + ResourceDetail []ResourceTag `json:"resourcedetail"` +} + +func (*ListResourceDetails) name() string { + return "listResourceDetails" +} + +func (*ListResourceDetails) description() string { + return "List resource detail(s)" +} + +func (*ListResourceDetails) response() interface{} { + return new(ListResourceDetailsResponse) +} diff --git a/vendor/github.com/exoscale/egoscale/reversedns.go b/vendor/github.com/exoscale/egoscale/reversedns.go new file mode 100644 index 000000000..05e4ca927 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/reversedns.go @@ -0,0 +1,77 @@ +package egoscale + +import ( + "net" +) + +// ReverseDNS represents the PTR record linked with an IPAddress or IP6Address belonging to a Virtual Machine or a Public IP Address (Elastic IP) instance +type ReverseDNS struct { + DomainName string `json:"domainname,omitempty" doc:"the domain name of the PTR record"` + IP6Address net.IP `json:"ip6address,omitempty" doc:"the IPv6 address linked with the PTR record (mutually exclusive with ipaddress)"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"the IPv4 address linked with the PTR record (mutually exclusive with ip6address)"` + NicID *UUID `json:"nicid,omitempty" doc:"the virtual machine default NIC ID"` + PublicIPID *UUID `json:"publicipid,omitempty" doc:"the public IP address ID"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"the virtual machine ID"` +} + +// DeleteReverseDNSFromPublicIPAddress is a command to create/delete the PTR record of a public IP address +type DeleteReverseDNSFromPublicIPAddress struct { + ID *UUID `json:"id,omitempty" doc:"the ID of the public IP address"` + _ bool `name:"deleteReverseDnsFromPublicIpAddress" description:"delete the PTR DNS record from the public IP address"` +} + +func (*DeleteReverseDNSFromPublicIPAddress) response() interface{} { + return new(booleanResponse) +} + +// DeleteReverseDNSFromVirtualMachine is a command to create/delete the PTR record(s) of a virtual machine +type DeleteReverseDNSFromVirtualMachine struct { + ID *UUID `json:"id,omitempty" doc:"the ID of the virtual machine"` + _ bool `name:"deleteReverseDnsFromVirtualMachine" description:"Delete the PTR DNS record(s) from the virtual machine"` +} + +func (*DeleteReverseDNSFromVirtualMachine) response() interface{} { + return new(booleanResponse) +} + +// QueryReverseDNSForPublicIPAddress is a command to create/query the PTR record of a public IP address +type QueryReverseDNSForPublicIPAddress struct { + ID *UUID `json:"id,omitempty" doc:"the ID of the public IP address"` + _ bool `name:"queryReverseDnsForPublicIpAddress" description:"Query the PTR DNS record for the public IP address"` +} + +func (*QueryReverseDNSForPublicIPAddress) response() interface{} { + return new(IPAddress) +} + +// QueryReverseDNSForVirtualMachine is a command to create/query the PTR record(s) of a virtual machine +type QueryReverseDNSForVirtualMachine struct { + ID *UUID `json:"id,omitempty" doc:"the ID of the virtual machine"` + _ bool `name:"queryReverseDnsForVirtualMachine" description:"Query the PTR DNS record(s) for the virtual machine"` +} + +func (*QueryReverseDNSForVirtualMachine) response() interface{} { + return new(VirtualMachine) +} + +// UpdateReverseDNSForPublicIPAddress is a command to create/update the PTR record of a public IP address +type UpdateReverseDNSForPublicIPAddress struct { + DomainName string `json:"domainname,omitempty" doc:"the domain name for the PTR record. It must have a valid TLD"` + ID *UUID `json:"id,omitempty" doc:"the ID of the public IP address"` + _ bool `name:"updateReverseDnsForPublicIpAddress" description:"Update/create the PTR DNS record for the public IP address"` +} + +func (*UpdateReverseDNSForPublicIPAddress) response() interface{} { + return new(IPAddress) +} + +// UpdateReverseDNSForVirtualMachine is a command to create/update the PTR record(s) of a virtual machine +type UpdateReverseDNSForVirtualMachine struct { + DomainName string `json:"domainname,omitempty" doc:"the domain name for the PTR record(s). It must have a valid TLD"` + ID *UUID `json:"id,omitempty" doc:"the ID of the virtual machine"` + _ bool `name:"updateReverseDnsForVirtualMachine" description:"Update/create the PTR DNS record(s) for the virtual machine"` +} + +func (*UpdateReverseDNSForVirtualMachine) response() interface{} { + return new(VirtualMachine) +} diff --git a/vendor/github.com/exoscale/egoscale/security_groups.go b/vendor/github.com/exoscale/egoscale/security_groups.go index 452083725..78b06c438 100644 --- a/vendor/github.com/exoscale/egoscale/security_groups.go +++ b/vendor/github.com/exoscale/egoscale/security_groups.go @@ -1,51 +1,104 @@ package egoscale import ( + "context" + "fmt" "net/url" "strconv" + "strings" ) // SecurityGroup represent a firewalling set of rules type SecurityGroup struct { - ID string `json:"id"` - Account string `json:"account,omitempty"` - Description string `json:"description,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Name string `json:"name"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - VirtualMachineCount int `json:"virtualmachinecount,omitempty"` // CloudStack 4.6+ - VirtualMachineIDs []string `json:"virtualmachineids,omitempty"` // CloudStack 4.6+ - IngressRule []IngressRule `json:"ingressrule"` - EgressRule []EgressRule `json:"egressrule"` - Tags []ResourceTag `json:"tags,omitempty"` - JobID string `json:"jobid,omitempty"` - JobStatus JobStatusType `json:"jobstatus,omitempty"` + Account string `json:"account,omitempty" doc:"the account owning the security group"` + Description string `json:"description,omitempty" doc:"the description of the security group"` + Domain string `json:"domain,omitempty" doc:"the domain name of the security group"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of the security group"` + EgressRule []EgressRule `json:"egressrule,omitempty" doc:"the list of egress rules associated with the security group"` + ID *UUID `json:"id,omitempty" doc:"the ID of the security group"` + IngressRule []IngressRule `json:"ingressrule,omitempty" doc:"the list of ingress rules associated with the security group"` + Name string `json:"name,omitempty" doc:"the name of the security group"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with the rule"` } // ResourceType returns the type of the resource -func (*SecurityGroup) ResourceType() string { +func (SecurityGroup) ResourceType() string { return "SecurityGroup" } +// UserSecurityGroup converts a SecurityGroup to a UserSecurityGroup +func (sg SecurityGroup) UserSecurityGroup() UserSecurityGroup { + return UserSecurityGroup{ + Account: sg.Account, + Group: sg.Name, + } +} + +// ListRequest builds the ListSecurityGroups request +func (sg SecurityGroup) ListRequest() (ListCommand, error) { + //TODO add tags + req := &ListSecurityGroups{ + Account: sg.Account, + DomainID: sg.DomainID, + ID: sg.ID, + SecurityGroupName: sg.Name, + } + + return req, nil +} + +// Delete deletes the given Security Group +func (sg SecurityGroup) Delete(ctx context.Context, client *Client) error { + if sg.ID == nil && sg.Name == "" { + return fmt.Errorf("a SecurityGroup may only be deleted using ID or Name") + } + + req := &DeleteSecurityGroup{ + Account: sg.Account, + DomainID: sg.DomainID, + } + + if sg.ID != nil { + req.ID = sg.ID + } else { + req.Name = sg.Name + } + + return client.BooleanRequestWithContext(ctx, req) +} + +// RuleByID returns IngressRule or EgressRule by a rule ID +func (sg SecurityGroup) RuleByID(ruleID UUID) (*IngressRule, *EgressRule) { + for i, in := range sg.IngressRule { + if in.RuleID.Equal(ruleID) { + return &sg.IngressRule[i], nil + } + } + + for i, out := range sg.EgressRule { + if out.RuleID.Equal(ruleID) { + return nil, &sg.EgressRule[i] + } + } + + return nil, nil +} + // IngressRule represents the ingress rule type IngressRule struct { - RuleID string `json:"ruleid"` - Account string `json:"account,omitempty"` - Cidr string `json:"cidr,omitempty"` - Description string `json:"description,omitempty"` - IcmpType int `json:"icmptype,omitempty"` - IcmpCode int `json:"icmpcode,omitempty"` - StartPort int `json:"startport,omitempty"` - EndPort int `json:"endport,omitempty"` - Protocol string `json:"protocol,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - SecurityGroupID string `json:"securitygroupid,omitempty"` - SecurityGroupName string `json:"securitygroupname,omitempty"` + Account string `json:"account,omitempty" doc:"account owning the security group rule"` + CIDR *CIDR `json:"cidr,omitempty" doc:"the CIDR notation for the base IP address of the security group rule"` + Description string `json:"description,omitempty" doc:"description of the security group rule"` + EndPort uint16 `json:"endport,omitempty" doc:"the ending port of the security group rule "` + IcmpCode uint8 `json:"icmpcode,omitempty" doc:"the code for the ICMP message response"` + IcmpType uint8 `json:"icmptype,omitempty" doc:"the type of the ICMP message response"` + Protocol string `json:"protocol,omitempty" doc:"the protocol of the security group rule"` + RuleID *UUID `json:"ruleid,omitempty" doc:"the id of the security group rule"` + SecurityGroupID *UUID `json:"securitygroupid,omitempty"` + SecurityGroupName string `json:"securitygroupname,omitempty" doc:"security group name"` + StartPort uint16 `json:"startport,omitempty" doc:"the starting port of the security group rule"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with the rule"` UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"` - JobID string `json:"jobid,omitempty"` - JobStatus JobStatusType `json:"jobstatus,omitempty"` } // EgressRule represents the ingress rule @@ -57,165 +110,131 @@ type UserSecurityGroup struct { Account string `json:"account,omitempty"` } -// SecurityGroupResponse represents a generic security group response -type SecurityGroupResponse struct { - SecurityGroup SecurityGroup `json:"securitygroup"` +// String gives the UserSecurityGroup name +func (usg UserSecurityGroup) String() string { + return usg.Group } // CreateSecurityGroup represents a security group creation -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/createSecurityGroup.html type CreateSecurityGroup struct { - Name string `json:"name"` - Description string `json:"description,omitempty"` + Name string `json:"name" doc:"name of the security group"` + Account string `json:"account,omitempty" doc:"an optional account for the security group. Must be used with domainId."` + Description string `json:"description,omitempty" doc:"the description of the security group"` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainId for the security group. If the account parameter is used, domainId must also be used."` + _ bool `name:"createSecurityGroup" description:"Creates a security group"` } -func (*CreateSecurityGroup) name() string { - return "createSecurityGroup" +func (CreateSecurityGroup) response() interface{} { + return new(SecurityGroup) } -func (*CreateSecurityGroup) response() interface{} { - return new(CreateSecurityGroupResponse) -} - -// CreateSecurityGroupResponse represents a new security group -type CreateSecurityGroupResponse SecurityGroupResponse - // DeleteSecurityGroup represents a security group deletion -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/deleteSecurityGroup.html type DeleteSecurityGroup struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` // Mutually exclusive with name - Name string `json:"name,omitempty"` // Mutually exclusive with id - ProjectID string `json:"project,omitempty"` + Account string `json:"account,omitempty" doc:"the account of the security group. Must be specified with domain ID"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of account owning the security group"` + ID *UUID `json:"id,omitempty" doc:"The ID of the security group. Mutually exclusive with name parameter"` + Name string `json:"name,omitempty" doc:"The ID of the security group. Mutually exclusive with id parameter"` + _ bool `name:"deleteSecurityGroup" description:"Deletes security group"` } -func (*DeleteSecurityGroup) name() string { - return "deleteSecurityGroup" -} - -func (*DeleteSecurityGroup) response() interface{} { - return new(booleanSyncResponse) +func (DeleteSecurityGroup) response() interface{} { + return new(booleanResponse) } // AuthorizeSecurityGroupIngress (Async) represents the ingress rule creation -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/authorizeSecurityGroupIngress.html type AuthorizeSecurityGroupIngress struct { - Account string `json:"account,omitempty"` - CidrList []string `json:"cidrlist,omitempty"` - Description string `json:"description,omitempty"` - DomainID string `json:"domainid,omitempty"` - IcmpType int `json:"icmptype,omitempty"` - IcmpCode int `json:"icmpcode,omitempty"` - StartPort int `json:"startport,omitempty"` - EndPort int `json:"endport,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Protocol string `json:"protocol,omitempty"` - SecurityGroupID string `json:"securitygroupid,omitempty"` - SecurityGroupName string `json:"securitygroupname,omitempty"` - UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty"` + Account string `json:"account,omitempty" doc:"an optional account for the security group. Must be used with domainId."` + CIDRList []CIDR `json:"cidrlist,omitempty" doc:"the cidr list associated"` + Description string `json:"description,omitempty" doc:"the description of the ingress/egress rule"` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainid for the security group. If the account parameter is used, domainid must also be used."` + EndPort uint16 `json:"endport,omitempty" doc:"end port for this ingress rule"` + IcmpCode uint8 `json:"icmpcode,omitempty" doc:"error code for this icmp message"` + IcmpType uint8 `json:"icmptype,omitempty" doc:"type of the icmp message being sent"` + Protocol string `json:"protocol,omitempty" doc:"TCP is default. UDP, ICMP, ICMPv6, AH, ESP, GRE are the other supported protocols"` + SecurityGroupID *UUID `json:"securitygroupid,omitempty" doc:"The ID of the security group. Mutually exclusive with securitygroupname parameter"` + SecurityGroupName string `json:"securitygroupname,omitempty" doc:"The name of the security group. Mutually exclusive with securitygroupid parameter"` + StartPort uint16 `json:"startport,omitempty" doc:"start port for this ingress rule"` + UserSecurityGroupList []UserSecurityGroup `json:"usersecuritygrouplist,omitempty" doc:"user to security group mapping"` + _ bool `name:"authorizeSecurityGroupIngress" description:"Authorizes a particular ingress/egress rule for this security group"` } -func (*AuthorizeSecurityGroupIngress) name() string { - return "authorizeSecurityGroupIngress" +func (AuthorizeSecurityGroupIngress) response() interface{} { + return new(AsyncJobResult) } -func (*AuthorizeSecurityGroupIngress) asyncResponse() interface{} { - return new(AuthorizeSecurityGroupIngressResponse) +func (AuthorizeSecurityGroupIngress) asyncResponse() interface{} { + return new(SecurityGroup) } -func (req *AuthorizeSecurityGroupIngress) onBeforeSend(params *url.Values) error { +func (req AuthorizeSecurityGroupIngress) onBeforeSend(params url.Values) error { // ICMP code and type may be zero but can also be omitted... - if req.Protocol == "ICMP" { + if strings.HasPrefix(strings.ToLower(req.Protocol), "icmp") { params.Set("icmpcode", strconv.FormatInt(int64(req.IcmpCode), 10)) params.Set("icmptype", strconv.FormatInt(int64(req.IcmpType), 10)) } + // StartPort may be zero but can also be omitted... + if req.EndPort != 0 && req.StartPort == 0 { + params.Set("startport", "0") + } return nil } -// AuthorizeSecurityGroupIngressResponse represents the new egress rule -// /!\ the Cloud Stack API document is not fully accurate. /!\ -type AuthorizeSecurityGroupIngressResponse SecurityGroupResponse - // AuthorizeSecurityGroupEgress (Async) represents the egress rule creation -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/authorizeSecurityGroupEgress.html type AuthorizeSecurityGroupEgress AuthorizeSecurityGroupIngress -func (*AuthorizeSecurityGroupEgress) name() string { - return "authorizeSecurityGroupEgress" +func (AuthorizeSecurityGroupEgress) response() interface{} { + return new(AsyncJobResult) } -func (*AuthorizeSecurityGroupEgress) asyncResponse() interface{} { - return new(AuthorizeSecurityGroupEgressResponse) +func (AuthorizeSecurityGroupEgress) asyncResponse() interface{} { + return new(SecurityGroup) } -func (req *AuthorizeSecurityGroupEgress) onBeforeSend(params *url.Values) error { - return (*AuthorizeSecurityGroupIngress)(req).onBeforeSend(params) +func (req AuthorizeSecurityGroupEgress) onBeforeSend(params url.Values) error { + return (AuthorizeSecurityGroupIngress)(req).onBeforeSend(params) } -// AuthorizeSecurityGroupEgressResponse represents the new egress rule -// /!\ the Cloud Stack API document is not fully accurate. /!\ -type AuthorizeSecurityGroupEgressResponse CreateSecurityGroupResponse - // RevokeSecurityGroupIngress (Async) represents the ingress/egress rule deletion -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/revokeSecurityGroupIngress.html type RevokeSecurityGroupIngress struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the ingress rule"` + _ bool `name:"revokeSecurityGroupIngress" description:"Deletes a particular ingress rule from this security group"` } -func (*RevokeSecurityGroupIngress) name() string { - return "revokeSecurityGroupIngress" +func (RevokeSecurityGroupIngress) response() interface{} { + return new(AsyncJobResult) } - -func (*RevokeSecurityGroupIngress) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (RevokeSecurityGroupIngress) asyncResponse() interface{} { + return new(booleanResponse) } // RevokeSecurityGroupEgress (Async) represents the ingress/egress rule deletion -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/revokeSecurityGroupEgress.html type RevokeSecurityGroupEgress struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the egress rule"` + _ bool `name:"revokeSecurityGroupEgress" description:"Deletes a particular egress rule from this security group"` } -func (*RevokeSecurityGroupEgress) name() string { - return "revokeSecurityGroupEgress" +func (RevokeSecurityGroupEgress) response() interface{} { + return new(AsyncJobResult) } -func (*RevokeSecurityGroupEgress) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (RevokeSecurityGroupEgress) asyncResponse() interface{} { + return new(booleanResponse) } // ListSecurityGroups represents a search for security groups -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listSecurityGroups.html type ListSecurityGroups struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"list the security group by the id provided"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Type string `json:"type,omitempty"` - SecurityGroupName string `json:"securitygroupname,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` -} - -func (*ListSecurityGroups) name() string { - return "listSecurityGroups" -} - -func (*ListSecurityGroups) response() interface{} { - return new(ListSecurityGroupsResponse) + SecurityGroupName string `json:"securitygroupname,omitempty" doc:"lists security groups by name"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"lists security groups by virtual machine id"` + _ bool `name:"listSecurityGroups" description:"Lists security groups"` } // ListSecurityGroupsResponse represents a list of security groups @@ -224,79 +243,30 @@ type ListSecurityGroupsResponse struct { SecurityGroup []SecurityGroup `json:"securitygroup"` } -// CreateIngressRule creates a set of ingress rules -// -// Deprecated: use the API directly -func (exo *Client) CreateIngressRule(req *AuthorizeSecurityGroupIngress, async AsyncInfo) ([]IngressRule, error) { - resp, err := exo.AsyncRequest(req, async) - if err != nil { - return nil, err - } - return resp.(*AuthorizeSecurityGroupIngressResponse).SecurityGroup.IngressRule, nil +func (ListSecurityGroups) response() interface{} { + return new(ListSecurityGroupsResponse) } -// CreateEgressRule creates a set of egress rules -// -// Deprecated: use the API directly -func (exo *Client) CreateEgressRule(req *AuthorizeSecurityGroupEgress, async AsyncInfo) ([]EgressRule, error) { - resp, err := exo.AsyncRequest(req, async) - if err != nil { - return nil, err - } - return resp.(*AuthorizeSecurityGroupEgressResponse).SecurityGroup.EgressRule, nil +// SetPage sets the current page +func (lsg *ListSecurityGroups) SetPage(page int) { + lsg.Page = page } -// CreateSecurityGroupWithRules create a security group with its rules -// Warning: it doesn't rollback in case of a failure! -// -// Deprecated: use the API directly -func (exo *Client) CreateSecurityGroupWithRules(name string, ingress []AuthorizeSecurityGroupIngress, egress []AuthorizeSecurityGroupEgress, async AsyncInfo) (*SecurityGroup, error) { - req := &CreateSecurityGroup{ - Name: name, - } - resp, err := exo.Request(req) - if err != nil { - return nil, err +// SetPageSize sets the page size +func (lsg *ListSecurityGroups) SetPageSize(pageSize int) { + lsg.PageSize = pageSize +} + +func (ListSecurityGroups) each(resp interface{}, callback IterateItemFunc) { + sgs, ok := resp.(*ListSecurityGroupsResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListSecurityGroupsResponse expected, got %T", resp)) + return } - sg := resp.(*SecurityGroupResponse).SecurityGroup - reqs := make([]AsyncCommand, 0, len(ingress)+len(egress)) - // Egress rules - for _, ereq := range egress { - ereq.SecurityGroupID = sg.ID - reqs = append(reqs, &ereq) - - } - // Ingress rules - for _, ireq := range ingress { - ireq.SecurityGroupID = sg.ID - reqs = append(reqs, &ireq) - } - - for _, r := range reqs { - _, err := exo.AsyncRequest(r, async) - if err != nil { - return nil, err + for i := range sgs.SecurityGroup { + if !callback(&sgs.SecurityGroup[i], nil) { + break } } - - r, err := exo.Request(&ListSecurityGroups{ - ID: sg.ID, - }) - if err != nil { - return nil, err - } - - sg = r.(*ListSecurityGroupsResponse).SecurityGroup[0] - return &sg, nil -} - -// DeleteSecurityGroup deletes a security group -// -// Deprecated: use the API directly -func (exo *Client) DeleteSecurityGroup(name string) error { - req := &DeleteSecurityGroup{ - Name: name, - } - return exo.BooleanRequest(req) } diff --git a/vendor/github.com/exoscale/egoscale/serialization.go b/vendor/github.com/exoscale/egoscale/serialization.go new file mode 100644 index 000000000..c2342057d --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/serialization.go @@ -0,0 +1,328 @@ +package egoscale + +import ( + "encoding/base64" + "fmt" + "log" + "net" + "net/url" + "reflect" + "strconv" + "strings" +) + +func csQuotePlus(s string) string { + s = strings.Replace(s, "+", "%20", -1) + return s +} + +func csEncode(s string) string { + return csQuotePlus(url.QueryEscape(s)) +} + +// info returns the meta info of a command +// +// command is not a Command so it's easier to Test +func info(command interface{}) (*CommandInfo, error) { + typeof := reflect.TypeOf(command) + + // Going up the pointer chain to find the underlying struct + for typeof.Kind() == reflect.Ptr { + typeof = typeof.Elem() + } + + field, ok := typeof.FieldByName("_") + if !ok { + return nil, fmt.Errorf(`missing meta ("_") field in %#v`, command) + } + + name, nameOk := field.Tag.Lookup("name") + description, _ := field.Tag.Lookup("description") + + if !nameOk { + return nil, fmt.Errorf(`missing "name" key in the tag string of %#v`, command) + } + + info := &CommandInfo{ + Name: name, + Description: description, + } + + return info, nil +} + +// prepareValues uses a command to build a POST request +// +// command is not a Command so it's easier to Test +func prepareValues(prefix string, params url.Values, command interface{}) error { + value := reflect.ValueOf(command) + typeof := reflect.TypeOf(command) + + // Going up the pointer chain to find the underlying struct + for typeof.Kind() == reflect.Ptr { + typeof = typeof.Elem() + value = value.Elem() + } + + // Checking for nil commands + if !value.IsValid() { + return fmt.Errorf("cannot serialize the invalid value %#v", command) + } + + for i := 0; i < typeof.NumField(); i++ { + field := typeof.Field(i) + if field.Name == "_" { + continue + } + + val := value.Field(i) + tag := field.Tag + if json, ok := tag.Lookup("json"); ok { + n, required := ExtractJSONTag(field.Name, json) + name := prefix + n + + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v := val.Int() + if v == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), n, val.Kind()) + } + } else { + params.Set(name, strconv.FormatInt(v, 10)) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v := val.Uint() + if v == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), n, val.Kind()) + } + } else { + params.Set(name, strconv.FormatUint(v, 10)) + } + case reflect.Float32, reflect.Float64: + v := val.Float() + if v == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got 0", typeof.Name(), n, val.Kind()) + } + } else { + params.Set(name, strconv.FormatFloat(v, 'f', -1, 64)) + } + case reflect.String: + v := val.String() + if v == "" { + if required { + return fmt.Errorf("%s.%s (%v) is required, got \"\"", typeof.Name(), n, val.Kind()) + } + } else { + params.Set(name, v) + } + case reflect.Bool: + v := val.Bool() + if !v { + if required { + params.Set(name, "false") + } + } else { + params.Set(name, "true") + } + case reflect.Ptr: + if val.IsNil() { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty ptr", typeof.Name(), n, val.Kind()) + } + } else { + switch field.Type.Elem().Kind() { + case reflect.Bool: + params.Set(name, strconv.FormatBool(val.Elem().Bool())) + case reflect.Struct: + i := val.Interface() + s, ok := i.(fmt.Stringer) + if !ok { + return fmt.Errorf("%s.%s (%v) is not a Stringer", typeof.Name(), field.Name, val.Kind()) + } + if s != nil && s.String() == "" { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty value", typeof.Name(), field.Name, val.Kind()) + } + } else { + params.Set(n, s.String()) + } + default: + log.Printf("[SKIP] %s.%s (%v) not supported", typeof.Name(), n, field.Type.Elem().Kind()) + } + } + case reflect.Slice: + switch field.Type.Elem().Kind() { + case reflect.Uint8: + switch field.Type { + case reflect.TypeOf(net.IPv4zero): + ip := (net.IP)(val.Bytes()) + if ip == nil || ip.Equal(net.IPv4zero) { + if required { + return fmt.Errorf("%s.%s (%v) is required, got zero IPv4 address", typeof.Name(), n, val.Kind()) + } + } else { + params.Set(name, ip.String()) + } + case reflect.TypeOf(MAC48(0, 0, 0, 0, 0, 0)): + mac := val.Interface().(MACAddress) + s := mac.String() + if s == "" { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty MAC address", typeof.Name(), field.Name, val.Kind()) + } + } else { + params.Set(name, s) + } + default: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), n, val.Kind()) + } + } else { + v := val.Bytes() + params.Set(name, base64.StdEncoding.EncodeToString(v)) + } + } + case reflect.String: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), n, val.Kind()) + } + } else { + elems := make([]string, 0, val.Len()) + for i := 0; i < val.Len(); i++ { + // XXX what if the value contains a comma? Double encode? + s := val.Index(i).String() + elems = append(elems, s) + } + params.Set(name, strings.Join(elems, ",")) + } + default: + switch field.Type.Elem() { + case reflect.TypeOf(CIDR{}), reflect.TypeOf(UUID{}): + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), n, val.Kind()) + } + } else { + value := reflect.ValueOf(val.Interface()) + ss := make([]string, val.Len()) + for i := 0; i < value.Len(); i++ { + v := value.Index(i).Interface() + s, ok := v.(fmt.Stringer) + if !ok { + return fmt.Errorf("not a String, %T", v) + } + ss[i] = s.String() + } + params.Set(name, strings.Join(ss, ",")) + } + default: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty slice", typeof.Name(), n, val.Kind()) + } + } else { + err := prepareList(name, params, val.Interface()) + if err != nil { + return err + } + } + } + } + case reflect.Map: + if val.Len() == 0 { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty map", typeof.Name(), field.Name, val.Kind()) + } + } else { + err := prepareMap(name, params, val.Interface()) + if err != nil { + return err + } + } + case reflect.Struct: + i := val.Interface() + s, ok := i.(fmt.Stringer) + if !ok { + return fmt.Errorf("%s.%s (%v) is not a Stringer", typeof.Name(), field.Name, val.Kind()) + } + if s != nil && s.String() == "" { + if required { + return fmt.Errorf("%s.%s (%v) is required, got empty value", typeof.Name(), field.Name, val.Kind()) + } + } else { + params.Set(n, s.String()) + } + default: + if required { + return fmt.Errorf("unsupported type %s.%s (%v)", typeof.Name(), n, val.Kind()) + } + fmt.Printf("%s\n", val.Kind()) + } + } else { + log.Printf("[SKIP] %s.%s no json label found", typeof.Name(), field.Name) + } + } + + return nil +} + +func prepareList(prefix string, params url.Values, slice interface{}) error { + value := reflect.ValueOf(slice) + + for i := 0; i < value.Len(); i++ { + err := prepareValues(fmt.Sprintf("%s[%d].", prefix, i), params, value.Index(i).Interface()) + if err != nil { + return err + } + } + + return nil +} + +func prepareMap(prefix string, params url.Values, m interface{}) error { + value := reflect.ValueOf(m) + + for i, key := range value.MapKeys() { + var keyName string + var keyValue string + + switch key.Kind() { + case reflect.String: + keyName = key.String() + default: + return fmt.Errorf("only map[string]string are supported (XXX)") + } + + val := value.MapIndex(key) + switch val.Kind() { + case reflect.String: + keyValue = val.String() + default: + return fmt.Errorf("only map[string]string are supported (XXX)") + } + params.Set(fmt.Sprintf("%s[%d].%s", prefix, i, keyName), keyValue) + } + return nil +} + +// ExtractJSONTag returns the variable name or defaultName as well as if the field is required (!omitempty) +func ExtractJSONTag(defaultName, jsonTag string) (string, bool) { + tags := strings.Split(jsonTag, ",") + name := tags[0] + required := true + for _, tag := range tags { + if tag == "omitempty" { + required = false + } + } + + if name == "" || name == "omitempty" { + name = defaultName + } + return name, required +} diff --git a/vendor/github.com/exoscale/egoscale/service_offerings.go b/vendor/github.com/exoscale/egoscale/service_offerings.go index 3c9a0b4ab..c5a636348 100644 --- a/vendor/github.com/exoscale/egoscale/service_offerings.go +++ b/vendor/github.com/exoscale/egoscale/service_offerings.go @@ -1,61 +1,79 @@ package egoscale +import ( + "fmt" +) + // ServiceOffering corresponds to the Compute Offerings +// +// A service offering correspond to some hardware features (CPU, RAM). +// +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/service_offerings.html type ServiceOffering struct { - ID string `json:"id"` - CPUNumber int `json:"cpunumber"` - CPUSpeed int `json:"cpuspeed"` - Created string `json:"created"` - DefaultUse bool `json:"defaultuse,omitempty"` - DeploymentPlanner string `json:"deploymentplanner,omitempty"` - DiskBytesReadRate int64 `json:"diskBytesReadRate,omitempty"` - DiskBytesWriteRate int64 `json:"diskBytesWriteRate,omitempty"` - DiskIopsReadRate int64 `json:"diskIopsReadRate,omitempty"` - DiskIopsWriteRate int64 `json:"diskIopsWriteRate,omitempty"` - DisplayText string `json:"displaytext,omitempty"` - Domain string `json:"domain"` - DomainID string `json:"domainid"` - HostTags string `json:"hosttags,omitempty"` - HypervisorSnapshotReserve int `json:"hypervisorsnapshotreserve,omitempty"` - IsCustomized bool `json:"iscustomized,omitempty"` - IsCustomizedIops bool `json:"iscustomizediops,omitempty"` - IsSystem bool `json:"issystem,omitempty"` - IsVolatile bool `json:"isvolatile,omitempty"` - LimitCPUUse bool `json:"limitcpuuse,omitempty"` - MaxIops int64 `json:"maxiops,omitempty"` - Memory int `json:"memory,omitempty"` - MinIops int64 `json:"miniops,omitempty"` - Name string `json:"name,omitempty"` - NetworkRate int `json:"networkrate,omitempty"` - OfferHA bool `json:"offerha,omitempty"` - ServiceOfferingDetails map[string]string `json:"serviceofferingdetails,omitempty"` - StorageType string `json:"storagetype,omitempty"` - SystemVMType string `json:"systemvmtype,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` + Authorized bool `json:"authorized,omitempty" doc:"is the account/domain authorized to use this service offering"` + CPUNumber int `json:"cpunumber,omitempty" doc:"the number of CPU"` + CPUSpeed int `json:"cpuspeed,omitempty" doc:"the clock rate CPU speed in Mhz"` + Created string `json:"created,omitempty" doc:"the date this service offering was created"` + DefaultUse bool `json:"defaultuse,omitempty" doc:"is this a default system vm offering"` + DeploymentPlanner string `json:"deploymentplanner,omitempty" doc:"deployment strategy used to deploy VM."` + DiskBytesReadRate int64 `json:"diskBytesReadRate,omitempty" doc:"bytes read rate of the service offering"` + DiskBytesWriteRate int64 `json:"diskBytesWriteRate,omitempty" doc:"bytes write rate of the service offering"` + DiskIopsReadRate int64 `json:"diskIopsReadRate,omitempty" doc:"io requests read rate of the service offering"` + DiskIopsWriteRate int64 `json:"diskIopsWriteRate,omitempty" doc:"io requests write rate of the service offering"` + Displaytext string `json:"displaytext,omitempty" doc:"an alternate display text of the service offering."` + Domain string `json:"domain,omitempty" doc:"Domain name for the offering"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain id of the service offering"` + HostTags string `json:"hosttags,omitempty" doc:"the host tag for the service offering"` + HypervisorSnapshotReserve int `json:"hypervisorsnapshotreserve,omitempty" doc:"Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)"` + ID *UUID `json:"id,omitempty" doc:"the id of the service offering"` + IsCustomized bool `json:"iscustomized,omitempty" doc:"is true if the offering is customized"` + IsCustomizedIops bool `json:"iscustomizediops,omitempty" doc:"true if disk offering uses custom iops, false otherwise"` + IsSystem bool `json:"issystem,omitempty" doc:"is this a system vm offering"` + IsVolatile bool `json:"isvolatile,omitempty" doc:"true if the vm needs to be volatile, i.e., on every reboot of vm from API root disk is discarded and creates a new root disk"` + LimitCPUUse bool `json:"limitcpuuse,omitempty" doc:"restrict the CPU usage to committed service offering"` + MaxIops int64 `json:"maxiops,omitempty" doc:"the max iops of the disk offering"` + Memory int `json:"memory,omitempty" doc:"the memory in MB"` + MinIops int64 `json:"miniops,omitempty" doc:"the min iops of the disk offering"` + Name string `json:"name,omitempty" doc:"the name of the service offering"` + NetworkRate int `json:"networkrate,omitempty" doc:"data transfer rate in megabits per second allowed."` + OfferHA bool `json:"offerha,omitempty" doc:"the ha support in the service offering"` + Restricted bool `json:"restricted,omitempty" doc:"is this offering restricted"` + ServiceOfferingDetails map[string]string `json:"serviceofferingdetails,omitempty" doc:"additional key/value details tied with this service offering"` + StorageType string `json:"storagetype,omitempty" doc:"the storage type for this service offering"` + SystemVMType string `json:"systemvmtype,omitempty" doc:"is this a the systemvm type for system vm offering"` + Tags string `json:"tags,omitempty" doc:"the tags for the service offering"` +} + +// ListRequest builds the ListSecurityGroups request +func (so ServiceOffering) ListRequest() (ListCommand, error) { + // Restricted cannot be applied here because it really has three states + req := &ListServiceOfferings{ + ID: so.ID, + DomainID: so.DomainID, + Name: so.Name, + SystemVMType: so.SystemVMType, + } + + if so.IsSystem { + req.IsSystem = &so.IsSystem + } + + return req, nil } // ListServiceOfferings represents a query for service offerings -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listServiceOfferings.html type ListServiceOfferings struct { - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - IsSystem bool `json:"issystem,omitempty"` - Keyword string `json:"keyword,omitempty"` - Name string `json:"name,omitempty"` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain associated with the service offering"` + ID *UUID `json:"id,omitempty" doc:"ID of the service offering"` + IsSystem *bool `json:"issystem,omitempty" doc:"is this a system vm offering"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Name string `json:"name,omitempty" doc:"name of the service offering"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - SystemVMType string `json:"systemvmtype,omitempty"` // consoleproxy, secondarystoragevm, or domainrouter - VirtualMachineID string `json:"virtualmachineid,omitempty"` -} - -func (*ListServiceOfferings) name() string { - return "listServiceOfferings" -} - -func (*ListServiceOfferings) response() interface{} { - return new(ListServiceOfferingsResponse) + Restricted *bool `json:"restricted,omitempty" doc:"filter by the restriction flag: true to list only the restricted service offerings, false to list non-restricted service offerings, or nothing for all."` + SystemVMType string `json:"systemvmtype,omitempty" doc:"the system VM type. Possible types are \"consoleproxy\", \"secondarystoragevm\" or \"domainrouter\"."` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"the ID of the virtual machine. Pass this in if you want to see the available service offering that a virtual machine can be changed to."` + _ bool `name:"listServiceOfferings" description:"Lists all available service offerings."` } // ListServiceOfferingsResponse represents a list of service offerings @@ -63,3 +81,31 @@ type ListServiceOfferingsResponse struct { Count int `json:"count"` ServiceOffering []ServiceOffering `json:"serviceoffering"` } + +func (ListServiceOfferings) response() interface{} { + return new(ListServiceOfferingsResponse) +} + +// SetPage sets the current page +func (lso *ListServiceOfferings) SetPage(page int) { + lso.Page = page +} + +// SetPageSize sets the page size +func (lso *ListServiceOfferings) SetPageSize(pageSize int) { + lso.PageSize = pageSize +} + +func (ListServiceOfferings) each(resp interface{}, callback IterateItemFunc) { + sos, ok := resp.(*ListServiceOfferingsResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListServiceOfferingsResponse expected, got %T", resp)) + return + } + + for i := range sos.ServiceOffering { + if !callback(&sos.ServiceOffering[i], nil) { + break + } + } +} diff --git a/vendor/github.com/exoscale/egoscale/snapshots.go b/vendor/github.com/exoscale/egoscale/snapshots.go index 5766dc693..716cd252a 100644 --- a/vendor/github.com/exoscale/egoscale/snapshots.go +++ b/vendor/github.com/exoscale/egoscale/snapshots.go @@ -1,86 +1,93 @@ package egoscale +// SnapshotState represents the Snapshot.State enum +// +// See: https://github.com/apache/cloudstack/blob/master/api/src/main/java/com/cloud/storage/Snapshot.java +type SnapshotState int + +//go:generate stringer -type SnapshotState +const ( + // Allocated ... (TODO) + Allocated SnapshotState = iota + // Creating ... (TODO) + Creating + // CreatedOnPrimary ... (TODO) + CreatedOnPrimary + // BackingUp ... (TODO) + BackingUp + // BackedUp ... (TODO) + BackedUp + // Copying ... (TODO) + Copying + // Destroying ... (TODO) + Destroying + // Destroyed ... (TODO) + Destroyed + // Error is a state where the user can't see the snapshot while the snapshot may still exist on the storage + Error +) + // Snapshot represents a volume snapshot type Snapshot struct { - ID string `json:"id"` - Account string `json:"account"` - Created string `json:"created,omitempty"` - Domain string `json:"domain"` - DomainID string `json:"domainid"` - IntervalType string `json:"intervaltype,omitempty"` // hourly, daily, weekly, monthly, ..., none - Name string `json:"name,omitempty"` - PhysicalSize int64 `json:"physicalsize"` - Project string `json:"project"` - ProjectID string `json:"projectid"` - Revertable bool `json:"revertable,omitempty"` - Size int64 `json:"size,omitempty"` - SnapshotType string `json:"snapshottype,omitempty"` - State string `json:"state"` // BackedUp, Creating, BackingUp, ... - VolumeID string `json:"volumeid"` - VolumeName string `json:"volumename,omitempty"` - VolumeType string `json:"volumetype,omitempty"` - ZoneID string `json:"zoneid"` - Tags []ResourceTag `json:"tags"` - JobID string `json:"jobid,omitempty"` - JobStatus JobStatusType `json:"jobstatus,omitempty"` + Account string `json:"account,omitempty" doc:"the account associated with the snapshot"` + AccountID *UUID `json:"accountid,omitempty" doc:"the account ID associated with the snapshot"` + Created string `json:"created,omitempty" doc:"the date the snapshot was created"` + Domain string `json:"domain,omitempty" doc:"the domain name of the snapshot's account"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of the snapshot's account"` + ID *UUID `json:"id,omitempty" doc:"ID of the snapshot"` + IntervalType string `json:"intervaltype,omitempty" doc:"valid types are hourly, daily, weekly, monthy, template, and none."` + Name string `json:"name,omitempty" doc:"name of the snapshot"` + PhysicalSize int64 `json:"physicalsize,omitempty" doc:"physical size of the snapshot on image store"` + Revertable *bool `json:"revertable,omitempty" doc:"indicates whether the underlying storage supports reverting the volume to this snapshot"` + Size int64 `json:"size,omitempty" doc:"the size of original volume"` + SnapshotType string `json:"snapshottype,omitempty" doc:"the type of the snapshot"` + State SnapshotState `json:"state,omitempty" doc:"the state of the snapshot. BackedUp means that snapshot is ready to be used; Creating - the snapshot is being allocated on the primary storage; BackingUp - the snapshot is being backed up on secondary storage"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with snapshot"` + VolumeID *UUID `json:"volumeid,omitempty" doc:"ID of the disk volume"` + VolumeName string `json:"volumename,omitempty" doc:"name of the disk volume"` + VolumeType string `json:"volumetype,omitempty" doc:"type of the disk volume"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"id of the availability zone"` } // ResourceType returns the type of the resource -func (*Snapshot) ResourceType() string { +func (Snapshot) ResourceType() string { return "Snapshot" } -// CreateSnapshot represents a request to create a volume snapshot -// -// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/createSnapshot.html +// CreateSnapshot (Async) creates an instant snapshot of a volume type CreateSnapshot struct { - VolumeID string `json:"volumeid"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - PolicyID string `json:"policyid,omitempty"` - QuiesceVM bool `json:"quiescevm,omitempty"` + VolumeID *UUID `json:"volumeid" doc:"The ID of the disk volume"` + Account string `json:"account,omitempty" doc:"The account of the snapshot. The account parameter must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"The domain ID of the snapshot. If used with the account parameter, specifies a domain for the account associated with the disk volume."` + QuiesceVM *bool `json:"quiescevm,omitempty" doc:"quiesce vm if true"` + _ bool `name:"createSnapshot" description:"Creates an instant snapshot of a volume."` } -func (*CreateSnapshot) name() string { - return "createSnapshot" +func (CreateSnapshot) response() interface{} { + return new(AsyncJobResult) } -func (*CreateSnapshot) asyncResponse() interface{} { - return new(CreateSnapshotResponse) -} - -// CreateSnapshotResponse represents a freshly created snapshot -type CreateSnapshotResponse struct { - Snapshot Snapshot `json:"snapshot"` +func (CreateSnapshot) asyncResponse() interface{} { + return new(Snapshot) } // ListSnapshots lists the volume snapshots -// -// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/listSnapshots.html type ListSnapshots struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IntervalType string `json:"intervaltype,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Name string `json:"name,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"lists snapshot by snapshot ID"` + IntervalType string `json:"intervaltype,omitempty" doc:"valid values are HOURLY, DAILY, WEEKLY, and MONTHLY."` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"lists snapshot by snapshot name"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` - SnapshotType string `json:"snapshottype,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - VolumeID string `json:"volumeid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListSnapshots) name() string { - return "listSnapshots" -} - -func (*ListSnapshots) response() interface{} { - return new(ListSnapshotsResponse) + SnapshotType string `json:"snapshottype,omitempty" doc:"valid values are MANUAL or RECURRING."` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + VolumeID *UUID `json:"volumeid,omitempty" doc:"the ID of the disk volume"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"list snapshots by zone id"` + _ bool `name:"listSnapshots" description:"Lists all available snapshots for the account."` } // ListSnapshotsResponse represents a list of volume snapshots @@ -89,32 +96,34 @@ type ListSnapshotsResponse struct { Snapshot []Snapshot `json:"snapshot"` } -// DeleteSnapshot represents the deletion of a volume snapshot -// -// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteSnapshot.html +func (ListSnapshots) response() interface{} { + return new(ListSnapshotsResponse) +} + +// DeleteSnapshot (Async) deletes a snapshot of a disk volume type DeleteSnapshot struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the snapshot"` + _ bool `name:"deleteSnapshot" description:"Deletes a snapshot of a disk volume."` } -func (*DeleteSnapshot) name() string { - return "deleteSnapshot" +func (DeleteSnapshot) response() interface{} { + return new(AsyncJobResult) } -func (*DeleteSnapshot) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (DeleteSnapshot) asyncResponse() interface{} { + return new(booleanResponse) } -// RevertSnapshot revert a volume snapshot -// -// CloudStackAPI: http://cloudstack.apache.org/api/apidocs-4.10/apis/revertSnapshot.html +// RevertSnapshot (Async) reverts a volume snapshot type RevertSnapshot struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the snapshot"` + _ bool `name:"revertSnapshot" description:"revert a volume snapshot."` } -func (*RevertSnapshot) name() string { - return "revertSnapshot" +func (RevertSnapshot) response() interface{} { + return new(AsyncJobResult) } -func (*RevertSnapshot) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (RevertSnapshot) asyncResponse() interface{} { + return new(booleanResponse) } diff --git a/vendor/github.com/exoscale/egoscale/snapshotstate_string.go b/vendor/github.com/exoscale/egoscale/snapshotstate_string.go new file mode 100644 index 000000000..a7119dca0 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/snapshotstate_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type SnapshotState"; DO NOT EDIT. + +package egoscale + +import "strconv" + +const _SnapshotState_name = "AllocatedCreatingCreatedOnPrimaryBackingUpBackedUpCopyingDestroyingDestroyedError" + +var _SnapshotState_index = [...]uint8{0, 9, 17, 33, 42, 50, 57, 67, 76, 81} + +func (i SnapshotState) String() string { + if i < 0 || i >= SnapshotState(len(_SnapshotState_index)-1) { + return "SnapshotState(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _SnapshotState_name[_SnapshotState_index[i]:_SnapshotState_index[i+1]] +} diff --git a/vendor/github.com/exoscale/egoscale/ssh_keypairs.go b/vendor/github.com/exoscale/egoscale/ssh_keypairs.go new file mode 100644 index 000000000..298dfb21a --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/ssh_keypairs.go @@ -0,0 +1,138 @@ +package egoscale + +import ( + "context" + "fmt" +) + +// SSHKeyPair represents an SSH key pair +// +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html#creating-the-ssh-keypair +type SSHKeyPair struct { + Fingerprint string `json:"fingerprint,omitempty" doc:"Fingerprint of the public key"` + Name string `json:"name,omitempty" doc:"Name of the keypair"` + PrivateKey string `json:"privatekey,omitempty" doc:"Private key"` +} + +// Delete removes the given SSH key, by Name +func (ssh SSHKeyPair) Delete(ctx context.Context, client *Client) error { + if ssh.Name == "" { + return fmt.Errorf("an SSH Key Pair may only be deleted using Name") + } + + return client.BooleanRequestWithContext(ctx, &DeleteSSHKeyPair{ + Name: ssh.Name, + }) +} + +// ListRequest builds the ListSSHKeyPairs request +func (ssh SSHKeyPair) ListRequest() (ListCommand, error) { + req := &ListSSHKeyPairs{ + Fingerprint: ssh.Fingerprint, + Name: ssh.Name, + } + + return req, nil +} + +// CreateSSHKeyPair represents a new keypair to be created +type CreateSSHKeyPair struct { + Name string `json:"name" doc:"Name of the keypair"` + Account string `json:"account,omitempty" doc:"an optional account for the ssh key. Must be used with domainId."` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainId for the ssh key. If the account parameter is used, domainId must also be used."` + _ bool `name:"createSSHKeyPair" description:"Create a new keypair and returns the private key"` +} + +func (CreateSSHKeyPair) response() interface{} { + return new(SSHKeyPair) +} + +// DeleteSSHKeyPair represents a new keypair to be created +type DeleteSSHKeyPair struct { + Name string `json:"name" doc:"Name of the keypair"` + Account string `json:"account,omitempty" doc:"the account associated with the keypair. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID associated with the keypair"` + _ bool `name:"deleteSSHKeyPair" description:"Deletes a keypair by name"` +} + +func (DeleteSSHKeyPair) response() interface{} { + return new(booleanResponse) +} + +// RegisterSSHKeyPair represents a new registration of a public key in a keypair +type RegisterSSHKeyPair struct { + Name string `json:"name" doc:"Name of the keypair"` + PublicKey string `json:"publickey" doc:"Public key material of the keypair"` + Account string `json:"account,omitempty" doc:"an optional account for the ssh key. Must be used with domainId."` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainId for the ssh key. If the account parameter is used, domainId must also be used."` + _ bool `name:"registerSSHKeyPair" description:"Register a public key in a keypair under a certain name"` +} + +func (RegisterSSHKeyPair) response() interface{} { + return new(SSHKeyPair) +} + +// ListSSHKeyPairs represents a query for a list of SSH KeyPairs +type ListSSHKeyPairs struct { + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + Fingerprint string `json:"fingerprint,omitempty" doc:"A public key fingerprint to look for"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"A key pair name to look for"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + _ bool `name:"listSSHKeyPairs" description:"List registered keypairs"` +} + +// ListSSHKeyPairsResponse represents a list of SSH key pairs +type ListSSHKeyPairsResponse struct { + Count int `json:"count"` + SSHKeyPair []SSHKeyPair `json:"sshkeypair"` +} + +func (ListSSHKeyPairs) response() interface{} { + return new(ListSSHKeyPairsResponse) +} + +// SetPage sets the current page +func (ls *ListSSHKeyPairs) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListSSHKeyPairs) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListSSHKeyPairs) each(resp interface{}, callback IterateItemFunc) { + sshs, ok := resp.(*ListSSHKeyPairsResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListSSHKeyPairsResponse expected, got %T", resp)) + return + } + + for i := range sshs.SSHKeyPair { + if !callback(&sshs.SSHKeyPair[i], nil) { + break + } + } +} + +// ResetSSHKeyForVirtualMachine (Async) represents a change for the key pairs +type ResetSSHKeyForVirtualMachine struct { + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + KeyPair string `json:"keypair" doc:"name of the ssh key pair used to login to the virtual machine"` + Account string `json:"account,omitempty" doc:"an optional account for the ssh key. Must be used with domainId."` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used."` + _ bool `name:"resetSSHKeyForVirtualMachine" description:"Resets the SSH Key for virtual machine. The virtual machine must be in a \"Stopped\" state."` +} + +func (ResetSSHKeyForVirtualMachine) response() interface{} { + return new(AsyncJobResult) +} + +func (ResetSSHKeyForVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) +} diff --git a/vendor/github.com/exoscale/egoscale/tags.go b/vendor/github.com/exoscale/egoscale/tags.go index d0ed616e0..62a5025df 100644 --- a/vendor/github.com/exoscale/egoscale/tags.go +++ b/vendor/github.com/exoscale/egoscale/tags.go @@ -1,89 +1,67 @@ package egoscale -// Taggable represents a resource which can have tags attached -// -// This is a helper to fill the resourcetype of a CreateTags call -type Taggable interface { - // CloudStack resource type of the Taggable type - ResourceType() string -} - // ResourceTag is a tag associated with a resource // // http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/4.9/management.html type ResourceTag struct { - Account string `json:"account,omitempty"` - Customer string `json:"customer,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Key string `json:"key"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - ResourceID string `json:"resourceid,omitempty"` - ResourceType string `json:"resourcetype,omitempty"` - Value string `json:"value"` + Account string `json:"account,omitempty" doc:"the account associated with the tag"` + Customer string `json:"customer,omitempty" doc:"customer associated with the tag"` + Domain string `json:"domain,omitempty" doc:"the domain associated with the tag"` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain associated with the tag"` + Key string `json:"key,omitempty" doc:"tag key name"` + ResourceID *UUID `json:"resourceid,omitempty" doc:"id of the resource"` + ResourceType string `json:"resourcetype,omitempty" doc:"resource type"` + Value string `json:"value,omitempty" doc:"tag value"` } // CreateTags (Async) creates resource tag(s) -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createTags.html type CreateTags struct { - ResourceIDs []string `json:"resourceids"` - ResourceType string `json:"resourcetype"` - Tags []ResourceTag `json:"tags"` - Customer string `json:"customer,omitempty"` + ResourceIDs []UUID `json:"resourceids" doc:"list of resources to create the tags for"` + ResourceType string `json:"resourcetype" doc:"type of the resource"` + Tags []ResourceTag `json:"tags" doc:"Map of tags (key/value pairs)"` + Customer string `json:"customer,omitempty" doc:"identifies client specific tag. When the value is not null, the tag can't be used by cloudStack code internally"` + _ bool `name:"createTags" description:"Creates resource tag(s)"` } -func (*CreateTags) name() string { - return "createTags" +func (CreateTags) response() interface{} { + return new(AsyncJobResult) } -func (*CreateTags) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (CreateTags) asyncResponse() interface{} { + return new(booleanResponse) } // DeleteTags (Async) deletes the resource tag(s) -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteTags.html type DeleteTags struct { - ResourceIDs []string `json:"resourceids"` - ResourceType string `json:"resourcetype"` - Tags []ResourceTag `json:"tags,omitempty"` + ResourceIDs []UUID `json:"resourceids" doc:"Delete tags for resource id(s)"` + ResourceType string `json:"resourcetype" doc:"Delete tag by resource type"` + Tags []ResourceTag `json:"tags,omitempty" doc:"Delete tags matching key/value pairs"` + _ bool `name:"deleteTags" description:"Deleting resource tag(s)"` } -func (*DeleteTags) name() string { - return "deleteTags" +func (DeleteTags) response() interface{} { + return new(AsyncJobResult) } -func (*DeleteTags) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (DeleteTags) asyncResponse() interface{} { + return new(booleanResponse) } // ListTags list resource tag(s) -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listTags.html type ListTags struct { - Account string `json:"account,omitempty"` - Customer string `json:"customer,omitempty"` - DomainID string `json:"domainid,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Key string `json:"key,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + Customer string `json:"customer,omitempty" doc:"list by customer name"` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Key string `json:"key,omitempty" doc:"list by key"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` - ResourceID string `json:"resourceid,omitempty"` - ResourceType string `json:"resourcetype,omitempty"` - Value string `json:"value,omitempty"` -} - -func (*ListTags) name() string { - return "listTags" -} - -func (*ListTags) response() interface{} { - return new(ListTagsResponse) + ResourceID *UUID `json:"resourceid,omitempty" doc:"list by resource id"` + ResourceType string `json:"resourcetype,omitempty" doc:"list by resource type"` + Value string `json:"value,omitempty" doc:"list by value"` + _ bool `name:"listTags" description:"List resource tag(s)"` } // ListTagsResponse represents a list of resource tags @@ -91,3 +69,7 @@ type ListTagsResponse struct { Count int `json:"count"` Tag []ResourceTag `json:"tag"` } + +func (ListTags) response() interface{} { + return new(ListTagsResponse) +} diff --git a/vendor/github.com/exoscale/egoscale/templates.go b/vendor/github.com/exoscale/egoscale/templates.go index 4c129bcce..029e7f5f7 100644 --- a/vendor/github.com/exoscale/egoscale/templates.go +++ b/vendor/github.com/exoscale/egoscale/templates.go @@ -1,72 +1,94 @@ package egoscale +import ( + "fmt" +) + // Template represents a machine to be deployed +// +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/templates.html type Template struct { - Account string `json:"account,omitempty"` - AccountID string `json:"accountid,omitempty"` - Bootable bool `json:"bootable,omitempty"` - Checksum string `json:"checksum,omitempty"` - Created string `json:"created,omitempty"` - CrossZones bool `json:"crossZones,omitempty"` - Details map[string]string `json:"details,omitempty"` - DisplayText string `json:"displaytext,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Format string `json:"format,omitempty"` - HostID string `json:"hostid,omitempty"` - HostName string `json:"hostname,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - ID string `json:"id,omitempty"` - IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty"` - IsExtractable bool `json:"isextractable,omitempty"` - IsFeatured bool `json:"isfeatured,omitempty"` - IsPublic bool `json:"ispublic,omitempty"` - IsReady bool `json:"isready,omitempty"` - Name string `json:"name,omitempty"` - OsTypeID string `json:"ostypeid,omitempty"` - OsTypeName string `json:"ostypename,omitempty"` - PasswordEnabled bool `json:"passwordenabled,omitempty"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - Removed string `json:"removed,omitempty"` - Size int64 `json:"size,omitempty"` - SourceTemplateID string `json:"sourcetemplateid,omitempty"` - SSHKeyEnabled bool `json:"sshkeyenabled,omitempty"` - Status string `json:"status,omitempty"` - Zoneid string `json:"zoneid,omitempty"` - Zonename string `json:"zonename,omitempty"` + Account string `json:"account,omitempty" doc:"the account name to which the template belongs"` + AccountID *UUID `json:"accountid,omitempty" doc:"the account id to which the template belongs"` + Bootable bool `json:"bootable,omitempty" doc:"true if the ISO is bootable, false otherwise"` + Checksum string `json:"checksum,omitempty" doc:"checksum of the template"` + Created string `json:"created,omitempty" doc:"the date this template was created"` + CrossZones bool `json:"crossZones,omitempty" doc:"true if the template is managed across all Zones, false otherwise"` + Details map[string]string `json:"details,omitempty" doc:"additional key/value details tied with template"` + DisplayText string `json:"displaytext,omitempty" doc:"the template display text"` + Domain string `json:"domain,omitempty" doc:"the name of the domain to which the template belongs"` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain to which the template belongs"` + Format string `json:"format,omitempty" doc:"the format of the template."` + HostID *UUID `json:"hostid,omitempty" doc:"the ID of the secondary storage host for the template"` + HostName string `json:"hostname,omitempty" doc:"the name of the secondary storage host for the template"` + Hypervisor string `json:"hypervisor,omitempty" doc:"the hypervisor on which the template runs"` + ID *UUID `json:"id,omitempty" doc:"the template ID"` + IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty" doc:"true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory"` + IsExtractable bool `json:"isextractable,omitempty" doc:"true if the template is extractable, false otherwise"` + IsFeatured bool `json:"isfeatured,omitempty" doc:"true if this template is a featured template, false otherwise"` + IsPublic bool `json:"ispublic,omitempty" doc:"true if this template is a public template, false otherwise"` + IsReady bool `json:"isready,omitempty" doc:"true if the template is ready to be deployed from, false otherwise."` + Name string `json:"name,omitempty" doc:"the template name"` + OsTypeID *UUID `json:"ostypeid,omitempty" doc:"the ID of the OS type for this template."` + OsTypeName string `json:"ostypename,omitempty" doc:"the name of the OS type for this template."` + PasswordEnabled bool `json:"passwordenabled,omitempty" doc:"true if the reset password feature is enabled, false otherwise"` + Removed string `json:"removed,omitempty" doc:"the date this template was removed"` + Size int64 `json:"size,omitempty" doc:"the size of the template"` + SourceTemplateID *UUID `json:"sourcetemplateid,omitempty" doc:"the template ID of the parent template if present"` + SSHKeyEnabled bool `json:"sshkeyenabled,omitempty" doc:"true if template is sshkey enabled, false otherwise"` + Status string `json:"status,omitempty" doc:"the status of the template"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with tempate"` + TemplateDirectory string `json:"templatedirectory,omitempty" doc:"Template directory"` + TemplateTag string `json:"templatetag,omitempty" doc:"the tag of this template"` + TemplateType string `json:"templatetype,omitempty" doc:"the type of the template"` + URL string `json:"url,omitempty" doc:"Original URL of the template where it was downloaded"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the zone for this template"` + ZoneName string `json:"zonename,omitempty" doc:"the name of the zone for this template"` } // ResourceType returns the type of the resource -func (*Template) ResourceType() string { +func (Template) ResourceType() string { return "Template" } +// ListRequest builds the ListTemplates request +func (temp Template) ListRequest() (ListCommand, error) { + req := &ListTemplates{ + Name: temp.Name, + Account: temp.Account, + DomainID: temp.DomainID, + ID: temp.ID, + ZoneID: temp.ZoneID, + Hypervisor: temp.Hypervisor, + //TODO Tags + } + if temp.IsFeatured { + req.TemplateFilter = "featured" + } + if temp.Removed != "" { + *req.ShowRemoved = true + } + + return req, nil +} + // ListTemplates represents a template query filter type ListTemplates struct { - TemplateFilter string `json:"templatefilter"` // featured, etc. - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Name string `json:"name,omitempty"` + TemplateFilter string `json:"templatefilter" doc:"possible values are \"featured\", \"self\", \"selfexecutable\",\"sharedexecutable\",\"executable\", and \"community\". * featured : templates that have been marked as featured and public. * self : templates that have been registered or created by the calling user. * selfexecutable : same as self, but only returns templates that can be used to deploy a new VM. * sharedexecutable : templates ready to be deployed that have been granted to the calling user by another user. * executable : templates that are owned by the calling user, or public templates, that can be used to deploy a VM. * community : templates that have been marked as public but not featured. * all : all templates (only usable by admins)."` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + Hypervisor string `json:"hypervisor,omitempty" doc:"the hypervisor for which to restrict the search"` + ID *UUID `json:"id,omitempty" doc:"the template ID"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"the template name"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - ProjectID string `json:"projectid,omitempty"` - ShowRemoved bool `json:"showremoved,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListTemplates) name() string { - return "listTemplates" -} - -func (*ListTemplates) response() interface{} { - return new(ListTemplatesResponse) + ShowRemoved *bool `json:"showremoved,omitempty" doc:"show removed templates as well"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"list templates by zoneId"` + _ bool `name:"listTemplates" description:"List all public, private, and privileged templates."` } // ListTemplatesResponse represents a list of templates @@ -74,3 +96,186 @@ type ListTemplatesResponse struct { Count int `json:"count"` Template []Template `json:"template"` } + +func (ListTemplates) response() interface{} { + return new(ListTemplatesResponse) +} + +func (ListTemplates) each(resp interface{}, callback IterateItemFunc) { + temps, ok := resp.(*ListTemplatesResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListTemplatesResponse expected, got %T", resp)) + return + } + + for i := range temps.Template { + if !callback(&temps.Template[i], nil) { + break + } + } +} + +// SetPage sets the current page +func (ls *ListTemplates) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListTemplates) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +// CreateTemplate (Async) represents a template creation +type CreateTemplate struct { + Bits int `json:"bits,omitempty" doc:"32 or 64 bit"` + Details map[string]string `json:"details,omitempty" doc:"Template details in key/value pairs."` + DisplayText string `json:"displaytext" doc:"the display text of the template. This is usually used for display purposes."` + IsDynamicallyScalable *bool `json:"isdynamicallyscalable,omitempty" doc:"true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory"` + IsFeatured *bool `json:"isfeatured,omitempty" doc:"true if this template is a featured template, false otherwise"` + IsPublic *bool `json:"ispublic,omitempty" doc:"true if this template is a public template, false otherwise"` + Name string `json:"name" doc:"the name of the template"` + OsTypeID *UUID `json:"ostypeid" doc:"the ID of the OS Type that best represents the OS of this template."` + PasswordEnabled *bool `json:"passwordenabled,omitempty" doc:"true if the template supports the password reset feature; default is false"` + RequiresHVM *bool `json:"requireshvm,omitempty" doc:"true if the template requres HVM, false otherwise"` + SnapshotID *UUID `json:"snapshotid,omitempty" doc:"the ID of the snapshot the template is being created from. Either this parameter, or volumeId has to be passed in"` + TemplateTag string `json:"templatetag,omitempty" doc:"the tag for this template."` + URL string `json:"url,omitempty" doc:"Optional, only for baremetal hypervisor. The directory name where template stored on CIFS server"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"Optional, VM ID. If this presents, it is going to create a baremetal template for VM this ID refers to. This is only for VM whose hypervisor type is BareMetal"` + VolumeID *UUID `json:"volumeid,omitempty" doc:"the ID of the disk volume the template is being created from. Either this parameter, or snapshotId has to be passed in"` + _ bool `name:"createTemplate" description:"Creates a template of a virtual machine. The virtual machine must be in a STOPPED state. A template created from this command is automatically designated as a private template visible to the account that created it."` +} + +func (CreateTemplate) response() interface{} { + return new(AsyncJobResult) +} + +func (CreateTemplate) asyncResponse() interface{} { + return new(Template) +} + +// CopyTemplate (Async) represents a template copy +type CopyTemplate struct { + DestZoneID *UUID `json:"destzoneid" doc:"ID of the zone the template is being copied to."` + ID *UUID `json:"id" doc:"Template ID."` + SourceZoneID *UUID `json:"sourcezoneid,omitempty" doc:"ID of the zone the template is currently hosted on. If not specified and template is cross-zone, then we will sync this template to region wide image store."` + _ bool `name:"copyTemplate" description:"Copies a template from one zone to another."` +} + +func (CopyTemplate) response() interface{} { + return new(AsyncJobResult) +} + +func (CopyTemplate) asyncResponse() interface{} { + return new(Template) +} + +// UpdateTemplate represents a template change +type UpdateTemplate struct { + Bootable *bool `json:"bootable,omitempty" doc:"true if image is bootable, false otherwise"` + Details map[string]string `json:"details,omitempty" doc:"Details in key/value pairs."` + DisplayText string `json:"displaytext,omitempty" doc:"the display text of the image"` + Format string `json:"format,omitempty" doc:"the format for the image"` + ID *UUID `json:"id" doc:"the ID of the image file"` + IsDynamicallyScalable *bool `json:"isdynamicallyscalable,omitempty" doc:"true if template/ISO contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory"` + IsRouting *bool `json:"isrouting,omitempty" doc:"true if the template type is routing i.e., if template is used to deploy router"` + Name string `json:"name,omitempty" doc:"the name of the image file"` + OsTypeID *UUID `json:"ostypeid,omitempty" doc:"the ID of the OS type that best represents the OS of this image."` + PasswordEnabled *bool `json:"passwordenabled,omitempty" doc:"true if the image supports the password reset feature; default is false"` + SortKey int `json:"sortkey,omitempty" doc:"sort key of the template, integer"` + _ bool `name:"updateTemplate" description:"Updates attributes of a template."` +} + +func (UpdateTemplate) response() interface{} { + return new(AsyncJobResult) +} + +func (UpdateTemplate) asyncResponse() interface{} { + return new(Template) +} + +// DeleteTemplate (Async) represents the deletion of a template +type DeleteTemplate struct { + ID *UUID `json:"id" doc:"the ID of the template"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of zone of the template"` + _ bool `name:"deleteTemplate" description:"Deletes a template from the system. All virtual machines using the deleted template will not be affected."` +} + +func (DeleteTemplate) response() interface{} { + return new(AsyncJobResult) +} + +func (DeleteTemplate) asyncResponse() interface{} { + return new(booleanResponse) +} + +// PrepareTemplate represents a template preparation +type PrepareTemplate struct { + TemplateID *UUID `json:"templateid" doc:"template ID of the template to be prepared in primary storage(s)."` + ZoneID *UUID `json:"zoneid" doc:"zone ID of the template to be prepared in primary storage(s)."` + _ bool `name:"prepareTemplate" description:"load template into primary storage"` +} + +func (PrepareTemplate) response() interface{} { + return new(AsyncJobResult) +} + +func (PrepareTemplate) asyncResponse() interface{} { + return new(Template) +} + +// RegisterTemplate represents a template registration +type RegisterTemplate struct { + Account string `json:"account,omitempty" doc:"an optional accountName. Must be used with domainId."` + Bits int `json:"bits,omitempty" doc:"32 or 64 bits support. 64 by default"` + Checksum string `json:"checksum,omitempty" doc:"the MD5 checksum value of this template"` + Details map[string]string `json:"details,omitempty" doc:"Template details in key/value pairs."` + DisplayText string `json:"displaytext" doc:"the display text of the template. This is usually used for display purposes."` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainId. If the account parameter is used, domainId must also be used."` + Format string `json:"format" doc:"the format for the template. Possible values include QCOW2, RAW, and VHD."` + Hypervisor string `json:"hypervisor" doc:"the target hypervisor for the template"` + IsDynamicallyScalable *bool `json:"isdynamicallyscalable,omitempty" doc:"true if template contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory"` + IsExtractable *bool `json:"isextractable,omitempty" doc:"true if the template or its derivatives are extractable; default is false"` + IsFeatured *bool `json:"isfeatured,omitempty" doc:"true if this template is a featured template, false otherwise"` + IsPublic *bool `json:"ispublic,omitempty" doc:"true if the template is available to all accounts; default is true"` + IsRouting *bool `json:"isrouting,omitempty" doc:"true if the template type is routing i.e., if template is used to deploy router"` + IsSystem *bool `json:"issystem,omitempty" doc:"true if the template type is system i.e., if template is used to deploy system VM"` + Name string `json:"name" doc:"the name of the template"` + OsTypeID *UUID `json:"ostypeid" doc:"the ID of the OS Type that best represents the OS of this template."` + PasswordEnabled *bool `json:"passwordenabled,omitempty" doc:"true if the template supports the password reset feature; default is false"` + RequiresHVM *bool `json:"requireshvm,omitempty" doc:"true if this template requires HVM"` + SSHKeyEnabled *bool `json:"sshkeyenabled,omitempty" doc:"true if the template supports the sshkey upload feature; default is false"` + TemplateTag string `json:"templatetag,omitempty" doc:"the tag for this template."` + URL string `json:"url" doc:"the URL of where the template is hosted. Possible URL include http:// and https://"` + ZoneID *UUID `json:"zoneid" doc:"the ID of the zone the template is to be hosted on"` + _ bool `name:"registerTemplate" description:"Registers an existing template into the CloudStack cloud."` +} + +func (RegisterTemplate) response() interface{} { + return new(Template) +} + +// OSCategory represents an OS category +type OSCategory struct { + ID string `json:"id,omitempty" doc:"the ID of the OS category"` + Name string `json:"name,omitempty" doc:"the name of the OS category"` +} + +// ListOSCategories lists the OS categories +type ListOSCategories struct { + ID string `json:"id,omitempty" doc:"list Os category by id"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Name string `json:"name,omitempty" doc:"list os category by name"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + _ bool `name:"listOsCategories" description:"Lists all supported OS categories for this cloud."` +} + +// ListOSCategoriesResponse represents a list of OS categories +type ListOSCategoriesResponse struct { + Count int `json:"count"` + OSCategory []OSCategory `json:"oscategory"` +} + +func (ListOSCategories) response() interface{} { + return new(ListOSCategoriesResponse) +} diff --git a/vendor/github.com/exoscale/egoscale/topology.go b/vendor/github.com/exoscale/egoscale/topology.go deleted file mode 100644 index 9ee270681..000000000 --- a/vendor/github.com/exoscale/egoscale/topology.go +++ /dev/null @@ -1,208 +0,0 @@ -package egoscale - -import ( - "fmt" - "regexp" - "strings" -) - -// GetSecurityGroups returns all security groups -// -// Deprecated: do it yourself -func (exo *Client) GetSecurityGroups() (map[string]SecurityGroup, error) { - var sgs map[string]SecurityGroup - resp, err := exo.Request(&ListSecurityGroups{}) - if err != nil { - return nil, err - } - - sgs = make(map[string]SecurityGroup) - for _, sg := range resp.(*ListSecurityGroupsResponse).SecurityGroup { - sgs[sg.Name] = sg - } - return sgs, nil -} - -// GetSecurityGroupID returns security group by name -// -// Deprecated: do it yourself -func (exo *Client) GetSecurityGroupID(name string) (string, error) { - resp, err := exo.Request(&ListSecurityGroups{SecurityGroupName: name}) - if err != nil { - return "", err - } - - for _, sg := range resp.(*ListSecurityGroupsResponse).SecurityGroup { - if sg.Name == name { - return sg.ID, nil - } - } - - return "", nil -} - -// GetAllZones returns all the zone id by name -// -// Deprecated: do it yourself -func (exo *Client) GetAllZones() (map[string]string, error) { - var zones map[string]string - resp, err := exo.Request(&ListZones{}) - if err != nil { - return zones, err - } - - zones = make(map[string]string) - for _, zone := range resp.(*ListZonesResponse).Zone { - zones[strings.ToLower(zone.Name)] = zone.ID - } - return zones, nil -} - -// GetProfiles returns a mapping of the service offerings by name -// -// Deprecated: do it yourself -func (exo *Client) GetProfiles() (map[string]string, error) { - profiles := make(map[string]string) - resp, err := exo.Request(&ListServiceOfferings{}) - if err != nil { - return profiles, nil - } - - for _, offering := range resp.(*ListServiceOfferingsResponse).ServiceOffering { - profiles[strings.ToLower(offering.Name)] = offering.ID - } - - return profiles, nil -} - -// GetKeypairs returns the list of SSH keyPairs -// -// Deprecated: do it yourself -func (exo *Client) GetKeypairs() ([]SSHKeyPair, error) { - var keypairs []SSHKeyPair - - resp, err := exo.Request(&ListSSHKeyPairs{}) - if err != nil { - return keypairs, err - } - - r := resp.(*ListSSHKeyPairsResponse) - keypairs = make([]SSHKeyPair, r.Count) - for i, keypair := range r.SSHKeyPair { - keypairs[i] = keypair - } - return keypairs, nil -} - -// GetAffinityGroups returns a mapping of the (anti-)affinity groups -// -// Deprecated: do it yourself -func (exo *Client) GetAffinityGroups() (map[string]string, error) { - var affinitygroups map[string]string - - resp, err := exo.Request(&ListAffinityGroups{}) - if err != nil { - return affinitygroups, err - } - - affinitygroups = make(map[string]string) - for _, affinitygroup := range resp.(*ListAffinityGroupsResponse).AffinityGroup { - affinitygroups[affinitygroup.Name] = affinitygroup.ID - } - return affinitygroups, nil -} - -// GetImages list the available featured images and group them by name, then size. -// -// Deprecated: do it yourself -func (exo *Client) GetImages() (map[string]map[int64]string, error) { - var images map[string]map[int64]string - images = make(map[string]map[int64]string) - re := regexp.MustCompile(`^Linux (?P.+?) (?P[0-9.]+)\b`) - - resp, err := exo.Request(&ListTemplates{ - TemplateFilter: "featured", - ZoneID: "1", // XXX: Hack to list only CH-GVA - }) - if err != nil { - return images, err - } - - for _, template := range resp.(*ListTemplatesResponse).Template { - size := int64(template.Size >> 30) // B to GiB - - fullname := strings.ToLower(template.Name) - - if _, present := images[fullname]; !present { - images[fullname] = make(map[int64]string) - } - images[fullname][size] = template.ID - - submatch := re.FindStringSubmatch(template.Name) - if len(submatch) > 0 { - name := strings.Replace(strings.ToLower(submatch[1]), " ", "-", -1) - version := submatch[2] - image := fmt.Sprintf("%s-%s", name, version) - - if _, present := images[image]; !present { - images[image] = make(map[int64]string) - } - images[image][size] = template.ID - } - } - return images, nil -} - -// GetTopology returns an big, yet incomplete view of the world -// -// Deprecated: will go away in the future -func (exo *Client) GetTopology() (*Topology, error) { - zones, err := exo.GetAllZones() - if err != nil { - return nil, err - } - images, err := exo.GetImages() - if err != nil { - return nil, err - } - securityGroups, err := exo.GetSecurityGroups() - if err != nil { - return nil, err - } - groups := make(map[string]string) - for k, v := range securityGroups { - groups[k] = v.ID - } - - keypairs, err := exo.GetKeypairs() - if err != nil { - return nil, err - } - - /* Convert the ssh keypair to contain just the name */ - keynames := make([]string, len(keypairs)) - for i, k := range keypairs { - keynames[i] = k.Name - } - - affinitygroups, err := exo.GetAffinityGroups() - if err != nil { - return nil, err - } - - profiles, err := exo.GetProfiles() - if err != nil { - return nil, err - } - - topo := &Topology{ - Zones: zones, - Images: images, - Keypairs: keynames, - Profiles: profiles, - AffinityGroups: affinitygroups, - SecurityGroups: groups, - } - - return topo, nil -} diff --git a/vendor/github.com/exoscale/egoscale/types.go b/vendor/github.com/exoscale/egoscale/types.go deleted file mode 100644 index 484dc31f5..000000000 --- a/vendor/github.com/exoscale/egoscale/types.go +++ /dev/null @@ -1,23 +0,0 @@ -package egoscale - -import ( - "net/http" -) - -// Client represents the CloudStack API client -type Client struct { - client *http.Client - endpoint string - apiKey string - apiSecret string -} - -// Topology represents a view of the servers -type Topology struct { - Zones map[string]string - Images map[string]map[int64]string - Profiles map[string]string - Keypairs []string - SecurityGroups map[string]string - AffinityGroups map[string]string -} diff --git a/vendor/github.com/exoscale/egoscale/users.go b/vendor/github.com/exoscale/egoscale/users.go index 353e4b6ba..391f2a1f8 100644 --- a/vendor/github.com/exoscale/egoscale/users.go +++ b/vendor/github.com/exoscale/egoscale/users.go @@ -2,42 +2,108 @@ package egoscale // User represents a User type User struct { - Account string `json:"account,omitempty"` - AccountID string `json:"accountid,omitempty"` - AccountType string `json:"accounttype,omitempty"` - APIKey string `json:"apikey,omitempty"` - Created string `json:"created,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Email string `json:"email,omitempty"` - FirstName string `json:"firstname,omitempty"` - ID string `json:"id,omitempty"` - IsCallerChildDomain bool `json:"iscallerchilddomain,omitempty"` - IsDefault bool `json:"isdefault,omitempty"` - LastName string `json:"lastname,omitempty"` - SecretKey string `json:"secretkey,omitempty"` - State string `json:"state,omitempty"` - UserName string `json:"username,omitempty"` + APIKey string `json:"apikey,omitempty" doc:"the api key of the user"` + Account string `json:"account,omitempty" doc:"the account name of the user"` + AccountID *UUID `json:"accountid,omitempty" doc:"the account ID of the user"` + AccountType int16 `json:"accounttype,omitempty" doc:"the account type of the user"` + Created string `json:"created,omitempty" doc:"the date and time the user account was created"` + Domain string `json:"domain,omitempty" doc:"the domain name of the user"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of the user"` + Email string `json:"email,omitempty" doc:"the user email address"` + FirstName string `json:"firstname,omitempty" doc:"the user firstname"` + ID *UUID `json:"id,omitempty" doc:"the user ID"` + IsCallerChildDomain bool `json:"iscallerchilddomain,omitempty" doc:"the boolean value representing if the updating target is in caller's child domain"` + IsDefault bool `json:"isdefault,omitempty" doc:"true if user is default, false otherwise"` + LastName string `json:"lastname,omitempty" doc:"the user lastname"` + RoleID *UUID `json:"roleid,omitempty" doc:"the ID of the role"` + RoleName string `json:"rolename,omitempty" doc:"the name of the role"` + RoleType string `json:"roletype,omitempty" doc:"the type of the role"` + SecretKey string `json:"secretkey,omitempty" doc:"the secret key of the user"` + State string `json:"state,omitempty" doc:"the user state"` + Timezone string `json:"timezone,omitempty" doc:"the timezone user was created in"` + UserName string `json:"username,omitempty" doc:"the user name"` } // RegisterUserKeys registers a new set of key of the given user // -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/registerUserKeys.html -type RegisterUserKeys struct { - ID string `json:"id"` -} - -func (*RegisterUserKeys) name() string { - return "registerUserKeys" -} - -func (*RegisterUserKeys) response() interface{} { - return new(RegisterUserKeysResponse) -} - -// RegisterUserKeysResponse represents a new set of UserKeys -// // NB: only the APIKey and SecretKey will be filled -type RegisterUserKeysResponse struct { - UserKeys User `json:"userkeys"` +type RegisterUserKeys struct { + ID *UUID `json:"id" doc:"User id"` + _ bool `name:"registerUserKeys" description:"This command allows a user to register for the developer API, returning a secret key and an API key. This request is made through the integration API port, so it is a privileged command and must be made on behalf of a user. It is up to the implementer just how the username and password are entered, and then how that translates to an integration API request. Both secret key and API key should be returned to the user"` +} + +func (RegisterUserKeys) response() interface{} { + return new(User) +} + +// CreateUser represents the creation of a User +type CreateUser struct { + Account string `json:"account" doc:"Creates the user under the specified account. If no account is specified, the username will be used as the account name."` + Email string `json:"email" doc:"email"` + FirstName string `json:"firstname" doc:"firstname"` + LastName string `json:"lastname" doc:"lastname"` + Password string `json:"password" doc:"Clear text password (Default hashed to SHA256SALT). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section."` + UserName string `json:"username" doc:"Unique username."` + DomainID *UUID `json:"domainid,omitempty" doc:"Creates the user under the specified domain. Has to be accompanied with the account parameter"` + Timezone string `json:"timezone,omitempty" doc:"Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format."` + UserID *UUID `json:"userid,omitempty" doc:"User UUID, required for adding account from external provisioning system"` + _ bool `name:"createUser" description:"Creates a user for an account that already exists"` +} + +func (CreateUser) response() interface{} { + return new(User) +} + +// UpdateUser represents the modification of a User +type UpdateUser struct { + ID *UUID `json:"id" doc:"User uuid"` + Email string `json:"email,omitempty" doc:"email"` + FirstName string `json:"firstname,omitempty" doc:"first name"` + LastName string `json:"lastname,omitempty" doc:"last name"` + Password string `json:"password,omitempty" doc:"Clear text password (default hashed to SHA256SALT). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter"` + Timezone string `json:"timezone,omitempty" doc:"Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format."` + UserAPIKey string `json:"userapikey,omitempty" doc:"The API key for the user. Must be specified with userSecretKey"` + UserName string `json:"username,omitempty" doc:"Unique username"` + UserSecretKey string `json:"usersecretkey,omitempty" doc:"The secret key for the user. Must be specified with userApiKey"` + _ bool `name:"updateUser" description:"Updates a user account"` +} + +func (UpdateUser) response() interface{} { + return new(User) +} + +// ListUsers represents the search for Users +type ListUsers struct { + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + AccountType int64 `json:"accounttype,omitempty" doc:"List users by account type. Valid types include admin, domain-admin, read-only-admin, or user."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"List user by ID."` + IsRecursive bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + State string `json:"state,omitempty" doc:"List users by state of the user account."` + Username string `json:"username,omitempty" doc:"List user by the username"` + _ bool `name:"listUsers" description:"Lists user accounts"` +} + +// ListUsersResponse represents a list of users +type ListUsersResponse struct { + Count int `json:"count"` + User []User `json:"user"` +} + +func (ListUsers) response() interface{} { + return new(ListUsersResponse) +} + +// DeleteUser deletes a user for an account +type DeleteUser struct { + ID *UUID `json:"id" doc:"id of the user to be deleted"` + _ bool `name:"deleteUser" description:"Deletes a user for an account"` +} + +func (DeleteUser) response() interface{} { + return new(booleanResponse) } diff --git a/vendor/github.com/exoscale/egoscale/uuid.go b/vendor/github.com/exoscale/egoscale/uuid.go new file mode 100644 index 000000000..a91ceae21 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/uuid.go @@ -0,0 +1,55 @@ +package egoscale + +import ( + "encoding/json" + "fmt" + + "github.com/satori/go.uuid" +) + +// UUID holds a UUID v4 +type UUID struct { + uuid.UUID +} + +// Equal returns true if itself is equal to other +func (u UUID) Equal(other UUID) bool { + return uuid.Equal(u.UUID, other.UUID) +} + +// UnmarshalJSON unmarshals the raw JSON into the MAC address +func (u *UUID) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + new, err := ParseUUID(s) + if err == nil { + u.UUID = new.UUID + } + return err +} + +// MarshalJSON converts the UUID to a string representation +func (u UUID) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", u.String())), nil +} + +// ParseUUID parses a string into a UUID +func ParseUUID(s string) (*UUID, error) { + u, err := uuid.FromString(s) + if err != nil { + return nil, err + } + return &UUID{u}, nil +} + +// MustParseUUID acts like ParseUUID but panic in case of a failure +func MustParseUUID(s string) *UUID { + u, e := ParseUUID(s) + if e != nil { + panic(e) + } + return u +} diff --git a/vendor/github.com/exoscale/egoscale/version.go b/vendor/github.com/exoscale/egoscale/version.go new file mode 100644 index 000000000..96fbe2865 --- /dev/null +++ b/vendor/github.com/exoscale/egoscale/version.go @@ -0,0 +1,4 @@ +package egoscale + +// Version of the library +const Version = "0.11.3" diff --git a/vendor/github.com/exoscale/egoscale/virtual_machines.go b/vendor/github.com/exoscale/egoscale/virtual_machines.go index 168aa07c0..418b12ad3 100644 --- a/vendor/github.com/exoscale/egoscale/virtual_machines.go +++ b/vendor/github.com/exoscale/egoscale/virtual_machines.go @@ -1,93 +1,176 @@ package egoscale -import "net" +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "fmt" + "io/ioutil" + "net" + "net/url" +) -// VirtualMachine reprents a virtual machine +// VirtualMachineState holds the state of the instance +// +// https://github.com/apache/cloudstack/blob/master/api/src/main/java/com/cloud/vm/VirtualMachine.java +type VirtualMachineState string + +const ( + // VirtualMachineStarting VM is being started. At this state, you should find host id filled which means it's being started on that host + VirtualMachineStarting VirtualMachineState = "Starting" + // VirtualMachineRunning VM is running. host id has the host that it is running on + VirtualMachineRunning VirtualMachineState = "Running" + // VirtualMachineStopping VM is being stopped. host id has the host that it is being stopped on + VirtualMachineStopping VirtualMachineState = "Stopping" + // VirtualMachineStopped VM is stopped. host id should be null + VirtualMachineStopped VirtualMachineState = "Stopped" + // VirtualMachineDestroyed VM is marked for destroy + VirtualMachineDestroyed VirtualMachineState = "Destroyed" + // VirtualMachineExpunging "VM is being expunged + VirtualMachineExpunging VirtualMachineState = "Expunging" + // VirtualMachineMigrating VM is being migrated. host id holds to from host + VirtualMachineMigrating VirtualMachineState = "Migrating" + // VirtualMachineError VM is in error + VirtualMachineError VirtualMachineState = "Error" + // VirtualMachineUnknown VM state is unknown + VirtualMachineUnknown VirtualMachineState = "Unknown" + // VirtualMachineShutdowned VM is shutdowned from inside + VirtualMachineShutdowned VirtualMachineState = "Shutdowned" +) + +// VirtualMachine represents a virtual machine +// +// See: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/stable/virtual_machines.html type VirtualMachine struct { - ID string `json:"id,omitempty"` - Account string `json:"account,omitempty"` - ClusterID string `json:"clusterid,omitempty"` - ClusterName string `json:"clustername,omitempty"` - CPUNumber int64 `json:"cpunumber,omitempty"` - CPUSpeed int64 `json:"cpuspeed,omitempty"` - CPUUsed string `json:"cpuused,omitempty"` - Created string `json:"created,omitempty"` - Details map[string]string `json:"details,omitempty"` - DiskIoRead int64 `json:"diskioread,omitempty"` - DiskIoWrite int64 `json:"diskiowrite,omitempty"` - DiskKbsRead int64 `json:"diskkbsread,omitempty"` - DiskKbsWrite int64 `json:"diskkbswrite,omitempty"` - DiskOfferingID string `json:"diskofferingid,omitempty"` - DiskOfferingName string `json:"diskofferingname,omitempty"` - DisplayName string `json:"displayname,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty"` - Group string `json:"group,omitempty"` - GroupID string `json:"groupid,omitempty"` - GuestOsID string `json:"guestosid,omitempty"` - HaEnable bool `json:"haenable,omitempty"` - HostID string `json:"hostid,omitempty"` - HostName string `json:"hostname,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - InstanceName string `json:"instancename,omitempty"` // root only - IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty"` - IsoDisplayText string `json:"isodisplaytext,omitempty"` - IsoID string `json:"isoid,omitempty"` - IsoName string `json:"isoname,omitempty"` - KeyPair string `json:"keypair,omitempty"` - Memory int64 `json:"memory,omitempty"` - MemoryIntFreeKbs int64 `json:"memoryintfreekbs,omitempty"` - MemoryKbs int64 `json:"memorykbs,omitempty"` - MemoryTargetKbs int64 `json:"memorytargetkbs,omitempty"` - Name string `json:"name,omitempty"` - NetworkKbsRead int64 `json:"networkkbsread,omitempty"` - NetworkKbsWrite int64 `json:"networkkbswrite,omitempty"` - OsCategoryID string `json:"oscategoryid,omitempty"` - OsTypeID int64 `json:"ostypeid,omitempty"` - Password string `json:"password,omitempty"` - PasswordEnabled bool `json:"passwordenabled,omitempty"` - PCIDevices string `json:"pcidevices,omitempty"` // not in the doc - PodID string `json:"podid,omitempty"` - PodName string `json:"podname,omitempty"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` - PublicIP string `json:"publicip,omitempty"` - PublicIPID string `json:"publicipid,omitempty"` - RootDeviceID int64 `json:"rootdeviceid,omitempty"` - RootDeviceType string `json:"rootdevicetype,omitempty"` - ServiceOfferingID string `json:"serviceofferingid,omitempty"` - ServiceOfferingName string `json:"serviceofferingname,omitempty"` - ServiceState string `json:"servicestate,omitempty"` - State string `json:"state,omitempty"` - TemplateDisplayText string `json:"templatedisplaytext,omitempty"` - TemplateID string `json:"templateid,omitempty"` - TemplateName string `json:"templatename,omitempty"` - UserID string `json:"userid,omitempty"` // not in the doc - UserName string `json:"username,omitempty"` // not in the doc - Vgpu string `json:"vgpu,omitempty"` // not in the doc - ZoneID string `json:"zoneid,omitempty"` - ZoneName string `json:"zonename,omitempty"` - AffinityGroup []AffinityGroup `json:"affinitygroup,omitempty"` - Nic []Nic `json:"nic,omitempty"` - SecurityGroup []SecurityGroup `json:"securitygroup,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - JobID string `json:"jobid,omitempty"` - JobStatus JobStatusType `json:"jobstatus,omitempty"` + Account string `json:"account,omitempty" doc:"the account associated with the virtual machine"` + AccountID *UUID `json:"accountid,omitempty" doc:"the account ID associated with the virtual machine"` + AffinityGroup []AffinityGroup `json:"affinitygroup,omitempty" doc:"list of affinity groups associated with the virtual machine"` + ClusterID *UUID `json:"clusterid,omitempty" doc:"the ID of the vm's cluster"` + ClusterName string `json:"clustername,omitempty" doc:"the name of the vm's cluster"` + CPUNumber int `json:"cpunumber,omitempty" doc:"the number of cpu this virtual machine is running with"` + CPUSpeed int `json:"cpuspeed,omitempty" doc:"the speed of each cpu"` + CPUUsed string `json:"cpuused,omitempty" doc:"the amount of the vm's CPU currently used"` + Created string `json:"created,omitempty" doc:"the date when this virtual machine was created"` + Details map[string]string `json:"details,omitempty" doc:"Vm details in key/value pairs."` + DiskIoRead int64 `json:"diskioread,omitempty" doc:"the read (io) of disk on the vm"` + DiskIoWrite int64 `json:"diskiowrite,omitempty" doc:"the write (io) of disk on the vm"` + DiskKbsRead int64 `json:"diskkbsread,omitempty" doc:"the read (bytes) of disk on the vm"` + DiskKbsWrite int64 `json:"diskkbswrite,omitempty" doc:"the write (bytes) of disk on the vm"` + DiskOfferingID *UUID `json:"diskofferingid,omitempty" doc:"the ID of the disk offering of the virtual machine"` + DiskOfferingName string `json:"diskofferingname,omitempty" doc:"the name of the disk offering of the virtual machine"` + DisplayName string `json:"displayname,omitempty" doc:"user generated name. The name of the virtual machine is returned if no displayname exists."` + DisplayVM bool `json:"displayvm,omitempty" doc:"an optional field whether to the display the vm to the end user or not."` + Domain string `json:"domain,omitempty" doc:"the name of the domain in which the virtual machine exists"` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain in which the virtual machine exists"` + ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty" doc:"the virtual network for the service offering"` + Group string `json:"group,omitempty" doc:"the group name of the virtual machine"` + GroupID *UUID `json:"groupid,omitempty" doc:"the group ID of the virtual machine"` + HAEnable bool `json:"haenable,omitempty" doc:"true if high-availability is enabled, false otherwise"` + HostID *UUID `json:"hostid,omitempty" doc:"the ID of the host for the virtual machine"` + HostName string `json:"hostname,omitempty" doc:"the name of the host for the virtual machine"` + Hypervisor string `json:"hypervisor,omitempty" doc:"the hypervisor on which the template runs"` + ID *UUID `json:"id,omitempty" doc:"the ID of the virtual machine"` + InstanceName string `json:"instancename,omitempty" doc:"instance name of the user vm; this parameter is returned to the ROOT admin only"` + IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty" doc:"true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory."` + IsoDisplayText string `json:"isodisplaytext,omitempty" doc:"an alternate display text of the ISO attached to the virtual machine"` + IsoID *UUID `json:"isoid,omitempty" doc:"the ID of the ISO attached to the virtual machine"` + IsoName string `json:"isoname,omitempty" doc:"the name of the ISO attached to the virtual machine"` + KeyPair string `json:"keypair,omitempty" doc:"ssh key-pair"` + Memory int `json:"memory,omitempty" doc:"the memory allocated for the virtual machine"` + Name string `json:"name,omitempty" doc:"the name of the virtual machine"` + NetworkKbsRead int64 `json:"networkkbsread,omitempty" doc:"the incoming network traffic on the vm"` + NetworkKbsWrite int64 `json:"networkkbswrite,omitempty" doc:"the outgoing network traffic on the host"` + Nic []Nic `json:"nic,omitempty" doc:"the list of nics associated with vm"` + OSCategoryID *UUID `json:"oscategoryid,omitempty" doc:"Os category ID of the virtual machine"` + OSCategoryName string `json:"oscategoryname,omitempty" doc:"Os category name of the virtual machine"` + Password string `json:"password,omitempty" doc:"the password (if exists) of the virtual machine"` + PasswordEnabled bool `json:"passwordenabled,omitempty" doc:"true if the password rest feature is enabled, false otherwise"` + PCIDevices []PCIDevice `json:"pcidevices,omitempty" doc:"list of PCI devices"` + PodID *UUID `json:"podid,omitempty" doc:"the ID of the vm's pod"` + PodName string `json:"podname,omitempty" doc:"the name of the vm's pod"` + PublicIP string `json:"publicip,omitempty" doc:"public IP address id associated with vm via Static nat rule"` + PublicIPID *UUID `json:"publicipid,omitempty" doc:"public IP address id associated with vm via Static nat rule"` + RootDeviceID int64 `json:"rootdeviceid,omitempty" doc:"device ID of the root volume"` + RootDeviceType string `json:"rootdevicetype,omitempty" doc:"device type of the root volume"` + SecurityGroup []SecurityGroup `json:"securitygroup,omitempty" doc:"list of security groups associated with the virtual machine"` + ServiceOfferingID *UUID `json:"serviceofferingid,omitempty" doc:"the ID of the service offering of the virtual machine"` + ServiceOfferingName string `json:"serviceofferingname,omitempty" doc:"the name of the service offering of the virtual machine"` + ServiceState string `json:"servicestate,omitempty" doc:"State of the Service from LB rule"` + State string `json:"state,omitempty" doc:"the state of the virtual machine"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with vm"` + TemplateDisplayText string `json:"templatedisplaytext,omitempty" doc:"an alternate display text of the template for the virtual machine"` + TemplateID *UUID `json:"templateid,omitempty" doc:"the ID of the template for the virtual machine. A -1 is returned if the virtual machine was created from an ISO file."` + TemplateName string `json:"templatename,omitempty" doc:"the name of the template for the virtual machine"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the availablility zone for the virtual machine"` + ZoneName string `json:"zonename,omitempty" doc:"the name of the availability zone for the virtual machine"` } // ResourceType returns the type of the resource -func (*VirtualMachine) ResourceType() string { +func (VirtualMachine) ResourceType() string { return "UserVM" } +// Delete destroys the VM +func (vm VirtualMachine) Delete(ctx context.Context, client *Client) error { + _, err := client.RequestWithContext(ctx, &DestroyVirtualMachine{ + ID: vm.ID, + }) + + return err +} + +// ListRequest builds the ListVirtualMachines request +func (vm VirtualMachine) ListRequest() (ListCommand, error) { + // XXX: AffinityGroupID, SecurityGroupID, Tags + + req := &ListVirtualMachines{ + Account: vm.Account, + DomainID: vm.DomainID, + GroupID: vm.GroupID, + ID: vm.ID, + Name: vm.Name, + State: vm.State, + TemplateID: vm.TemplateID, + ZoneID: vm.ZoneID, + } + + nic := vm.DefaultNic() + if nic != nil { + req.IPAddress = nic.IPAddress + } + + return req, nil +} + +// DefaultNic returns the default nic +func (vm VirtualMachine) DefaultNic() *Nic { + for _, nic := range vm.Nic { + if nic.IsDefault { + return &nic + } + } + + return nil +} + +// IP returns the default nic IP address +func (vm VirtualMachine) IP() *net.IP { + nic := vm.DefaultNic() + if nic != nil { + ip := nic.IPAddress + return &ip + } + + return nil +} + // NicsByType returns the corresponding interfaces base on the given type -func (vm *VirtualMachine) NicsByType(nicType string) []Nic { +func (vm VirtualMachine) NicsByType(nicType string) []Nic { nics := make([]Nic, 0) for _, nic := range vm.Nic { if nic.Type == nicType { - // XXX The CloudStack API forgets to specify it + // XXX The API forgets to specify it nic.VirtualMachineID = vm.ID nics = append(nics, nic) } @@ -96,9 +179,11 @@ func (vm *VirtualMachine) NicsByType(nicType string) []Nic { } // NicByNetworkID returns the corresponding interface based on the given NetworkID -func (vm *VirtualMachine) NicByNetworkID(networkID string) *Nic { +// +// A VM cannot be connected twice to a same network. +func (vm VirtualMachine) NicByNetworkID(networkID UUID) *Nic { for _, nic := range vm.Nic { - if nic.NetworkID == networkID { + if nic.NetworkID.Equal(networkID) { nic.VirtualMachineID = vm.ID return &nic } @@ -107,9 +192,9 @@ func (vm *VirtualMachine) NicByNetworkID(networkID string) *Nic { } // NicByID returns the corresponding interface base on its ID -func (vm *VirtualMachine) NicByID(nicID string) *Nic { +func (vm VirtualMachine) NicByID(nicID UUID) *Nic { for _, nic := range vm.Nic { - if nic.ID == nicID { + if nic.ID.Equal(nicID) { nic.VirtualMachineID = vm.ID return &nic } @@ -120,332 +205,319 @@ func (vm *VirtualMachine) NicByID(nicID string) *Nic { // IPToNetwork represents a mapping between ip and networks type IPToNetwork struct { - IP string `json:"ip,omitempty"` - IPV6 string `json:"ipv6,omitempty"` - NetworkID string `json:"networkid,omitempty"` + IP net.IP `json:"ip,omitempty"` + Ipv6 net.IP `json:"ipv6,omitempty"` + NetworkID *UUID `json:"networkid,omitempty"` } -// VirtualMachineResponse represents a generic Virtual Machine response -type VirtualMachineResponse struct { - VirtualMachine VirtualMachine `json:"virtualmachine"` +// PCIDevice represents a PCI card present in the host +type PCIDevice struct { + PCIVendorName string `json:"pcivendorname,omitempty" doc:"Device vendor name of pci card"` + DeviceID *UUID `json:"deviceid,omitempty" doc:"Device model ID of pci card"` + RemainingCapacity int `json:"remainingcapacity,omitempty" doc:"Remaining capacity in terms of no. of more VMs that can be deployped with this vGPU type"` + MaxCapacity int `json:"maxcapacity,omitempty" doc:"Maximum vgpu can be created with this vgpu type on the given pci group"` + PCIVendorID *UUID `json:"pcivendorid,omitempty" doc:"Device vendor ID of pci card"` + PCIDeviceName string `json:"pcidevicename,omitempty" doc:"Device model name of pci card"` +} + +// Password represents an encrypted password +// +// TODO: method to decrypt it, https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=34014652 +type Password struct { + EncryptedPassword string `json:"encryptedpassword"` +} + +// VirtualMachineUserData represents the base64 encoded user-data +type VirtualMachineUserData struct { + UserData string `json:"userdata,omitempty" doc:"Base 64 encoded VM user data"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"the ID of the virtual machine"` +} + +// Decode decodes as a readable string the content of the user-data (base64 · gzip) +func (userdata VirtualMachineUserData) Decode() (string, error) { + data, err := base64.StdEncoding.DecodeString(userdata.UserData) + if err != nil { + return "", err + } + // 0x1f8b is the magic number for gzip + if len(data) < 2 || data[0] != 0x1f || data[1] != 0x8b { + return string(data), nil + } + gr, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return "", err + } + defer gr.Close() // nolint: errcheck + + str, err := ioutil.ReadAll(gr) + if err != nil { + return "", err + } + return string(str), nil } // DeployVirtualMachine (Async) represents the machine creation // -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/deployVirtualMachine.html +// Regarding the UserData field, the client is responsible to base64 (and probably gzip) it. Doing it within this library would make the integration with other tools, e.g. Terraform harder. type DeployVirtualMachine struct { - ServiceOfferingID string `json:"serviceofferingid"` - TemplateID string `json:"templateid"` - ZoneID string `json:"zoneid"` - Account string `json:"account,omitempty"` - AffinityGroupIDs []string `json:"affinitygroupids,omitempty"` - AffinityGroupNames []string `json:"affinitygroupnames,omitempty"` - CustomID string `json:"customid,omitempty"` // root only - DeploymentPlanner string `json:"deploymentplanner,omitempty"` // root only - Details map[string]string `json:"details,omitempty"` - DiskOfferingID string `json:"diskofferingid,omitempty"` - DisplayName string `json:"displayname,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` - DomainID string `json:"domainid,omitempty"` - Group string `json:"group,omitempty"` - HostID string `json:"hostid,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - IP6Address net.IP `json:"ip6address,omitempty"` - IPAddress net.IP `json:"ipaddress,omitempty"` - IPToNetworkList []IPToNetwork `json:"iptonetworklist,omitempty"` - Keyboard string `json:"keyboard,omitempty"` - KeyPair string `json:"keypair,omitempty"` - Name string `json:"name,omitempty"` - NetworkIDs []string `json:"networkids,omitempty"` // mutually exclusive with IPToNetworkList - ProjectID string `json:"projectid,omitempty"` - RootDiskSize int64 `json:"rootdisksize,omitempty"` // in GiB - SecurityGroupIDs []string `json:"securitygroupids,omitempty"` - SecurityGroupNames []string `json:"securitygroupnames,omitempty"` // does nothing, mutually exclusive - Size string `json:"size,omitempty"` // mutually exclusive with DiskOfferingID - StartVM bool `json:"startvm,omitempty"` - UserData string `json:"userdata,omitempty"` // the client is responsible to base64/gzip it + Account string `json:"account,omitempty" doc:"an optional account for the virtual machine. Must be used with domainId."` + AffinityGroupIDs []UUID `json:"affinitygroupids,omitempty" doc:"comma separated list of affinity groups id that are going to be applied to the virtual machine. Mutually exclusive with affinitygroupnames parameter"` + AffinityGroupNames []string `json:"affinitygroupnames,omitempty" doc:"comma separated list of affinity groups names that are going to be applied to the virtual machine.Mutually exclusive with affinitygroupids parameter"` + CustomID *UUID `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"` + DeploymentPlanner string `json:"deploymentplanner,omitempty" doc:"Deployment planner to use for vm allocation. Available to ROOT admin only"` + Details map[string]string `json:"details,omitempty" doc:"used to specify the custom parameters."` + DiskOfferingID *UUID `json:"diskofferingid,omitempty" doc:"the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created."` + DisplayName string `json:"displayname,omitempty" doc:"an optional user generated name for the virtual machine"` + DisplayVM *bool `json:"displayvm,omitempty" doc:"an optional field, whether to the display the vm to the end user or not."` + DomainID *UUID `json:"domainid,omitempty" doc:"an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used."` + Group string `json:"group,omitempty" doc:"an optional group for the virtual machine"` + HostID *UUID `json:"hostid,omitempty" doc:"destination Host ID to deploy the VM to - parameter available for root admin only"` + Hypervisor string `json:"hypervisor,omitempty" doc:"the hypervisor on which to deploy the virtual machine"` + IP4 *bool `json:"ip4,omitempty" doc:"True to set an IPv4 to the default interface"` + IP6 *bool `json:"ip6,omitempty" doc:"True to set an IPv6 to the default interface"` + IP6Address net.IP `json:"ip6address,omitempty" doc:"the ipv6 address for default vm's network"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"the ip address for default vm's network"` + IPToNetworkList []IPToNetwork `json:"iptonetworklist,omitempty" doc:"ip to network mapping. Can't be specified with networkIds parameter. Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid"` + Keyboard string `json:"keyboard,omitempty" doc:"an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us"` + KeyPair string `json:"keypair,omitempty" doc:"name of the ssh key pair used to login to the virtual machine"` + Name string `json:"name,omitempty" doc:"host name for the virtual machine"` + NetworkIDs []UUID `json:"networkids,omitempty" doc:"list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter"` + RootDiskSize int64 `json:"rootdisksize,omitempty" doc:"Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided"` + SecurityGroupIDs []UUID `json:"securitygroupids,omitempty" doc:"comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupnames parameter"` + SecurityGroupNames []string `json:"securitygroupnames,omitempty" doc:"comma separated list of security groups names that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support. Mutually exclusive with securitygroupids parameter"` + ServiceOfferingID *UUID `json:"serviceofferingid" doc:"the ID of the service offering for the virtual machine"` + Size int64 `json:"size,omitempty" doc:"the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId"` + StartVM *bool `json:"startvm,omitempty" doc:"true if start vm after creating. Default value is true"` + TemplateID *UUID `json:"templateid" doc:"the ID of the template for the virtual machine"` + UserData string `json:"userdata,omitempty" doc:"an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding."` + ZoneID *UUID `json:"zoneid" doc:"availability zone for the virtual machine"` + _ bool `name:"deployVirtualMachine" description:"Creates and automatically starts a virtual machine based on a service offering, disk offering, and template."` } -func (*DeployVirtualMachine) name() string { - return "deployVirtualMachine" +func (req DeployVirtualMachine) onBeforeSend(params url.Values) error { + // Either AffinityGroupIDs or AffinityGroupNames must be set + if len(req.AffinityGroupIDs) > 0 && len(req.AffinityGroupNames) > 0 { + return fmt.Errorf("either AffinityGroupIDs or AffinityGroupNames must be set") + } + + // Either SecurityGroupIDs or SecurityGroupNames must be set + if len(req.SecurityGroupIDs) > 0 && len(req.SecurityGroupNames) > 0 { + return fmt.Errorf("either SecurityGroupIDs or SecurityGroupNames must be set") + } + + return nil } -func (*DeployVirtualMachine) asyncResponse() interface{} { - return new(DeployVirtualMachineResponse) +func (DeployVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -// DeployVirtualMachineResponse represents a deployed VM instance -type DeployVirtualMachineResponse VirtualMachineResponse +func (DeployVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) +} // StartVirtualMachine (Async) represents the creation of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/startVirtualMachine.html type StartVirtualMachine struct { - ID string `json:"id"` - DeploymentPlanner string `json:"deploymentplanner,omitempty"` // root only - HostID string `json:"hostid,omitempty"` // root only + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + DeploymentPlanner string `json:"deploymentplanner,omitempty" doc:"Deployment planner to use for vm allocation. Available to ROOT admin only"` + HostID *UUID `json:"hostid,omitempty" doc:"destination Host ID to deploy the VM to - parameter available for root admin only"` + _ bool `name:"startVirtualMachine" description:"Starts a virtual machine."` } -func (*StartVirtualMachine) name() string { - return "startVirtualMachine" -} -func (*StartVirtualMachine) asyncResponse() interface{} { - return new(StartVirtualMachineResponse) +func (StartVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -// StartVirtualMachineResponse represents a started VM instance -type StartVirtualMachineResponse VirtualMachineResponse +func (StartVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) +} // StopVirtualMachine (Async) represents the stopping of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/stopVirtualMachine.html type StopVirtualMachine struct { - ID string `json:"id"` - Forced bool `json:"forced,omitempty"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + Forced *bool `json:"forced,omitempty" doc:"Force stop the VM (vm is marked as Stopped even when command fails to be send to the backend). The caller knows the VM is stopped."` + _ bool `name:"stopVirtualMachine" description:"Stops a virtual machine."` } -func (*StopVirtualMachine) name() string { - return "stopVirtualMachine" +func (StopVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*StopVirtualMachine) asyncResponse() interface{} { - return new(StopVirtualMachineResponse) +func (StopVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -// StopVirtualMachineResponse represents a stopped VM instance -type StopVirtualMachineResponse VirtualMachineResponse - // RebootVirtualMachine (Async) represents the rebooting of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/rebootVirtualMachine.html type RebootVirtualMachine struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + _ bool `name:"rebootVirtualMachine" description:"Reboots a virtual machine."` } -func (*RebootVirtualMachine) name() string { - return "rebootVirtualMachine" +func (RebootVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*RebootVirtualMachine) asyncResponse() interface{} { - return new(RebootVirtualMachineResponse) +func (RebootVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -// RebootVirtualMachineResponse represents a rebooted VM instance -type RebootVirtualMachineResponse VirtualMachineResponse - // RestoreVirtualMachine (Async) represents the restoration of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/restoreVirtualMachine.html type RestoreVirtualMachine struct { - VirtualMachineID string `json:"virtualmachineid"` - TemplateID string `json:"templateid,omitempty"` + VirtualMachineID *UUID `json:"virtualmachineid" doc:"Virtual Machine ID"` + TemplateID *UUID `json:"templateid,omitempty" doc:"an optional template Id to restore vm from the new template. This can be an ISO id in case of restore vm deployed using ISO"` + RootDiskSize int64 `json:"rootdisksize,omitempty" doc:"Optional field to resize root disk on restore. Value is in GB. Only applies to template-based deployments."` + _ bool `name:"restoreVirtualMachine" description:"Restore a VM to original template/ISO or new template/ISO"` } -func (*RestoreVirtualMachine) name() string { - return "restoreVirtualMachine" +func (RestoreVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*RestoreVirtualMachine) asyncResponse() interface{} { - return new(RestoreVirtualMachineResponse) +func (RestoreVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -// RestoreVirtualMachineResponse represents a restored VM instance -type RestoreVirtualMachineResponse VirtualMachineResponse - // RecoverVirtualMachine represents the restoration of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/recoverVirtualMachine.html type RecoverVirtualMachine struct { - ID string `json:"virtualmachineid"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + _ bool `name:"recoverVirtualMachine" description:"Recovers a virtual machine."` } -func (*RecoverVirtualMachine) name() string { - return "recoverVirtualMachine" +func (RecoverVirtualMachine) response() interface{} { + return new(VirtualMachine) } -func (*RecoverVirtualMachine) response() interface{} { - return new(RecoverVirtualMachineResponse) -} - -// RecoverVirtualMachineResponse represents a recovered VM instance -type RecoverVirtualMachineResponse VirtualMachineResponse - // DestroyVirtualMachine (Async) represents the destruction of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/destroyVirtualMachine.html type DestroyVirtualMachine struct { - ID string `json:"id"` - Expunge bool `json:"expunge,omitempty"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + Expunge *bool `json:"expunge,omitempty" doc:"If true is passed, the vm is expunged immediately. False by default."` + _ bool `name:"destroyVirtualMachine" description:"Destroys a virtual machine."` } -func (*DestroyVirtualMachine) name() string { - return "destroyVirtualMachine" +func (DestroyVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*DestroyVirtualMachine) asyncResponse() interface{} { - return new(DestroyVirtualMachineResponse) +func (DestroyVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -// DestroyVirtualMachineResponse represents a destroyed VM instance -type DestroyVirtualMachineResponse VirtualMachineResponse - // UpdateVirtualMachine represents the update of the virtual machine -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/updateVirtualMachine.html type UpdateVirtualMachine struct { - ID string `json:"id"` - CustomID string `json:"customid,omitempty"` // root only - Details map[string]string `json:"details,omitempty"` - DisplayName string `json:"displayname,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` - Group string `json:"group,omitempty"` - HAEnable bool `json:"haenable,omitempty"` - IsDynamicallyScalable bool `json:"isdynamicallyscalable,omitempty"` - Name string `json:"name,omitempty"` // must reboot - OsTypeID int64 `json:"ostypeid,omitempty"` - SecurityGroupIDs []string `json:"securitygroupids,omitempty"` - UserData string `json:"userdata,omitempty"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + CustomID *UUID `json:"customid,omitempty" doc:"an optional field, in case you want to set a custom id to the resource. Allowed to Root Admins only"` + Details map[string]string `json:"details,omitempty" doc:"Details in key/value pairs."` + DisplayName string `json:"displayname,omitempty" doc:"user generated name"` + DisplayVM *bool `json:"displayvm,omitempty" doc:"an optional field, whether to the display the vm to the end user or not."` + Group string `json:"group,omitempty" doc:"group of the virtual machine"` + HAEnable *bool `json:"haenable,omitempty" doc:"true if high-availability is enabled for the virtual machine, false otherwise"` + IsDynamicallyScalable *bool `json:"isdynamicallyscalable,omitempty" doc:"true if VM contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory"` + Name string `json:"name,omitempty" doc:"new host name of the vm. The VM has to be stopped/started for this update to take affect"` + SecurityGroupIDs []UUID `json:"securitygroupids,omitempty" doc:"list of security group ids to be applied on the virtual machine."` + UserData string `json:"userdata,omitempty" doc:"an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding."` + _ bool `name:"updateVirtualMachine" description:"Updates properties of a virtual machine. The VM has to be stopped and restarted for the new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. Therefore, stop the VM manually before issuing this call."` } -func (*UpdateVirtualMachine) name() string { - return "updateVirtualMachine" +func (UpdateVirtualMachine) response() interface{} { + return new(VirtualMachine) } -func (*UpdateVirtualMachine) response() interface{} { - return new(UpdateVirtualMachineResponse) -} - -// UpdateVirtualMachineResponse represents an updated VM instance -type UpdateVirtualMachineResponse VirtualMachineResponse - // ExpungeVirtualMachine represents the annihilation of a VM type ExpungeVirtualMachine struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + _ bool `name:"expungeVirtualMachine" description:"Expunge a virtual machine. Once expunged, it cannot be recoverd."` } -func (*ExpungeVirtualMachine) name() string { - return "expungeVirtualMachine" +func (ExpungeVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*ExpungeVirtualMachine) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (ExpungeVirtualMachine) asyncResponse() interface{} { + return new(booleanResponse) } -// ScaleVirtualMachine (Async) represents the scaling of a VM +// ScaleVirtualMachine (Async) scales the virtual machine to a new service offering. // // ChangeServiceForVirtualMachine does the same thing but returns the // new Virtual Machine which is more consistent with the rest of the API. -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/scaleVirtualMachine.html type ScaleVirtualMachine struct { - ID string `json:"id"` - ServiceOfferingID string `json:"serviceofferingid"` - Details map[string]string `json:"details,omitempty"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + ServiceOfferingID *UUID `json:"serviceofferingid" doc:"the ID of the service offering for the virtual machine"` + Details map[string]string `json:"details,omitempty" doc:"name value pairs of custom parameters for cpu,memory and cpunumber. example details[i].name=value"` + _ bool `name:"scaleVirtualMachine" description:"Scales the virtual machine to a new service offering."` } -func (*ScaleVirtualMachine) name() string { - return "scaleVirtualMachine" +func (ScaleVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*ScaleVirtualMachine) asyncResponse() interface{} { - return new(booleanAsyncResponse) +func (ScaleVirtualMachine) asyncResponse() interface{} { + return new(booleanResponse) } -// ChangeServiceForVirtualMachine represents the scaling of a VM -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/changeServiceForVirtualMachine.html -type ChangeServiceForVirtualMachine ScaleVirtualMachine - -func (*ChangeServiceForVirtualMachine) name() string { - return "changeServiceForVirtualMachine" +// ChangeServiceForVirtualMachine changes the service offering for a virtual machine. The virtual machine must be in a "Stopped" state for this command to take effect. +type ChangeServiceForVirtualMachine struct { + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + ServiceOfferingID *UUID `json:"serviceofferingid" doc:"the service offering ID to apply to the virtual machine"` + Details map[string]string `json:"details,omitempty" doc:"name value pairs of custom parameters for cpu, memory and cpunumber. example details[i].name=value"` + _ bool `name:"changeServiceForVirtualMachine" description:"Changes the service offering for a virtual machine. The virtual machine must be in a \"Stopped\" state for this command to take effect."` } -func (*ChangeServiceForVirtualMachine) response() interface{} { - return new(ChangeServiceForVirtualMachineResponse) +func (ChangeServiceForVirtualMachine) response() interface{} { + return new(VirtualMachine) } -// ChangeServiceForVirtualMachineResponse represents an changed VM instance -type ChangeServiceForVirtualMachineResponse VirtualMachineResponse - -// ResetPasswordForVirtualMachine (Async) represents the scaling of a VM -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/resetPasswordForVirtualMachine.html -type ResetPasswordForVirtualMachine ScaleVirtualMachine - -func (*ResetPasswordForVirtualMachine) name() string { - return "resetPasswordForVirtualMachine" +// ResetPasswordForVirtualMachine resets the password for virtual machine. The virtual machine must be in a "Stopped" state... +type ResetPasswordForVirtualMachine struct { + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + _ bool `name:"resetPasswordForVirtualMachine" description:"Resets the password for virtual machine. The virtual machine must be in a \"Stopped\" state and the template must already support this feature for this command to take effect."` } -func (*ResetPasswordForVirtualMachine) asyncResponse() interface{} { - return new(ResetPasswordForVirtualMachineResponse) +func (ResetPasswordForVirtualMachine) response() interface{} { + return new(AsyncJobResult) +} +func (ResetPasswordForVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } - -// ResetPasswordForVirtualMachineResponse represents the updated vm -type ResetPasswordForVirtualMachineResponse VirtualMachineResponse // GetVMPassword asks for an encrypted password -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/getVMPassword.html type GetVMPassword struct { - ID string `json:"id"` + ID *UUID `json:"id" doc:"The ID of the virtual machine"` + _ bool `name:"getVMPassword" description:"Returns an encrypted password for the VM"` } -func (*GetVMPassword) name() string { - return "getVMPassword" -} - -func (*GetVMPassword) response() interface{} { - return new(GetVMPasswordResponse) -} - -// GetVMPasswordResponse represents the encrypted password -type GetVMPasswordResponse struct { - // Base64 encrypted password for the VM - EncryptedPassword string `json:"encryptedpassword"` +func (GetVMPassword) response() interface{} { + return new(Password) } // ListVirtualMachines represents a search for a VM -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listVirtualMachine.html type ListVirtualMachines struct { - Account string `json:"account,omitempty"` - AffinityGroupID string `json:"affinitygroupid,omitempty"` - Details map[string]string `json:"details,omitempty"` - DisplayVM bool `json:"displayvm,omitempty"` // root only - DomainID string `json:"domainid,omitempty"` - ForVirtualNetwork bool `json:"forvirtualnetwork,omitempty"` - GroupID string `json:"groupid,omitempty"` - HostID string `json:"hostid,omitempty"` - Hypervisor string `json:"hypervisor,omitempty"` - ID string `json:"id,omitempty"` - IDs []string `json:"ids,omitempty"` // mutually exclusive with id - IsoID string `json:"isoid,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - KeyPair string `json:"keypair,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Name string `json:"name,omitempty"` - NetworkID string `json:"networkid,omitempty"` - Page int `json:"page,omitempty"` - PageSize int `json:"pagesize,omitempty"` - PodID string `json:"podid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - ServiceOfferindID string `json:"serviceofferingid,omitempty"` - State string `json:"state,omitempty"` // Running, Stopped, Present, ... - StorageID string `json:"storageid,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - TemplateID string `json:"templateid,omitempty"` - UserID string `json:"userid,omitempty"` - VpcID string `json:"vpcid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListVirtualMachines) name() string { - return "listVirtualMachines" -} - -func (*ListVirtualMachines) response() interface{} { - return new(ListVirtualMachinesResponse) + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + AffinityGroupID *UUID `json:"affinitygroupid,omitempty" doc:"list vms by affinity group"` + Details []string `json:"details,omitempty" doc:"comma separated list of host details requested, value can be a list of [all, group, nics, stats, secgrp, tmpl, servoff, diskoff, iso, volume, min, affgrp]. If no parameter is passed in, the details will be defaulted to all"` + DisplayVM *bool `json:"displayvm,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ForVirtualNetwork *bool `json:"forvirtualnetwork,omitempty" doc:"list by network type; true if need to list vms using Virtual Network, false otherwise"` + GroupID *UUID `json:"groupid,omitempty" doc:"the group ID"` + HostID *UUID `json:"hostid,omitempty" doc:"the host ID"` + Hypervisor string `json:"hypervisor,omitempty" doc:"the target hypervisor for the template"` + ID *UUID `json:"id,omitempty" doc:"the ID of the virtual machine"` + IDs []string `json:"ids,omitempty" doc:"the IDs of the virtual machines, mutually exclusive with id"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"an IP address to filter the result"` + IsoID *UUID `json:"isoid,omitempty" doc:"list vms by iso"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"name of the virtual machine"` + NetworkID *UUID `json:"networkid,omitempty" doc:"list by network id"` + Page int `json:"page,omitempty"` + PageSize int `json:"pagesize,omitempty"` + ServiceOfferindID *UUID `json:"serviceofferingid,omitempty" doc:"list by the service offering"` + State string `json:"state,omitempty" doc:"state of the virtual machine"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + TemplateID *UUID `json:"templateid,omitempty" doc:"list vms by template"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the availability zone ID"` + _ bool `name:"listVirtualMachines" description:"List the virtual machines owned by the account."` } // ListVirtualMachinesResponse represents a list of virtual machines @@ -454,61 +526,103 @@ type ListVirtualMachinesResponse struct { VirtualMachine []VirtualMachine `json:"virtualmachine"` } +func (ListVirtualMachines) response() interface{} { + return new(ListVirtualMachinesResponse) +} + +// SetPage sets the current page +func (ls *ListVirtualMachines) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListVirtualMachines) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListVirtualMachines) each(resp interface{}, callback IterateItemFunc) { + vms, ok := resp.(*ListVirtualMachinesResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListVirtualMachinesResponse expected, got %T", resp)) + return + } + + for i := range vms.VirtualMachine { + if !callback(&vms.VirtualMachine[i], nil) { + break + } + } +} + // AddNicToVirtualMachine (Async) adds a NIC to a VM -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/addNicToVirtualMachine.html type AddNicToVirtualMachine struct { - NetworkID string `json:"networkid"` - VirtualMachineID string `json:"virtualmachineid"` - IPAddress net.IP `json:"ipaddress,omitempty"` + NetworkID *UUID `json:"networkid" doc:"Network ID"` + VirtualMachineID *UUID `json:"virtualmachineid" doc:"Virtual Machine ID"` + IPAddress net.IP `json:"ipaddress,omitempty" doc:"IP Address for the new network"` + _ bool `name:"addNicToVirtualMachine" description:"Adds VM to specified network by creating a NIC"` } -func (*AddNicToVirtualMachine) name() string { - return "addNicToVirtualMachine" +func (AddNicToVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*AddNicToVirtualMachine) asyncResponse() interface{} { - return new(AddNicToVirtualMachineResponse) +func (AddNicToVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -// AddNicToVirtualMachineResponse represents the modified VM -type AddNicToVirtualMachineResponse VirtualMachineResponse - // RemoveNicFromVirtualMachine (Async) removes a NIC from a VM -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/removeNicFromVirtualMachine.html type RemoveNicFromVirtualMachine struct { - NicID string `json:"nicid"` - VirtualMachineID string `json:"virtualmachineid"` + NicID *UUID `json:"nicid" doc:"NIC ID"` + VirtualMachineID *UUID `json:"virtualmachineid" doc:"Virtual Machine ID"` + _ bool `name:"removeNicFromVirtualMachine" description:"Removes VM from specified network by deleting a NIC"` } -func (*RemoveNicFromVirtualMachine) name() string { - return "removeNicFromVirtualMachine" +func (RemoveNicFromVirtualMachine) response() interface{} { + return new(AsyncJobResult) } -func (*RemoveNicFromVirtualMachine) asyncResponse() interface{} { - return new(RemoveNicFromVirtualMachineResponse) +func (RemoveNicFromVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -// RemoveNicFromVirtualMachineResponse represents the modified VM -type RemoveNicFromVirtualMachineResponse VirtualMachineResponse - // UpdateDefaultNicForVirtualMachine (Async) adds a NIC to a VM -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateDefaultNicForVirtualMachine.html type UpdateDefaultNicForVirtualMachine struct { - NetworkID string `json:"networkid"` - VirtualMachineID string `json:"virtualmachineid"` - IPAddress net.IP `json:"ipaddress,omitempty"` + NicID *UUID `json:"nicid" doc:"NIC ID"` + VirtualMachineID *UUID `json:"virtualmachineid" doc:"Virtual Machine ID"` + _ bool `name:"updateDefaultNicForVirtualMachine" description:"Changes the default NIC on a VM"` } -func (*UpdateDefaultNicForVirtualMachine) name() string { - return "updateDefaultNicForVirtualMachine" +func (UpdateDefaultNicForVirtualMachine) response() interface{} { + return new(AsyncJobResult) +} +func (UpdateDefaultNicForVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) } -func (*UpdateDefaultNicForVirtualMachine) asyncResponse() interface{} { - return new(UpdateDefaultNicForVirtualMachineResponse) +// GetVirtualMachineUserData returns the user-data of the given VM +type GetVirtualMachineUserData struct { + VirtualMachineID *UUID `json:"virtualmachineid" doc:"The ID of the virtual machine"` + _ bool `name:"getVirtualMachineUserData" description:"Returns user data associated with the VM"` } -// UpdateDefaultNicForVirtualMachineResponse represents the modified VM -type UpdateDefaultNicForVirtualMachineResponse VirtualMachineResponse +func (GetVirtualMachineUserData) response() interface{} { + return new(VirtualMachineUserData) +} + +// Decode decodes the base64 / gzipped encoded user data + +// MigrateVirtualMachine (Async) attempts migration of a VM to a different host or Root volume of the vm to a different storage pool +type MigrateVirtualMachine struct { + HostID *UUID `json:"hostid,omitempty" doc:"Destination Host ID to migrate VM to. Required for live migrating a VM from host to host"` + StorageID *UUID `json:"storageid,omitempty" doc:"Destination storage pool ID to migrate VM volumes to. Required for migrating the root disk volume"` + VirtualMachineID *UUID `json:"virtualmachineid" doc:"the ID of the virtual machine"` + _ bool `name:"migrateVirtualMachine" description:"Attempts Migration of a VM to a different host or Root volume of the vm to a different storage pool"` +} + +func (MigrateVirtualMachine) response() interface{} { + return new(AsyncJobResult) +} + +func (MigrateVirtualMachine) asyncResponse() interface{} { + return new(VirtualMachine) +} diff --git a/vendor/github.com/exoscale/egoscale/vm_groups.go b/vendor/github.com/exoscale/egoscale/vm_groups.go index 511d707d0..7937e9cf9 100644 --- a/vendor/github.com/exoscale/egoscale/vm_groups.go +++ b/vendor/github.com/exoscale/egoscale/vm_groups.go @@ -2,101 +2,59 @@ package egoscale // InstanceGroup represents a group of VM type InstanceGroup struct { - ID string `json:"id"` - Account string `json:"account,omitempty"` - Created string `json:"created,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Name string `json:"name,omitempty"` - Project string `json:"project,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -// InstanceGroupResponse represents a VM group -type InstanceGroupResponse struct { - InstanceGroup InstanceGroup `json:"instancegroup"` + Account string `json:"account,omitempty" doc:"the account owning the instance group"` + Created string `json:"created,omitempty" doc:"time and date the instance group was created"` + Domain string `json:"domain,omitempty" doc:"the domain name of the instance group"` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of the instance group"` + ID *UUID `json:"id,omitempty" doc:"the id of the instance group"` + Name string `json:"name,omitempty" doc:"the name of the instance group"` } // CreateInstanceGroup creates a VM group -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/createInstanceGroup.html type CreateInstanceGroup struct { - Name string `json:"name"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` + Name string `json:"name" doc:"the name of the instance group"` + Account string `json:"account,omitempty" doc:"the account of the instance group. The account parameter must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"the domain ID of account owning the instance group"` + _ bool `name:"createInstanceGroup" description:"Creates a vm group"` } -func (*CreateInstanceGroup) name() string { - return "createInstanceGroup" +func (CreateInstanceGroup) response() interface{} { + return new(InstanceGroup) } -func (*CreateInstanceGroup) response() interface{} { - return new(CreateInstanceGroupResponse) -} - -// CreateInstanceGroupResponse represents a freshly created VM group -type CreateInstanceGroupResponse InstanceGroupResponse - -// UpdateInstanceGroup creates a VM group -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/updateInstanceGroup.html +// UpdateInstanceGroup updates a VM group type UpdateInstanceGroup struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` + ID *UUID `json:"id" doc:"Instance group ID"` + Name string `json:"name,omitempty" doc:"new instance group name"` + _ bool `name:"updateInstanceGroup" description:"Updates a vm group"` } -func (*UpdateInstanceGroup) name() string { - return "updateInstanceGroup" +func (UpdateInstanceGroup) response() interface{} { + return new(InstanceGroup) } -func (*UpdateInstanceGroup) response() interface{} { - return new(UpdateInstanceGroupResponse) -} - -// UpdateInstanceGroupResponse represents an updated VM group -type UpdateInstanceGroupResponse InstanceGroupResponse - -// DeleteInstanceGroup creates a VM group -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/deleteInstanceGroup.html +// DeleteInstanceGroup deletes a VM group type DeleteInstanceGroup struct { - Name string `json:"name"` - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ProjectID string `json:"projectid,omitempty"` + ID *UUID `json:"id" doc:"the ID of the instance group"` + _ bool `name:"deleteInstanceGroup" description:"Deletes a vm group"` } -func (*DeleteInstanceGroup) name() string { - return "deleteInstanceGroup" -} - -func (*DeleteInstanceGroup) response() interface{} { - return new(booleanSyncResponse) +func (DeleteInstanceGroup) response() interface{} { + return new(booleanResponse) } // ListInstanceGroups lists VM groups -// -// CloudStack API: http://cloudstack.apache.org/api/apidocs-4.10/apis/listInstanceGroups.html type ListInstanceGroups struct { - Account string `json:"account,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + ID *UUID `json:"id,omitempty" doc:"list instance groups by ID"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"list instance groups by name"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - State string `json:"state,omitempty"` - ProjectID string `json:"projectid,omitempty"` -} - -func (*ListInstanceGroups) name() string { - return "listInstanceGroups" -} - -func (*ListInstanceGroups) response() interface{} { - return new(ListInstanceGroupsResponse) + _ bool `name:"listInstanceGroups" description:"Lists vm groups"` } // ListInstanceGroupsResponse represents a list of instance groups @@ -104,3 +62,7 @@ type ListInstanceGroupsResponse struct { Count int `json:"count"` InstanceGroup []InstanceGroup `json:"instancegroup"` } + +func (ListInstanceGroups) response() interface{} { + return new(ListInstanceGroupsResponse) +} diff --git a/vendor/github.com/exoscale/egoscale/volumes.go b/vendor/github.com/exoscale/egoscale/volumes.go index 63d8b47f1..e3fbbebb5 100644 --- a/vendor/github.com/exoscale/egoscale/volumes.go +++ b/vendor/github.com/exoscale/egoscale/volumes.go @@ -6,92 +6,117 @@ import ( // Volume represents a volume linked to a VM type Volume struct { - ID string `json:"id"` - Account string `json:"account,omitempty"` - Attached string `json:"attached,omitempty"` - ChainInfo string `json:"chaininfo,omitempty"` - Created string `json:"created,omitempty"` - Destroyed bool `json:"destroyed,omitempty"` - DisplayVolume bool `json:"displayvolume,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - Name string `json:"name,omitempty"` - QuiesceVM bool `json:"quiescevm,omitempty"` - ServiceOfferingDisplayText string `json:"serviceofferingdisplaytext,omitempty"` - ServiceOfferingID string `json:"serviceofferingid,omitempty"` - ServiceOfferingName string `json:"serviceofferingname,omitempty"` - Size uint64 `json:"size,omitempty"` - State string `json:"state,omitempty"` - Type string `json:"type,omitempty"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` - VMName string `json:"vmname,omitempty"` - VMState string `json:"vmstate,omitempty"` - ZoneID string `json:"zoneid,omitempty"` - ZoneName string `json:"zonename,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - JobID string `json:"jobid,omitempty"` - JobStatus JobStatusType `json:"jobstatus,omitempty"` + Account string `json:"account,omitempty" doc:"the account associated with the disk volume"` + Attached string `json:"attached,omitempty" doc:"the date the volume was attached to a VM instance"` + ChainInfo string `json:"chaininfo,omitempty" doc:"the chain info of the volume"` + ClusterID *UUID `json:"clusterid,omitempty" doc:"ID of the cluster"` + ClusterName string `json:"clustername,omitempty" doc:"name of the cluster"` + Created string `json:"created,omitempty" doc:"the date the disk volume was created"` + Destroyed bool `json:"destroyed,omitempty" doc:"the boolean state of whether the volume is destroyed or not"` + DeviceID int64 `json:"deviceid,omitempty" doc:"the ID of the device on user vm the volume is attahed to. This tag is not returned when the volume is detached."` + DiskBytesReadRate int64 `json:"diskBytesReadRate,omitempty" doc:"bytes read rate of the disk volume"` + DiskBytesWriteRate int64 `json:"diskBytesWriteRate,omitempty" doc:"bytes write rate of the disk volume"` + DiskIopsReadRate int64 `json:"diskIopsReadRate,omitempty" doc:"io requests read rate of the disk volume"` + DiskIopsWriteRate int64 `json:"diskIopsWriteRate,omitempty" doc:"io requests write rate of the disk volume"` + DiskOfferingDisplayText string `json:"diskofferingdisplaytext,omitempty" doc:"the display text of the disk offering"` + DiskOfferingID *UUID `json:"diskofferingid,omitempty" doc:"ID of the disk offering"` + DiskOfferingName string `json:"diskofferingname,omitempty" doc:"name of the disk offering"` + DisplayVolume bool `json:"displayvolume,omitempty" doc:"an optional field whether to the display the volume to the end user or not."` + Domain string `json:"domain,omitempty" doc:"the domain associated with the disk volume"` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain associated with the disk volume"` + Hypervisor string `json:"hypervisor,omitempty" doc:"Hypervisor the volume belongs to"` + ID *UUID `json:"id,omitempty" doc:"ID of the disk volume"` + IsExtractable *bool `json:"isextractable,omitempty" doc:"true if the volume is extractable, false otherwise"` + IsoDisplayText string `json:"isodisplaytext,omitempty" doc:"an alternate display text of the ISO attached to the virtual machine"` + IsoID *UUID `json:"isoid,omitempty" doc:"the ID of the ISO attached to the virtual machine"` + IsoName string `json:"isoname,omitempty" doc:"the name of the ISO attached to the virtual machine"` + MaxIops int64 `json:"maxiops,omitempty" doc:"max iops of the disk volume"` + MinIops int64 `json:"miniops,omitempty" doc:"min iops of the disk volume"` + Name string `json:"name,omitempty" doc:"name of the disk volume"` + Path string `json:"path,omitempty" doc:"the path of the volume"` + PodID *UUID `json:"podid,omitempty" doc:"ID of the pod"` + PodName string `json:"podname,omitempty" doc:"name of the pod"` + QuiesceVM bool `json:"quiescevm,omitempty" doc:"need quiesce vm or not when taking snapshot"` + ServiceOfferingDisplayText string `json:"serviceofferingdisplaytext,omitempty" doc:"the display text of the service offering for root disk"` + ServiceOfferingID *UUID `json:"serviceofferingid,omitempty" doc:"ID of the service offering for root disk"` + ServiceOfferingName string `json:"serviceofferingname,omitempty" doc:"name of the service offering for root disk"` + Size uint64 `json:"size,omitempty" doc:"size of the disk volume"` + SnapshotID *UUID `json:"snapshotid,omitempty" doc:"ID of the snapshot from which this volume was created"` + State string `json:"state,omitempty" doc:"the state of the disk volume"` + Status string `json:"status,omitempty" doc:"the status of the volume"` + Storage string `json:"storage,omitempty" doc:"name of the primary storage hosting the disk volume"` + StorageID *UUID `json:"storageid,omitempty" doc:"id of the primary storage hosting the disk volume; returned to admin user only"` + StorageType string `json:"storagetype,omitempty" doc:"shared or local storage"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with volume"` + TemplateDisplayText string `json:"templatedisplaytext,omitempty" doc:"an alternate display text of the template for the virtual machine"` + TemplateID string `json:"templateid,omitempty" doc:"the ID of the template for the virtual machine. A -1 is returned if the virtual machine was created from an ISO file."` // no *UUID because of the -1 thingy... + TemplateName string `json:"templatename,omitempty" doc:"the name of the template for the virtual machine"` + Type string `json:"type,omitempty" doc:"type of the disk volume (ROOT or DATADISK)"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"id of the virtual machine"` + VMDisplayName string `json:"vmdisplayname,omitempty" doc:"display name of the virtual machine"` + VMName string `json:"vmname,omitempty" doc:"name of the virtual machine"` + VMState string `json:"vmstate,omitempty" doc:"state of the virtual machine"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"ID of the availability zone"` + ZoneName string `json:"zonename,omitempty" doc:"name of the availability zone"` } // ResourceType returns the type of the resource -func (*Volume) ResourceType() string { +func (Volume) ResourceType() string { return "Volume" } +// ListRequest builds the ListVolumes request +func (vol Volume) ListRequest() (ListCommand, error) { + req := &ListVolumes{ + Account: vol.Account, + DomainID: vol.DomainID, + Name: vol.Name, + Type: vol.Type, + VirtualMachineID: vol.VirtualMachineID, + ZoneID: vol.ZoneID, + } + + return req, nil +} + // ResizeVolume (Async) resizes a volume -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/resizeVolume.html type ResizeVolume struct { - ID string `json:"id"` - DiskOfferingID string `json:"diskofferingid,omitempty"` - ShrinkOk bool `json:"shrinkok,omitempty"` - Size int64 `json:"size,omitempty"` // in GiB + ID *UUID `json:"id" doc:"the ID of the disk volume"` + DiskOfferingID *UUID `json:"diskofferingid,omitempty" doc:"new disk offering id"` + ShrinkOk *bool `json:"shrinkok,omitempty" doc:"Verify OK to Shrink"` + Size int64 `json:"size,omitempty" doc:"New volume size in G"` + _ bool `name:"resizeVolume" description:"Resizes a volume"` } -func (*ResizeVolume) name() string { - return "resizeVolume" +func (ResizeVolume) response() interface{} { + return new(AsyncJobResult) } -func (*ResizeVolume) asyncResponse() interface{} { - return new(ResizeVolumeResponse) -} - -// ResizeVolumeResponse represents the new Volume -type ResizeVolumeResponse struct { - Volume Volume `json:"volume"` +func (ResizeVolume) asyncResponse() interface{} { + return new(Volume) } // ListVolumes represents a query listing volumes -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listVolumes.html type ListVolumes struct { - Account string `json:"account,omitempty"` - DiskOfferingID string `json:"diskoffering,omitempty"` - DisplayVolume string `json:"displayvolume,omitempty"` // root only - DomainID string `json:"domainid,omitempty"` - HostID string `json:"hostid,omitempty"` - ID string `json:"id,omitempty"` - IsRecursive bool `json:"isrecursive,omitempty"` - Keyword string `json:"keyword,omitempty"` - ListAll bool `json:"listall,omitempty"` - Name string `json:"name,omitempty"` + Account string `json:"account,omitempty" doc:"list resources by account. Must be used with the domainId parameter."` + DiskOfferingID *UUID `json:"diskofferingid,omitempty" doc:"list volumes by disk offering"` + DisplayVolume *bool `json:"displayvolume,omitempty" doc:"list resources by display flag; only ROOT admin is eligible to pass this parameter"` + DomainID *UUID `json:"domainid,omitempty" doc:"list only resources belonging to the domain specified"` + HostID *UUID `json:"hostid,omitempty" doc:"list volumes on specified host"` + ID *UUID `json:"id,omitempty" doc:"the ID of the disk volume"` + IsRecursive *bool `json:"isrecursive,omitempty" doc:"defaults to false, but if true, lists all resources from the parent specified by the domainId till leaves."` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + ListAll *bool `json:"listall,omitempty" doc:"If set to false, list only resources belonging to the command's caller; if set to true - list resources that the caller is authorized to see. Default value is false"` + Name string `json:"name,omitempty" doc:"the name of the disk volume"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - PodID string `json:"podid,omitempty"` - ProjectID string `json:"projectid,omitempty"` - StorageID string `json:"storageid,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` - Type string `json:"type,omitempty"` - VirtualMachineID string `json:"virtualmachineid,omitempty"` - ZoneID string `json:"zoneid,omitempty"` -} - -func (*ListVolumes) name() string { - return "listVolumes" -} - -func (*ListVolumes) response() interface{} { - return new(ListVolumesResponse) + PodID *UUID `json:"podid,omitempty" doc:"the pod id the disk volume belongs to"` + StorageID *UUID `json:"storageid,omitempty" doc:"the ID of the storage pool, available to ROOT admin only"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List resources by tags (key/value pairs)"` + Type string `json:"type,omitempty" doc:"the type of disk volume"` + VirtualMachineID *UUID `json:"virtualmachineid,omitempty" doc:"the ID of the virtual machine"` + ZoneID *UUID `json:"zoneid,omitempty" doc:"the ID of the availability zone"` + _ bool `name:"listVolumes" description:"Lists all volumes."` } // ListVolumesResponse represents a list of volumes @@ -100,22 +125,30 @@ type ListVolumesResponse struct { Volume []Volume `json:"volume"` } -// GetRootVolumeForVirtualMachine returns the root volume of a VM -// -// Deprecated: helper function shouldn't be used -func (exo *Client) GetRootVolumeForVirtualMachine(virtualMachineID string) (*Volume, error) { - resp, err := exo.Request(&ListVolumes{ - VirtualMachineID: virtualMachineID, - Type: "ROOT", - }) - if err != nil { - return nil, err - } - - r := resp.(*ListVolumesResponse) - if r.Count != 1 { - return nil, fmt.Errorf("Expected exactly one volume for %v, got %d", virtualMachineID, r.Count) - } - - return &(r.Volume[0]), nil +func (ListVolumes) response() interface{} { + return new(ListVolumesResponse) +} + +// SetPage sets the current page +func (ls *ListVolumes) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListVolumes) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListVolumes) each(resp interface{}, callback IterateItemFunc) { + volumes, ok := resp.(*ListVolumesResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListVolumesResponse expected, got %T", resp)) + return + } + + for i := range volumes.Volume { + if !callback(&volumes.Volume[i], nil) { + break + } + } } diff --git a/vendor/github.com/exoscale/egoscale/zones.go b/vendor/github.com/exoscale/egoscale/zones.go index 228a7e369..90d801574 100644 --- a/vendor/github.com/exoscale/egoscale/zones.go +++ b/vendor/github.com/exoscale/egoscale/zones.go @@ -1,56 +1,62 @@ package egoscale -import "net" +import ( + "fmt" + "net" +) // Zone represents a data center +// +// TODO: represent correctly the capacity field. type Zone struct { - ID string `json:"id"` - AllocationState string `json:"allocationstate,omitempty"` - Capacity string `json:"capacity,omitempty"` - Description string `json:"description,omitempty"` - DhcpProvider string `json:"dhcpprovider,omitempty"` - DisplayText string `json:"displaytext,omitempty"` - DNS1 net.IP `json:"dns1,omitempty"` - DNS2 net.IP `json:"dns2,omitempty"` - Domain string `json:"domain,omitempty"` - DomainID string `json:"domainid,omitempty"` - DomainName string `json:"domainname,omitempty"` - GuestCidrAddress string `json:"guestcidraddress,omitempty"` - InternalDNS1 net.IP `json:"internaldns1,omitempty"` - InternalDNS2 net.IP `json:"internaldns2,omitempty"` - IP6DNS1 net.IP `json:"ip6dns1,omitempty"` - IP6DNS2 net.IP `json:"ip6dns2,omitempty"` - LocalStorageEnabled bool `json:"localstorageenabled,omitempty"` - Name string `json:"name,omitempty"` - NetworkType string `json:"networktype,omitempty"` - ResourceDetails map[string]string `json:"resourcedetails,omitempty"` - SecurityGroupsEnabled bool `json:"securitygroupsenabled,omitempty"` - Vlan string `json:"vlan,omitempty"` - ZoneToken string `json:"zonetoken,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` + AllocationState string `json:"allocationstate,omitempty" doc:"the allocation state of the cluster"` + Description string `json:"description,omitempty" doc:"Zone description"` + DhcpProvider string `json:"dhcpprovider,omitempty" doc:"the dhcp Provider for the Zone"` + DisplayText string `json:"displaytext,omitempty" doc:"the display text of the zone"` + DNS1 net.IP `json:"dns1,omitempty" doc:"the first DNS for the Zone"` + DNS2 net.IP `json:"dns2,omitempty" doc:"the second DNS for the Zone"` + Domain string `json:"domain,omitempty" doc:"Network domain name for the networks in the zone"` + DomainID *UUID `json:"domainid,omitempty" doc:"the UUID of the containing domain, null for public zones"` + DomainName string `json:"domainname,omitempty" doc:"the name of the containing domain, null for public zones"` + GuestCIDRAddress *CIDR `json:"guestcidraddress,omitempty" doc:"the guest CIDR address for the Zone"` + ID *UUID `json:"id,omitempty" doc:"Zone id"` + InternalDNS1 net.IP `json:"internaldns1,omitempty" doc:"the first internal DNS for the Zone"` + InternalDNS2 net.IP `json:"internaldns2,omitempty" doc:"the second internal DNS for the Zone"` + IP6DNS1 net.IP `json:"ip6dns1,omitempty" doc:"the first IPv6 DNS for the Zone"` + IP6DNS2 net.IP `json:"ip6dns2,omitempty" doc:"the second IPv6 DNS for the Zone"` + LocalStorageEnabled *bool `json:"localstorageenabled,omitempty" doc:"true if local storage offering enabled, false otherwise"` + Name string `json:"name,omitempty" doc:"Zone name"` + NetworkType string `json:"networktype,omitempty" doc:"the network type of the zone; can be Basic or Advanced"` + ResourceDetails map[string]string `json:"resourcedetails,omitempty" doc:"Meta data associated with the zone (key/value pairs)"` + SecurityGroupsEnabled *bool `json:"securitygroupsenabled,omitempty" doc:"true if security groups support is enabled, false otherwise"` + Tags []ResourceTag `json:"tags,omitempty" doc:"the list of resource tags associated with zone."` + Vlan string `json:"vlan,omitempty" doc:"the vlan range of the zone"` + ZoneToken string `json:"zonetoken,omitempty" doc:"Zone Token"` +} + +// ListRequest builds the ListZones request +func (zone Zone) ListRequest() (ListCommand, error) { + req := &ListZones{ + DomainID: zone.DomainID, + ID: zone.ID, + Name: zone.Name, + } + + return req, nil } // ListZones represents a query for zones -// -// CloudStack API: https://cloudstack.apache.org/api/apidocs-4.10/apis/listZones.html type ListZones struct { - Available bool `json:"available,omitempty"` - DomainID string `json:"domainid,omitempty"` - ID string `json:"id,omitempty"` - Keyword string `json:"keyword,omitempty"` - Name string `json:"name,omitempty"` + Available *bool `json:"available,omitempty" doc:"true if you want to retrieve all available Zones. False if you only want to return the Zones from which you have at least one VM. Default is false."` + DomainID *UUID `json:"domainid,omitempty" doc:"the ID of the domain associated with the zone"` + ID *UUID `json:"id,omitempty" doc:"the ID of the zone"` + Keyword string `json:"keyword,omitempty" doc:"List by keyword"` + Name string `json:"name,omitempty" doc:"the name of the zone"` Page int `json:"page,omitempty"` PageSize int `json:"pagesize,omitempty"` - ShowCapacities bool `json:"showcapacities,omitempty"` - Tags []ResourceTag `json:"tags,omitempty"` -} - -func (*ListZones) name() string { - return "listZones" -} - -func (*ListZones) response() interface{} { - return new(ListZonesResponse) + ShowCapacities *bool `json:"showcapacities,omitempty" doc:"flag to display the capacity of the zones"` + Tags []ResourceTag `json:"tags,omitempty" doc:"List zones by resource tags (key/value pairs)"` + _ bool `name:"listZones" description:"Lists zones"` } // ListZonesResponse represents a list of zones @@ -58,3 +64,31 @@ type ListZonesResponse struct { Count int `json:"count"` Zone []Zone `json:"zone"` } + +func (ListZones) response() interface{} { + return new(ListZonesResponse) +} + +// SetPage sets the current page +func (ls *ListZones) SetPage(page int) { + ls.Page = page +} + +// SetPageSize sets the page size +func (ls *ListZones) SetPageSize(pageSize int) { + ls.PageSize = pageSize +} + +func (ListZones) each(resp interface{}, callback IterateItemFunc) { + zones, ok := resp.(*ListZonesResponse) + if !ok { + callback(nil, fmt.Errorf("wrong type. ListZonesResponse was expected, got %T", resp)) + return + } + + for i := range zones.Zone { + if !callback(&zones.Zone[i], nil) { + break + } + } +} diff --git a/vendor/github.com/iij/doapi/api.go b/vendor/github.com/iij/doapi/api.go new file mode 100644 index 000000000..796e06680 --- /dev/null +++ b/vendor/github.com/iij/doapi/api.go @@ -0,0 +1,170 @@ +// Package doapi : DO APIクライアントモジュール +package doapi + +import ( + "bytes" + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "hash" + "io" + "net/http" + "net/url" + "sort" + "strings" + "time" + + log "github.com/sirupsen/logrus" +) + +const ( + HmacSHA1 = "HmacSHA1" + HmacSHA256 = "HmacSHA256" + SignatureVersion2 = "2" + APIVersion = "20140601" + EndpointJSON = "https://do.api.iij.jp/" + // EndpointJSON = "http://localhost:9999/" + TimeLayout = "2006-01-02T15:04:05Z" + PostContentType = "application/json" +) + +// API の呼び出し先に関連する構造 +type API struct { + AccessKey string + SecretKey string + Endpoint string + SignMethod string + Expires time.Duration + Insecure bool +} + +// NewAPI API構造体のコンストラクタ +func NewAPI(accesskey, secretkey string) *API { + dur, _ := time.ParseDuration("1h") + return &API{AccessKey: accesskey, + SecretKey: secretkey, + Endpoint: EndpointJSON, + SignMethod: HmacSHA256, + Expires: dur, + } +} + +func convert1(r byte) string { + passchar := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_~.-" + if strings.ContainsRune(passchar, rune(r)) { + return string(r) + } + return fmt.Sprintf("%%%02X", r) +} + +// CustomEscape escape string +func CustomEscape(v string) string { + res := "" + for _, c := range []byte(v) { + res += convert1(c) + } + return res +} + +// String2Sign get string to calculate signature +func String2Sign(method string, header http.Header, param url.URL) string { + var keys []string + ctflag := false + for k := range header { + hdr := strings.ToLower(k) + if strings.HasPrefix(hdr, "x-iijapi-") { + keys = append(keys, hdr) + } else if hdr == "content-type" || hdr == "content-md5" { + keys = append(keys, hdr) + ctflag = true + } + } + sort.Strings(keys) + var target []string + target = append(target, method) + target = append(target, "") + if !ctflag { + target = append(target, "") + } + for _, k := range keys { + if k == "content-type" || k == "content-md5" { + target = append(target, header.Get(k)) + } else { + target = append(target, k+":"+header.Get(k)) + } + } + target = append(target, param.Path) + return strings.Join(target, "\n") +} + +// Sign get signature string +func (a API) Sign(method string, header http.Header, param url.URL, signmethod string) http.Header { + header.Set("x-iijapi-Expire", time.Now().Add(a.Expires).UTC().Format(TimeLayout)) + header.Set("x-iijapi-SignatureMethod", signmethod) + header.Set("x-iijapi-SignatureVersion", SignatureVersion2) + tgtstr := String2Sign(method, header, param) + var hfn func() hash.Hash + switch signmethod { + case HmacSHA1: + hfn = sha1.New + case HmacSHA256: + hfn = sha256.New + } + mac := hmac.New(hfn, []byte(a.SecretKey)) + io.WriteString(mac, tgtstr) + macstr := mac.Sum(nil) + header.Set("Authorization", "IIJAPI "+a.AccessKey+":"+base64.StdEncoding.EncodeToString(macstr)) + return header +} + +// Get : low-level Get +func (a API) Get(param url.URL) (resp *http.Response, err error) { + return a.PostSome("GET", param, nil) +} + +// PostSome : low-level Call +func (a API) PostSome(method string, param url.URL, body interface{}) (resp *http.Response, err error) { + cl := http.Client{} + if a.Insecure { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + cl.Transport = tr + } + log.Debug("param", param) + var buf *bytes.Buffer + if body != nil { + var bufb []byte + bufb, err = json.Marshal(body) + if err != nil { + return nil, err + } + if len(bufb) > 2 { + log.Debug("call with body", method, string(bufb)) + buf = bytes.NewBuffer(bufb) + } else { + // string(bufb)=="{}" + log.Debug("call without body(empty)", method) + buf = bytes.NewBufferString("") + body = nil + } + } else { + log.Debug("call without body(nil)", method) + buf = bytes.NewBufferString("") + } + req, err := http.NewRequest(method, param.String(), buf) + if err != nil { + return nil, err + } + if body != nil { + req.Header.Add("content-type", PostContentType) + } + req.Header = a.Sign(method, req.Header, param, HmacSHA256) + log.Debug("sign", req.Header) + resp, err = cl.Do(req) + return +} diff --git a/vendor/github.com/iij/doapi/parse.go b/vendor/github.com/iij/doapi/parse.go new file mode 100644 index 000000000..61401e33e --- /dev/null +++ b/vendor/github.com/iij/doapi/parse.go @@ -0,0 +1,267 @@ +package doapi + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "strings" + "text/template" + + log "github.com/sirupsen/logrus" + + "github.com/iij/doapi/protocol" +) + +func execTemplate(name string, tmplstr string, arg interface{}) string { + tmpl, err := template.New(name).Parse(tmplstr) + if err != nil { + panic(err) + } + buf := new(bytes.Buffer) + if err = tmpl.Execute(buf, arg); err != nil { + panic(err) + } + return buf.String() +} + +// GetPath APIのURIのパス部分を求める +func GetPath(arg protocol.CommonArg) string { + return "/r/" + APIVersion + execTemplate(arg.APIName(), arg.URI(), arg) +} + +// GetParam APIのクエリストリング部分を求める +func GetParam(api API, arg protocol.CommonArg) *url.URL { + param, err := url.Parse(api.Endpoint) + if err != nil { + panic(err) + } + param.Path = GetPath(arg) + q := param.Query() + _, toQuery, _ := ArgumentListType(arg) + typs := reflect.TypeOf(arg) + vals := reflect.ValueOf(arg) + for _, key := range toQuery { + if _, flag := typs.FieldByName(key); !flag { + log.Info("no field", key) + continue + } + if val := vals.FieldByName(key).String(); len(val) != 0 { + q.Set(key, val) + } + } + param.RawQuery = q.Encode() + return param +} + +// GetBody API呼び出しのリクエストボディ(JSON文字列)を求める +func GetBody(arg protocol.CommonArg) string { + b, err := json.Marshal(arg) + if err != nil { + panic(err) + } + return string(b) +} + +// Call API呼び出しを実行し、レスポンスを得る +func Call(api API, arg protocol.CommonArg, resp interface{}) (err error) { + if err = Validate(arg); err != nil { + return + } + var res *http.Response + param := GetParam(api, arg) + log.Debug("method", arg.Method(), "param", param, "arg", arg) + if res, err = api.PostSome(arg.Method(), *param, arg); err != nil { + log.Error("PostSome", err) + return + } + log.Debug("res", res, "err", err) + var b []byte + if b, err = ioutil.ReadAll(res.Body); err != nil { + log.Error("ioutil.ReadAll", err) + return + } + log.Debug("data", string(b)) + if err = json.Unmarshal(b, &resp); err != nil { + log.Error("json.Unmarshal", err) + return + } + var cresp = protocol.CommonResponse{} + err = json.Unmarshal(b, &cresp) + if err == nil && cresp.ErrorResponse.ErrorType != "" { + return fmt.Errorf("%s: %s", cresp.ErrorResponse.ErrorType, cresp.ErrorResponse.ErrorMessage) + } + return +} + +// Validate APIの必須引数が入っているかどうかをチェック +func Validate(arg protocol.CommonArg) error { + var res []string + typs := reflect.TypeOf(arg) + vals := reflect.ValueOf(arg) + for i := 0; i < typs.NumField(); i++ { + fld := typs.Field(i) + tagstrJSON := fld.Tag.Get("json") + tagstrP2 := fld.Tag.Get("p2pub") + if strings.Contains(tagstrJSON, "omitempty") { + // optional argument + continue + } + if strings.Contains(tagstrP2, "query") { + // optional argument + continue + } + if val := vals.Field(i).String(); len(val) == 0 { + res = append(res, fld.Name) + } + } + if len(res) != 0 { + return fmt.Errorf("missing arguments: %+v", res) + } + return nil +} + +// ArgumentList API引数のリストを求める。必須とオプションに分類 +func ArgumentList(arg protocol.CommonArg) (required, optional []string) { + typs := reflect.TypeOf(arg) + for i := 0; i < typs.NumField(); i++ { + fld := typs.Field(i) + tagstrJSON := fld.Tag.Get("json") + tagstrP2 := fld.Tag.Get("p2pub") + if strings.Contains(tagstrJSON, "omitempty") || strings.Contains(tagstrP2, "query") { + optional = append(optional, fld.Name) + } else { + required = append(required, fld.Name) + } + } + return +} + +// ArgumentListType API引数のリストを求める。URI埋め込み、クエリストリング、JSONに分類 +func ArgumentListType(arg protocol.CommonArg) (toURI, toQuery, toJSON []string) { + typs := reflect.TypeOf(arg) + for i := 0; i < typs.NumField(); i++ { + fld := typs.Field(i) + tagstrJSON := fld.Tag.Get("json") + tagstrP2 := fld.Tag.Get("p2pub") + if strings.Contains(tagstrP2, "query") { + toQuery = append(toQuery, fld.Name) + } else if strings.HasPrefix(tagstrJSON, "-") { + toURI = append(toURI, fld.Name) + } else { + toJSON = append(toJSON, fld.Name) + } + } + return +} + +func argumentAltKeyList(arg protocol.CommonArg) (toAltQuery, toAltJSON map[string]string) { + toAltQuery = make(map[string]string) + toAltJSON = make(map[string]string) + typs := reflect.TypeOf(arg) + for i := 0; i < typs.NumField(); i++ { + fld := typs.Field(i) + tagstrJSON := fld.Tag.Get("json") + tagstrP2 := fld.Tag.Get("p2pub") + altKey := strings.Split(tagstrJSON, ",")[0] + if altKey == "" || altKey == "-" { + continue + } + if strings.Contains(tagstrP2, "query") { + toAltQuery[fld.Name] = altKey + } else { + toAltJSON[fld.Name] = altKey + } + } + return +} + +// ValidateMap APIの必須引数が入っているかどうかをチェック +func ValidateMap(name string, data map[string]string) error { + var res []string + typs := protocol.TypeMap[name] + for i := 0; i < typs.NumField(); i++ { + fld := typs.Field(i) + tagstrJSON := fld.Tag.Get("json") + tagstrP2 := fld.Tag.Get("p2pub") + if strings.Contains(tagstrJSON, "omitempty") { + // optional argument + continue + } + if strings.Contains(tagstrP2, "query") { + // optional argument + continue + } + if data[fld.Name] == "" { + res = append(res, fld.Name) + } + } + if len(res) != 0 { + return fmt.Errorf("missing arguments: %+v", res) + } + return nil +} + +// CallWithMap API呼び出しを実行する。引数と戻り値が構造体ではなくmap +func CallWithMap(api API, name string, data map[string]string, resp map[string]interface{}) error { + if err := ValidateMap(name, data); err != nil { + return err + } + argt := protocol.TypeMap[name] + arg := reflect.Zero(argt).Interface().(protocol.CommonArg) + var res *http.Response + param, err := url.Parse(api.Endpoint) + if err != nil { + panic(err) + } + param.Path = "/r/" + APIVersion + execTemplate(name, arg.URI(), data) + q := param.Query() + _, toQuery, toJSON := ArgumentListType(arg) + log.Debug("query", toQuery, "json", toJSON, "path", param.Path) + toAltQuery, toAltJSON := argumentAltKeyList(arg) + log.Debug("query altkey - ", toAltQuery, " json altkey - ", toAltJSON) + var jsonmap = map[string]interface{}{} + for _, v := range toJSON { + if len(data[v]) != 0 { + if altkey, ok := toAltJSON[v]; ok { + jsonmap[altkey] = data[v] + } else { + jsonmap[v] = data[v] + } + } + } + for _, v := range toQuery { + if len(data[v]) != 0 { + if altkey, ok := toAltQuery[v]; ok { + q.Set(altkey, data[v]) + } else { + q.Set(v, data[v]) + } + } + } + param.RawQuery = q.Encode() + if res, err = api.PostSome(arg.Method(), *param, jsonmap); err != nil { + log.Error("PostSome", err) + return err + } + log.Debug("res", res) + var b []byte + if b, err = ioutil.ReadAll(res.Body); err != nil { + log.Error("ioutil.ReadAll", err) + return err + } + log.Debug("data", string(b)) + if err = json.Unmarshal(b, &resp); err != nil { + log.Error("json.Unmarshal", err) + return err + } + if val, ok := resp["ErrorResponse"]; ok { + errstr := execTemplate("ErrorResponse", "{{.ErrorType}}: {{.ErrorMessage}}", val) + return errors.New(errstr) + } + return nil +} diff --git a/vendor/github.com/iij/doapi/protocol/Commit.go b/vendor/github.com/iij/doapi/protocol/Commit.go new file mode 100644 index 000000000..2b23bb856 --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/Commit.go @@ -0,0 +1,44 @@ +package protocol + +import ( + "reflect" +) + +// Commit +type Commit struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) +} + +// URI /{{.DoServiceCode}}/commit.json +func (t Commit) URI() string { + return "/{{.DoServiceCode}}/commit.json" +} + +// APIName Commit +func (t Commit) APIName() string { + return "Commit" +} + +// Method PUT +func (t Commit) Method() string { + return "PUT" +} + +// http://manual.iij.jp/dns/doapi/754632.html +func (t Commit) Document() string { + return "http://manual.iij.jp/dns/doapi/754632.html" +} + +// JPName PUT Commit +func (t Commit) JPName() string { + return "PUT commit" +} +func init() { + APIlist = append(APIlist, Commit{}) + TypeMap["Commit"] = reflect.TypeOf(Commit{}) +} + +// CommitResponse PUT Commitのレスポンス +type CommitResponse struct { + *CommonResponse +} diff --git a/vendor/github.com/iij/doapi/protocol/RecordAdd.go b/vendor/github.com/iij/doapi/protocol/RecordAdd.go new file mode 100644 index 000000000..0f165b098 --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/RecordAdd.go @@ -0,0 +1,51 @@ +package protocol + +import ( + "reflect" +) + +// RecordAdd POST record (同期) +// http://manual.iij.jp/dns/doapi/754517.html +type RecordAdd struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) + ZoneName string `json:"-"` // Zone Name + Owner string // owner of record + TTL string // TTL of record + RecordType string // type of record + RData string // data of record +} + +// URI /:GisServiceCode/fw-lbs/:IflServiceCode/filters/:IpVersion/:Direction.json +func (t RecordAdd) URI() string { + return "/{{.DoServiceCode}}/{{.ZoneName}}/record.json" +} + +// APIName RecordAdd +func (t RecordAdd) APIName() string { + return "RecordAdd" +} + +// Method POST +func (t RecordAdd) Method() string { + return "POST" +} + +// http://manual.iij.jp/dns/doapi/754517.html +func (t RecordAdd) Document() string { + return "http://manual.iij.jp/dns/doapi/754517.html" +} + +// JPName POST record +func (t RecordAdd) JPName() string { + return "POST record" +} +func init() { + APIlist = append(APIlist, RecordAdd{}) + TypeMap["RecordAdd"] = reflect.TypeOf(RecordAdd{}) +} + +// RecordAddResponse POST recordのレスポンス +type RecordAddResponse struct { + *CommonResponse + Record ResourceRecord +} diff --git a/vendor/github.com/iij/doapi/protocol/RecordDelete.go b/vendor/github.com/iij/doapi/protocol/RecordDelete.go new file mode 100644 index 000000000..5d087df3f --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/RecordDelete.go @@ -0,0 +1,47 @@ +package protocol + +import ( + "reflect" +) + +// RecordDelete DELETE record +// http://manual.iij.jp/dns/doapi/754525.html +type RecordDelete struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) + ZoneName string `json:"-"` // Zone Name + RecordID string `json:"-"` // Record ID +} + +// URI /{{.DoServiceCode}}/{{.ZoneName}}/record/{{.RecordID}}.json +func (t RecordDelete) URI() string { + return "/{{.DoServiceCode}}/{{.ZoneName}}/record/{{.RecordID}}.json" +} + +// APIName RecordDelete +func (t RecordDelete) APIName() string { + return "RecordDelete" +} + +// Method DELETE +func (t RecordDelete) Method() string { + return "DELETE" +} + +// http://manual.iij.jp/dns/doapi/754525.html +func (t RecordDelete) Document() string { + return "http://manual.iij.jp/dns/doapi/754525.html" +} + +// JPName DELETE record +func (t RecordDelete) JPName() string { + return "DELETE record" +} +func init() { + APIlist = append(APIlist, RecordDelete{}) + TypeMap["RecordDelete"] = reflect.TypeOf(RecordDelete{}) +} + +// RecordDeleteResponse DELETE recordのレスポンス +type RecordDeleteResponse struct { + *CommonResponse +} diff --git a/vendor/github.com/iij/doapi/protocol/RecordGet.go b/vendor/github.com/iij/doapi/protocol/RecordGet.go new file mode 100644 index 000000000..051bb6595 --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/RecordGet.go @@ -0,0 +1,48 @@ +package protocol + +import ( + "reflect" +) + +// GET records +// http://manual.iij.jp/dns/doapi/754619.html +type RecordGet struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) + ZoneName string `json:"-"` // ゾーン名 + RecordID string `json:"-"` // +} + +// URI /{{.DoServiceCode}}/{{.ZoneName}}/record/{{.RecordID}}.json +func (t RecordGet) URI() string { + return "/{{.DoServiceCode}}/{{.ZoneName}}/record/{{.RecordID}}.json" +} + +// APIName RecordGet +func (t RecordGet) APIName() string { + return "RecordGet" +} + +// Method GET +func (t RecordGet) Method() string { + return "GET" +} + +// http://manual.iij.jp/dns/doapi/754503.html +func (t RecordGet) Document() string { + return "http://manual.iij.jp/dns/doapi/754503.html" +} + +// JPName GET record +func (t RecordGet) JPName() string { + return "GET record" +} +func init() { + APIlist = append(APIlist, RecordGet{}) + TypeMap["RecordGet"] = reflect.TypeOf(RecordGet{}) +} + +// RecordGetResponse フィルタリングルール情報取得のレスポンス +type RecordGetResponse struct { + *CommonResponse + Record ResourceRecord +} diff --git a/vendor/github.com/iij/doapi/protocol/RecordListGet.go b/vendor/github.com/iij/doapi/protocol/RecordListGet.go new file mode 100644 index 000000000..3e4b9d637 --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/RecordListGet.go @@ -0,0 +1,47 @@ +package protocol + +import ( + "reflect" +) + +// GET records +type RecordListGet struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) + ZoneName string `json:"-"` // ゾーン名 +} + +// URI /{{.DoServiceCode}}/{{.ZoneName}}/records/DETAIL.json +func (t RecordListGet) URI() string { + return "/{{.DoServiceCode}}/{{.ZoneName}}/records/DETAIL.json" +} + +// APIName RecordListGet +func (t RecordListGet) APIName() string { + return "RecordListGet" +} + +// Method GET +func (t RecordListGet) Method() string { + return "GET" +} + +// http://manual.iij.jp/dns/doapi/754619.html +func (t RecordListGet) Document() string { + return "http://manual.iij.jp/dns/doapi/754619.html" +} + +// JPName GET records +func (t RecordListGet) JPName() string { + return "GET records" +} +func init() { + APIlist = append(APIlist, RecordListGet{}) + TypeMap["RecordListGet"] = reflect.TypeOf(RecordListGet{}) +} + +// RecordListGetResponse GET recordsのレスポンス +type RecordListGetResponse struct { + *CommonResponse + RecordList []ResourceRecord + StaticRecordList []ResourceRecord +} diff --git a/vendor/github.com/iij/doapi/protocol/Reset.go b/vendor/github.com/iij/doapi/protocol/Reset.go new file mode 100644 index 000000000..0a26056fb --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/Reset.go @@ -0,0 +1,45 @@ +package protocol + +import ( + "reflect" +) + +// Reset PUT reset (同期) +type Reset struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) + ZoneName string `json:"-"` // Zone name +} + +// URI /{{.DoServiceCode}}/{{.ZoneName}}/reset.json +func (t Reset) URI() string { + return "/{{.DoServiceCode}}/{{.ZoneName}}/reset.json" +} + +// APIName Reset +func (t Reset) APIName() string { + return "Reset" +} + +// Method PUT +func (t Reset) Method() string { + return "PUT" +} + +// http://manual.iij.jp/dns/doapi/754610.html +func (t Reset) Document() string { + return "http://manual.iij.jp/dns/doapi/754610.html" +} + +// JPName PUT reset +func (t Reset) JPName() string { + return "PUT Reset" +} +func init() { + APIlist = append(APIlist, Reset{}) + TypeMap["Reset"] = reflect.TypeOf(Reset{}) +} + +// ResetResponse PUT resetのレスポンス +type ResetResponse struct { + *CommonResponse +} diff --git a/vendor/github.com/iij/doapi/protocol/ZoneListGet.go b/vendor/github.com/iij/doapi/protocol/ZoneListGet.go new file mode 100644 index 000000000..20258a4db --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/ZoneListGet.go @@ -0,0 +1,45 @@ +package protocol + +import ( + "reflect" +) + +// GET zones +type ZoneListGet struct { + DoServiceCode string `json:"-"` // DO契約のサービスコード(do########) +} + +// URI /{{.DoServiceCode}}/zones.json +func (t ZoneListGet) URI() string { + return "/{{.DoServiceCode}}/zones.json" +} + +// APIName ZoneListGet +func (t ZoneListGet) APIName() string { + return "ZoneListGet" +} + +// Method GET +func (t ZoneListGet) Method() string { + return "GET" +} + +// http://manual.iij.jp/dns/doapi/754466.html +func (t ZoneListGet) Document() string { + return "http://manual.iij.jp/dns/doapi/754466.html" +} + +// JPName GET zones +func (t ZoneListGet) JPName() string { + return "GET zones" +} +func init() { + APIlist = append(APIlist, ZoneListGet{}) + TypeMap["ZoneListGet"] = reflect.TypeOf(ZoneListGet{}) +} + +// ZoneListGetResponse GET zonesのレスポンス +type ZoneListGetResponse struct { + *CommonResponse + ZoneList []string +} diff --git a/vendor/github.com/iij/doapi/protocol/common.go b/vendor/github.com/iij/doapi/protocol/common.go new file mode 100644 index 000000000..77f7a2e5c --- /dev/null +++ b/vendor/github.com/iij/doapi/protocol/common.go @@ -0,0 +1,53 @@ +package protocol + +//go:generate python doc2struct.py + +import ( + "fmt" + "reflect" +) + +type CommonArg interface { + APIName() string + Method() string + URI() string + Document() string + JPName() string +} + +type CommonResponse struct { + RequestId string + ErrorResponse struct { + RequestId string + ErrorType string + ErrorMessage string + } +} + +var APIlist []CommonArg + +var TypeMap = map[string]reflect.Type{} + +type ResourceRecord struct { + Id string `json:",omitempty"` + Status string + Owner string + TTL string + RecordType string + RData string +} + +func (r *ResourceRecord) String() string { + return fmt.Sprintf("%s %s IN %s %s", r.Owner, r.TTL, r.RecordType, r.RData) +} + +func (r *ResourceRecord) FQDN(zone string) string { + return fmt.Sprintf("%s.%s %s IN %s %s", r.Owner, zone, r.TTL, r.RecordType, r.RData) +} + +const ( + UNCAHNGED = "UNCHANGED" + ADDING = "ADDING" + DELETING = "DELETING" + DELETED = "DELETED" +) diff --git a/vendor/github.com/satori/go.uuid/LICENSE b/vendor/github.com/satori/go.uuid/LICENSE index 488357b8a..926d54987 100644 --- a/vendor/github.com/satori/go.uuid/LICENSE +++ b/vendor/github.com/satori/go.uuid/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2013-2016 by Maxim Bublis +Copyright (C) 2013-2018 by Maxim Bublis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/vendor/github.com/satori/go.uuid/codec.go b/vendor/github.com/satori/go.uuid/codec.go new file mode 100644 index 000000000..656892c53 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/codec.go @@ -0,0 +1,206 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "bytes" + "encoding/hex" + "fmt" +) + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromBytesOrNil returns UUID converted from raw byte slice input. +// Same behavior as FromBytes, but returns a Nil UUID on error. +func FromBytesOrNil(input []byte) UUID { + uuid, err := FromBytes(input) + if err != nil { + return Nil + } + return uuid +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// FromStringOrNil returns UUID parsed from string input. +// Same behavior as FromString, but returns a Nil UUID on error. +func FromStringOrNil(input string) UUID { + uuid, err := FromString(input) + if err != nil { + return Nil + } + return uuid +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +// "6ba7b8109dad11d180b400c04fd430c8" +// ABNF for supported UUID text representation follows: +// uuid := canonical | hashlike | braced | urn +// plain := canonical | hashlike +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct +// hashlike := 12hexoct +// braced := '{' plain '}' +// urn := URN ':' UUID-NID ':' plain +// URN := 'urn' +// UUID-NID := 'uuid' +// 12hexoct := 6hexoct 6hexoct +// 6hexoct := 4hexoct 2hexoct +// 4hexoct := 2hexoct 2hexoct +// 2hexoct := hexoct hexoct +// hexoct := hexdig hexdig +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' +func (u *UUID) UnmarshalText(text []byte) (err error) { + switch len(text) { + case 32: + return u.decodeHashLike(text) + case 36: + return u.decodeCanonical(text) + case 38: + return u.decodeBraced(text) + case 41: + fallthrough + case 45: + return u.decodeURN(text) + default: + return fmt.Errorf("uuid: incorrect UUID length: %s", text) + } +} + +// decodeCanonical decodes UUID string in format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". +func (u *UUID) decodeCanonical(t []byte) (err error) { + if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + src := t[:] + dst := u[:] + + for i, byteGroup := range byteGroups { + if i > 0 { + src = src[1:] // skip dash + } + _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup]) + if err != nil { + return + } + src = src[byteGroup:] + dst = dst[byteGroup/2:] + } + + return +} + +// decodeHashLike decodes UUID string in format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeHashLike(t []byte) (err error) { + src := t[:] + dst := u[:] + + if _, err = hex.Decode(dst, src); err != nil { + return err + } + return +} + +// decodeBraced decodes UUID string in format +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format +// "{6ba7b8109dad11d180b400c04fd430c8}". +func (u *UUID) decodeBraced(t []byte) (err error) { + l := len(t) + + if t[0] != '{' || t[l-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + return u.decodePlain(t[1 : l-1]) +} + +// decodeURN decodes UUID string in format +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeURN(t []byte) (err error) { + total := len(t) + + urn_uuid_prefix := t[:9] + + if !bytes.Equal(urn_uuid_prefix, urnPrefix) { + return fmt.Errorf("uuid: incorrect UUID format: %s", t) + } + + return u.decodePlain(t[9:total]) +} + +// decodePlain decodes UUID string in canonical format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodePlain(t []byte) (err error) { + switch len(t) { + case 32: + return u.decodeHashLike(t) + case 36: + return u.decodeCanonical(t) + default: + return fmt.Errorf("uuid: incorrrect UUID length: %s", t) + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != Size { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} diff --git a/vendor/github.com/satori/go.uuid/generator.go b/vendor/github.com/satori/go.uuid/generator.go new file mode 100644 index 000000000..3f2f1da2d --- /dev/null +++ b/vendor/github.com/satori/go.uuid/generator.go @@ -0,0 +1,239 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "hash" + "net" + "os" + "sync" + "time" +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +var ( + global = newDefaultGenerator() + + epochFunc = unixTimeFunc + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() UUID { + return global.NewV1() +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) UUID { + return global.NewV2(domain) +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + return global.NewV3(ns, name) +} + +// NewV4 returns random generated UUID. +func NewV4() UUID { + return global.NewV4() +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + return global.NewV5(ns, name) +} + +// Generator provides interface for generating UUIDs. +type Generator interface { + NewV1() UUID + NewV2(domain byte) UUID + NewV3(ns UUID, name string) UUID + NewV4() UUID + NewV5(ns UUID, name string) UUID +} + +// Default generator implementation. +type generator struct { + storageOnce sync.Once + storageMutex sync.Mutex + + lastTime uint64 + clockSequence uint16 + hardwareAddr [6]byte +} + +func newDefaultGenerator() Generator { + return &generator{} +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func (g *generator) NewV1() UUID { + u := UUID{} + + timeNow, clockSeq, hardwareAddr := g.getStorage() + + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + copy(u[10:], hardwareAddr) + + u.SetVersion(V1) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func (g *generator) NewV2(domain byte) UUID { + u := UUID{} + + timeNow, clockSeq, hardwareAddr := g.getStorage() + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[0:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[0:], posixGID) + } + + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + u[9] = domain + + copy(u[10:], hardwareAddr) + + u.SetVersion(V2) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func (g *generator) NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(V3) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV4 returns random generated UUID. +func (g *generator) NewV4() UUID { + u := UUID{} + g.safeRandom(u[:]) + u.SetVersion(V4) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func (g *generator) NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(V5) + u.SetVariant(VariantRFC4122) + + return u +} + +func (g *generator) initStorage() { + g.initClockSequence() + g.initHardwareAddr() +} + +func (g *generator) initClockSequence() { + buf := make([]byte, 2) + g.safeRandom(buf) + g.clockSequence = binary.BigEndian.Uint16(buf) +} + +func (g *generator) initHardwareAddr() { + interfaces, err := net.Interfaces() + if err == nil { + for _, iface := range interfaces { + if len(iface.HardwareAddr) >= 6 { + copy(g.hardwareAddr[:], iface.HardwareAddr) + return + } + } + } + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence + g.safeRandom(g.hardwareAddr[:]) + + // Set multicast bit as recommended in RFC 4122 + g.hardwareAddr[0] |= 0x01 +} + +func (g *generator) safeRandom(dest []byte) { + if _, err := rand.Read(dest); err != nil { + panic(err) + } +} + +// Returns UUID v1/v2 storage state. +// Returns epoch timestamp, clock sequence, and hardware address. +func (g *generator) getStorage() (uint64, uint16, []byte) { + g.storageOnce.Do(g.initStorage) + + g.storageMutex.Lock() + defer g.storageMutex.Unlock() + + timeNow := epochFunc() + // Clock changed backwards since last UUID generation. + // Should increase clock sequence. + if timeNow <= g.lastTime { + g.clockSequence++ + } + g.lastTime = timeNow + + return timeNow, g.clockSequence, g.hardwareAddr[:] +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +// This is default epoch calculation function. +func unixTimeFunc() uint64 { + return epochStart + uint64(time.Now().UnixNano()/100) +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} diff --git a/vendor/github.com/satori/go.uuid/sql.go b/vendor/github.com/satori/go.uuid/sql.go new file mode 100644 index 000000000..56759d390 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/sql.go @@ -0,0 +1,78 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Value implements the driver.Valuer interface. +func (u UUID) Value() (driver.Value, error) { + return u.String(), nil +} + +// Scan implements the sql.Scanner interface. +// A 16-byte slice is handled by UnmarshalBinary, while +// a longer byte slice or a string is handled by UnmarshalText. +func (u *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + if len(src) == Size { + return u.UnmarshalBinary(src) + } + return u.UnmarshalText(src) + + case string: + return u.UnmarshalText([]byte(src)) + } + + return fmt.Errorf("uuid: cannot convert %T to UUID", src) +} + +// NullUUID can be used with the standard sql package to represent a +// UUID value that can be NULL in the database +type NullUUID struct { + UUID UUID + Valid bool +} + +// Value implements the driver.Valuer interface. +func (u NullUUID) Value() (driver.Value, error) { + if !u.Valid { + return nil, nil + } + // Delegate to UUID Value function + return u.UUID.Value() +} + +// Scan implements the sql.Scanner interface. +func (u *NullUUID) Scan(src interface{}) error { + if src == nil { + u.UUID, u.Valid = Nil, false + return nil + } + + // Delegate to UUID Scan function + u.Valid = true + return u.UUID.Scan(src) +} diff --git a/vendor/github.com/satori/go.uuid/uuid.go b/vendor/github.com/satori/go.uuid/uuid.go index 9c7fbaa54..a2b8e2ca2 100644 --- a/vendor/github.com/satori/go.uuid/uuid.go +++ b/vendor/github.com/satori/go.uuid/uuid.go @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2015 by Maxim Bublis +// Copyright (C) 2013-2018 by Maxim Bublis // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -26,23 +26,29 @@ package uuid import ( "bytes" - "crypto/md5" - "crypto/rand" - "crypto/sha1" - "database/sql/driver" - "encoding/binary" "encoding/hex" - "fmt" - "hash" - "net" - "os" - "sync" - "time" +) + +// Size of a UUID in bytes. +const Size = 16 + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [Size]byte + +// UUID versions +const ( + _ byte = iota + V1 + V2 + V3 + V4 + V5 ) // UUID layout variants. const ( - VariantNCS = iota + VariantNCS byte = iota VariantRFC4122 VariantMicrosoft VariantFuture @@ -55,136 +61,48 @@ const ( DomainOrg ) -// Difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). -const epochStart = 122192928000000000 - -// Used in string method conversion -const dash byte = '-' - -// UUID v1/v2 storage. -var ( - storageMutex sync.Mutex - storageOnce sync.Once - epochFunc = unixTimeFunc - clockSequence uint16 - lastTime uint64 - hardwareAddr [6]byte - posixUID = uint32(os.Getuid()) - posixGID = uint32(os.Getgid()) -) - // String parse helpers. var ( urnPrefix = []byte("urn:uuid:") byteGroups = []int{8, 4, 4, 4, 12} ) -func initClockSequence() { - buf := make([]byte, 2) - safeRandom(buf) - clockSequence = binary.BigEndian.Uint16(buf) -} - -func initHardwareAddr() { - interfaces, err := net.Interfaces() - if err == nil { - for _, iface := range interfaces { - if len(iface.HardwareAddr) >= 6 { - copy(hardwareAddr[:], iface.HardwareAddr) - return - } - } - } - - // Initialize hardwareAddr randomly in case - // of real network interfaces absence - safeRandom(hardwareAddr[:]) - - // Set multicast bit as recommended in RFC 4122 - hardwareAddr[0] |= 0x01 -} - -func initStorage() { - initClockSequence() - initHardwareAddr() -} - -func safeRandom(dest []byte) { - if _, err := rand.Read(dest); err != nil { - panic(err) - } -} - -// Returns difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and current time. -// This is default epoch calculation function. -func unixTimeFunc() uint64 { - return epochStart + uint64(time.Now().UnixNano()/100) -} - -// UUID representation compliant with specification -// described in RFC 4122. -type UUID [16]byte - -// NullUUID can be used with the standard sql package to represent a -// UUID value that can be NULL in the database -type NullUUID struct { - UUID UUID - Valid bool -} - -// The nil UUID is special form of UUID that is specified to have all +// Nil is special form of UUID that is specified to have all // 128 bits set to zero. var Nil = UUID{} // Predefined namespace UUIDs. var ( - NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8") - NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8") - NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8") + NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) ) -// And returns result of binary AND of two UUIDs. -func And(u1 UUID, u2 UUID) UUID { - u := UUID{} - for i := 0; i < 16; i++ { - u[i] = u1[i] & u2[i] - } - return u -} - -// Or returns result of binary OR of two UUIDs. -func Or(u1 UUID, u2 UUID) UUID { - u := UUID{} - for i := 0; i < 16; i++ { - u[i] = u1[i] | u2[i] - } - return u -} - // Equal returns true if u1 and u2 equals, otherwise returns false. func Equal(u1 UUID, u2 UUID) bool { return bytes.Equal(u1[:], u2[:]) } // Version returns algorithm version used to generate UUID. -func (u UUID) Version() uint { - return uint(u[6] >> 4) +func (u UUID) Version() byte { + return u[6] >> 4 } // Variant returns UUID layout variant. -func (u UUID) Variant() uint { +func (u UUID) Variant() byte { switch { - case (u[8] & 0x80) == 0x00: + case (u[8] >> 7) == 0x00: return VariantNCS - case (u[8]&0xc0)|0x80 == 0x80: + case (u[8] >> 6) == 0x02: return VariantRFC4122 - case (u[8]&0xe0)|0xc0 == 0xc0: + case (u[8] >> 5) == 0x06: return VariantMicrosoft + case (u[8] >> 5) == 0x07: + fallthrough + default: + return VariantFuture } - return VariantFuture } // Bytes returns bytes slice representation of UUID. @@ -198,13 +116,13 @@ func (u UUID) String() string { buf := make([]byte, 36) hex.Encode(buf[0:8], u[0:4]) - buf[8] = dash + buf[8] = '-' hex.Encode(buf[9:13], u[4:6]) - buf[13] = dash + buf[13] = '-' hex.Encode(buf[14:18], u[6:8]) - buf[18] = dash + buf[18] = '-' hex.Encode(buf[19:23], u[8:10]) - buf[23] = dash + buf[23] = '-' hex.Encode(buf[24:], u[10:]) return string(buf) @@ -215,274 +133,29 @@ func (u *UUID) SetVersion(v byte) { u[6] = (u[6] & 0x0f) | (v << 4) } -// SetVariant sets variant bits as described in RFC 4122. -func (u *UUID) SetVariant() { - u[8] = (u[8] & 0xbf) | 0x80 -} - -// MarshalText implements the encoding.TextMarshaler interface. -// The encoding is the same as returned by String. -func (u UUID) MarshalText() (text []byte, err error) { - text = []byte(u.String()) - return -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Following formats are supported: -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -func (u *UUID) UnmarshalText(text []byte) (err error) { - if len(text) < 32 { - err = fmt.Errorf("uuid: UUID string too short: %s", text) - return +// SetVariant sets variant bits. +func (u *UUID) SetVariant(v byte) { + switch v { + case VariantNCS: + u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) + case VariantRFC4122: + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + case VariantMicrosoft: + u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) + case VariantFuture: + fallthrough + default: + u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) } - - t := text[:] - braced := false - - if bytes.Equal(t[:9], urnPrefix) { - t = t[9:] - } else if t[0] == '{' { - braced = true - t = t[1:] - } - - b := u[:] - - for i, byteGroup := range byteGroups { - if i > 0 && t[0] == '-' { - t = t[1:] - } else if i > 0 && t[0] != '-' { - err = fmt.Errorf("uuid: invalid string format") - return - } - - if i == 2 { - if !bytes.Contains([]byte("012345"), []byte{t[0]}) { - err = fmt.Errorf("uuid: invalid version number: %s", t[0]) - return - } - } - - if len(t) < byteGroup { - err = fmt.Errorf("uuid: UUID string too short: %s", text) - return - } - - if i == 4 && len(t) > byteGroup && - ((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) { - err = fmt.Errorf("uuid: UUID string too long: %s", t) - return - } - - _, err = hex.Decode(b[:byteGroup/2], t[:byteGroup]) - - if err != nil { - return - } - - t = t[byteGroup:] - b = b[byteGroup/2:] - } - - return } -// MarshalBinary implements the encoding.BinaryMarshaler interface. -func (u UUID) MarshalBinary() (data []byte, err error) { - data = u.Bytes() - return -} - -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -// It will return error if the slice isn't 16 bytes long. -func (u *UUID) UnmarshalBinary(data []byte) (err error) { - if len(data) != 16 { - err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) - return - } - copy(u[:], data) - - return -} - -// Value implements the driver.Valuer interface. -func (u UUID) Value() (driver.Value, error) { - return u.String(), nil -} - -// Scan implements the sql.Scanner interface. -// A 16-byte slice is handled by UnmarshalBinary, while -// a longer byte slice or a string is handled by UnmarshalText. -func (u *UUID) Scan(src interface{}) error { - switch src := src.(type) { - case []byte: - if len(src) == 16 { - return u.UnmarshalBinary(src) - } - return u.UnmarshalText(src) - - case string: - return u.UnmarshalText([]byte(src)) - } - - return fmt.Errorf("uuid: cannot convert %T to UUID", src) -} - -// Value implements the driver.Valuer interface. -func (u NullUUID) Value() (driver.Value, error) { - if !u.Valid { - return nil, nil - } - // Delegate to UUID Value function - return u.UUID.Value() -} - -// Scan implements the sql.Scanner interface. -func (u *NullUUID) Scan(src interface{}) error { - if src == nil { - u.UUID, u.Valid = Nil, false - return nil - } - - // Delegate to UUID Scan function - u.Valid = true - return u.UUID.Scan(src) -} - -// FromBytes returns UUID converted from raw byte slice input. -// It will return error if the slice isn't 16 bytes long. -func FromBytes(input []byte) (u UUID, err error) { - err = u.UnmarshalBinary(input) - return -} - -// FromBytesOrNil returns UUID converted from raw byte slice input. -// Same behavior as FromBytes, but returns a Nil UUID on error. -func FromBytesOrNil(input []byte) UUID { - uuid, err := FromBytes(input) +// Must is a helper that wraps a call to a function returning (UUID, error) +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")); +func Must(u UUID, err error) UUID { if err != nil { - return Nil + panic(err) } - return uuid -} - -// FromString returns UUID parsed from string input. -// Input is expected in a form accepted by UnmarshalText. -func FromString(input string) (u UUID, err error) { - err = u.UnmarshalText([]byte(input)) - return -} - -// FromStringOrNil returns UUID parsed from string input. -// Same behavior as FromString, but returns a Nil UUID on error. -func FromStringOrNil(input string) UUID { - uuid, err := FromString(input) - if err != nil { - return Nil - } - return uuid -} - -// Returns UUID v1/v2 storage state. -// Returns epoch timestamp, clock sequence, and hardware address. -func getStorage() (uint64, uint16, []byte) { - storageOnce.Do(initStorage) - - storageMutex.Lock() - defer storageMutex.Unlock() - - timeNow := epochFunc() - // Clock changed backwards since last UUID generation. - // Should increase clock sequence. - if timeNow <= lastTime { - clockSequence++ - } - lastTime = timeNow - - return timeNow, clockSequence, hardwareAddr[:] -} - -// NewV1 returns UUID based on current timestamp and MAC address. -func NewV1() UUID { - u := UUID{} - - timeNow, clockSeq, hardwareAddr := getStorage() - - binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - - copy(u[10:], hardwareAddr) - - u.SetVersion(1) - u.SetVariant() - - return u -} - -// NewV2 returns DCE Security UUID based on POSIX UID/GID. -func NewV2(domain byte) UUID { - u := UUID{} - - timeNow, clockSeq, hardwareAddr := getStorage() - - switch domain { - case DomainPerson: - binary.BigEndian.PutUint32(u[0:], posixUID) - case DomainGroup: - binary.BigEndian.PutUint32(u[0:], posixGID) - } - - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - u[9] = domain - - copy(u[10:], hardwareAddr) - - u.SetVersion(2) - u.SetVariant() - - return u -} - -// NewV3 returns UUID based on MD5 hash of namespace UUID and name. -func NewV3(ns UUID, name string) UUID { - u := newFromHash(md5.New(), ns, name) - u.SetVersion(3) - u.SetVariant() - - return u -} - -// NewV4 returns random generated UUID. -func NewV4() UUID { - u := UUID{} - safeRandom(u[:]) - u.SetVersion(4) - u.SetVariant() - - return u -} - -// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. -func NewV5(ns UUID, name string) UUID { - u := newFromHash(sha1.New(), ns, name) - u.SetVersion(5) - u.SetVariant() - - return u -} - -// Returns UUID based on hashing of namespace UUID and name. -func newFromHash(h hash.Hash, ns UUID, name string) UUID { - u := UUID{} - h.Write(ns[:]) - h.Write([]byte(name)) - copy(u[:], h.Sum(nil)) - return u } diff --git a/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/xenolf/lego/acme/client.go index b8daa7512..72e5f63f4 100644 --- a/vendor/github.com/xenolf/lego/acme/client.go +++ b/vendor/github.com/xenolf/lego/acme/client.go @@ -41,6 +41,17 @@ type solver interface { Solve(challenge challenge, domain string) error } +// Interface for challenges like dns, where we can set a record in advance for ALL challenges. +// This saves quite a bit of time vs creating the records and solving them serially. +type presolver interface { + PreSolve(challenge challenge, domain string) error +} + +// Interface for challenges like dns, where we can solve all the challenges before to delete them. +type cleanup interface { + CleanUp(challenge challenge, domain string) error +} + type validateFunc func(j *jws, domain, uri string, chlng challenge) error // Client is the user-friendy way to ACME @@ -374,8 +385,10 @@ DNSNames: } } - // Add the CSR to the certificate so that it can be used for renewals. - cert.CSR = pemEncode(&csr) + if cert != nil { + // Add the CSR to the certificate so that it can be used for renewals. + cert.CSR = pemEncode(&csr) + } // do not return an empty failures map, because // it would still be a non-nil error value @@ -548,29 +561,75 @@ func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, err return orderRes, nil } +// an authz with the solver we have chosen and the index of the challenge associated with it +type selectedAuthSolver struct { + authz authorization + challengeIndex int + solver solver +} + // Looks through the challenge combinations to find a solvable match. // Then solves the challenges in series and returns. func (c *Client) solveChallengeForAuthz(authorizations []authorization) error { failures := make(ObtainError) - // loop through the resources, basically through the domains. + authSolvers := []*selectedAuthSolver{} + + // loop through the resources, basically through the domains. First pass just selects a solver for each authz. for _, authz := range authorizations { if authz.Status == "valid" { // Boulder might recycle recent validated authz (see issue #267) log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value) continue } - - // no solvers - no solving if i, solver := c.chooseSolver(authz, authz.Identifier.Value); solver != nil { - err := solver.Solve(authz.Challenges[i], authz.Identifier.Value) - if err != nil { - //c.disableAuthz(authz.Identifier) + authSolvers = append(authSolvers, &selectedAuthSolver{ + authz: authz, + challengeIndex: i, + solver: solver, + }) + } else { + failures[authz.Identifier.Value] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Identifier.Value) + } + } + + // for all valid presolvers, first submit the challenges so they have max time to propigate + for _, item := range authSolvers { + authz := item.authz + i := item.challengeIndex + if presolver, ok := item.solver.(presolver); ok { + if err := presolver.PreSolve(authz.Challenges[i], authz.Identifier.Value); err != nil { failures[authz.Identifier.Value] = err } - } else { - //c.disableAuthz(authz) - failures[authz.Identifier.Value] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Identifier.Value) + } + } + + defer func() { + // clean all created TXT records + for _, item := range authSolvers { + if cleanup, ok := item.solver.(cleanup); ok { + if failures[item.authz.Identifier.Value] != nil { + // already failed in previous loop + continue + } + err := cleanup.CleanUp(item.authz.Challenges[item.challengeIndex], item.authz.Identifier.Value) + if err != nil { + log.Warnf("Error cleaning up %s: %v ", item.authz.Identifier.Value, err) + } + } + } + }() + + // finally solve all challenges for real + for _, item := range authSolvers { + authz := item.authz + i := item.challengeIndex + if failures[authz.Identifier.Value] != nil { + // already failed in previous loop + continue + } + if err := item.solver.Solve(authz.Challenges[i], authz.Identifier.Value); err != nil { + failures[authz.Identifier.Value] = err } } diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go index c8a35eb88..2a43ce374 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go @@ -71,8 +71,10 @@ type dnsChallenge struct { provider ChallengeProvider } -func (s *dnsChallenge) Solve(chlng challenge, domain string) error { - log.Infof("[%s] acme: Trying to solve DNS-01", domain) +// PreSolve just submits the txt record to the dns provider. It does not validate record propagation, or +// do anything at all with the acme server. +func (s *dnsChallenge) PreSolve(chlng challenge, domain string) error { + log.Infof("[%s] acme: Preparing to solve DNS-01", domain) if s.provider == nil { return errors.New("no DNS Provider configured") @@ -88,12 +90,18 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { if err != nil { return fmt.Errorf("error presenting token: %s", err) } - defer func() { - err := s.provider.CleanUp(domain, chlng.Token, keyAuth) - if err != nil { - log.Warnf("Error cleaning up %s: %v ", domain, err) - } - }() + + return nil +} + +func (s *dnsChallenge) Solve(chlng challenge, domain string) error { + log.Infof("[%s] acme: Trying to solve DNS-01", domain) + + // Generate the Key Authorization for the challenge + keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) + if err != nil { + return err + } fqdn, value, _ := DNS01Record(domain, keyAuth) @@ -117,6 +125,15 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { return s.validate(s.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) } +// CleanUp cleans the challenge +func (s *dnsChallenge) CleanUp(chlng challenge, domain string) error { + keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) + if err != nil { + return err + } + return s.provider.CleanUp(domain, chlng.Token, keyAuth) +} + // checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers. func checkDNSPropagation(fqdn, value string) (bool, error) { // Initial attempt to resolve at the recursive NS diff --git a/vendor/github.com/xenolf/lego/platform/config/env/env.go b/vendor/github.com/xenolf/lego/platform/config/env/env.go index 1267def78..267adcda5 100644 --- a/vendor/github.com/xenolf/lego/platform/config/env/env.go +++ b/vendor/github.com/xenolf/lego/platform/config/env/env.go @@ -3,6 +3,7 @@ package env import ( "fmt" "os" + "strconv" "strings" ) @@ -25,3 +26,14 @@ func Get(names ...string) (map[string]string, error) { return values, nil } + +// GetOrDefaultInt returns the given environment variable value as an integer. +// Returns the default if the envvar cannot be coopered to an int, or is not found. +func GetOrDefaultInt(envVar string, defaultValue int) int { + v, err := strconv.Atoi(os.Getenv(envVar)) + if err != nil { + return defaultValue + } + + return v +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go b/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go new file mode 100644 index 000000000..13fd853a7 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go @@ -0,0 +1,166 @@ +// Package alidns implements a DNS provider for solving the DNS-01 challenge +// using Alibaba Cloud DNS. +package alidns + +import ( + "fmt" + "os" + "strings" + + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" +) + +const defaultRegionID = "cn-hangzhou" + +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { + client *alidns.Client +} + +// NewDNSProvider returns a DNSProvider instance configured for Alibaba Cloud DNS. +// Credentials must be passed in the environment variables: ALICLOUD_ACCESS_KEY and ALICLOUD_SECRET_KEY. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get("ALICLOUD_ACCESS_KEY", "ALICLOUD_SECRET_KEY") + if err != nil { + return nil, fmt.Errorf("AliDNS: %v", err) + } + + regionID := os.Getenv("ALICLOUD_REGION_ID") + + return NewDNSProviderCredentials(values["ALICLOUD_ACCESS_KEY"], values["ALICLOUD_SECRET_KEY"], regionID) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider instance configured for alidns. +func NewDNSProviderCredentials(apiKey, secretKey, regionID string) (*DNSProvider, error) { + if apiKey == "" || secretKey == "" { + return nil, fmt.Errorf("AliDNS: credentials missing") + } + + if len(regionID) == 0 { + regionID = defaultRegionID + } + + client, err := alidns.NewClientWithAccessKey(regionID, apiKey, secretKey) + if err != nil { + return nil, fmt.Errorf("AliDNS: credentials failed: %v", err) + } + + return &DNSProvider{ + client: client, + }, nil +} + +// Present creates a TXT record to fulfil the dns-01 challenge. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + + _, zoneName, err := d.getHostedZone(domain) + if err != nil { + return err + } + + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + + _, err = d.client.AddDomainRecord(recordAttributes) + if err != nil { + return fmt.Errorf("AliDNS: API call failed: %v", err) + } + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + + records, err := d.findTxtRecords(domain, fqdn) + if err != nil { + return err + } + + _, _, err = d.getHostedZone(domain) + if err != nil { + return err + } + + for _, rec := range records { + request := alidns.CreateDeleteDomainRecordRequest() + request.RecordId = rec.RecordId + _, err = d.client.DeleteDomainRecord(request) + if err != nil { + return err + } + } + return nil +} + +func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { + request := alidns.CreateDescribeDomainsRequest() + zones, err := d.client.DescribeDomains(request) + if err != nil { + return "", "", fmt.Errorf("AliDNS: API call failed: %v", err) + } + + authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + if err != nil { + return "", "", err + } + + var hostedZone alidns.Domain + for _, zone := range zones.Domains.Domain { + if zone.DomainName == acme.UnFqdn(authZone) { + hostedZone = zone + } + } + + if hostedZone.DomainId == "" { + return "", "", fmt.Errorf("AliDNS: zone %s not found in AliDNS for domain %s", authZone, domain) + } + return fmt.Sprintf("%v", hostedZone.DomainId), hostedZone.DomainName, nil +} + +func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *alidns.AddDomainRecordRequest { + request := alidns.CreateAddDomainRecordRequest() + request.Type = "TXT" + request.DomainName = zone + request.RR = d.extractRecordName(fqdn, zone) + request.Value = value + request.TTL = requests.NewInteger(600) + return request +} + +func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]alidns.Record, error) { + _, zoneName, err := d.getHostedZone(domain) + if err != nil { + return nil, err + } + + request := alidns.CreateDescribeDomainRecordsRequest() + request.DomainName = zoneName + request.PageSize = requests.NewInteger(500) + + var records []alidns.Record + + result, err := d.client.DescribeDomainRecords(request) + if err != nil { + return records, fmt.Errorf("AliDNS: API call has failed: %v", err) + } + + recordName := d.extractRecordName(fqdn, zoneName) + for _, record := range result.DomainRecords.Record { + if record.RR == recordName { + records = append(records, record) + } + } + return records, nil +} + +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { + name := acme.UnFqdn(fqdn) + if idx := strings.Index(name, "."+domain); idx != -1 { + return name[:idx] + } + return name +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go index ec8c31875..5ef34da5c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go @@ -5,6 +5,7 @@ import ( "github.com/xenolf/lego/acme" "github.com/xenolf/lego/providers/dns/acmedns" + "github.com/xenolf/lego/providers/dns/alidns" "github.com/xenolf/lego/providers/dns/auroradns" "github.com/xenolf/lego/providers/dns/azure" "github.com/xenolf/lego/providers/dns/bluecat" @@ -24,10 +25,12 @@ import ( "github.com/xenolf/lego/providers/dns/gcloud" "github.com/xenolf/lego/providers/dns/glesys" "github.com/xenolf/lego/providers/dns/godaddy" + "github.com/xenolf/lego/providers/dns/iij" "github.com/xenolf/lego/providers/dns/lightsail" "github.com/xenolf/lego/providers/dns/linode" "github.com/xenolf/lego/providers/dns/namecheap" "github.com/xenolf/lego/providers/dns/namedotcom" + "github.com/xenolf/lego/providers/dns/netcup" "github.com/xenolf/lego/providers/dns/nifcloud" "github.com/xenolf/lego/providers/dns/ns1" "github.com/xenolf/lego/providers/dns/otc" @@ -46,6 +49,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) switch name { case "acme-dns": return acmedns.NewDNSProvider() + case "alidns": + return alidns.NewDNSProvider() case "azure": return azure.NewDNSProvider() case "auroradns": @@ -82,6 +87,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) return gcloud.NewDNSProvider() case "godaddy": return godaddy.NewDNSProvider() + case "iij": + return iij.NewDNSProvider() case "lightsail": return lightsail.NewDNSProvider() case "linode": @@ -92,6 +99,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) return namecheap.NewDNSProvider() case "namedotcom": return namedotcom.NewDNSProvider() + case "netcup": + return netcup.NewDNSProvider() case "nifcloud": return nifcloud.NewDNSProvider() case "rackspace": diff --git a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go index c5b26d1dc..e619b1663 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go @@ -58,20 +58,27 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return err } - record := egoscale.DNSRecord{ - Name: recordName, - TTL: ttl, - Content: value, - RecordType: "TXT", - } - if recordID == 0 { + record := egoscale.DNSRecord{ + Name: recordName, + TTL: ttl, + Content: value, + RecordType: "TXT", + } + _, err := d.client.CreateRecord(zone, record) if err != nil { return errors.New("Error while creating DNS record: " + err.Error()) } } else { - record.ID = recordID + record := egoscale.UpdateDNSRecord{ + ID: recordID, + Name: recordName, + TTL: ttl, + Content: value, + RecordType: "TXT", + } + _, err := d.client.UpdateRecord(zone, record) if err != nil { return errors.New("Error while updating DNS record: " + err.Error()) diff --git a/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go b/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go new file mode 100644 index 000000000..ea9e57785 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go @@ -0,0 +1,211 @@ +// Package iij implements a DNS provider for solving the DNS-01 challenge using IIJ DNS. +package iij + +import ( + "fmt" + "strings" + "time" + + "github.com/iij/doapi" + "github.com/iij/doapi/protocol" + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" +) + +// Config is used to configure the creation of the DNSProvider +type Config struct { + AccessKey string + SecretKey string + DoServiceCode string +} + +// DNSProvider implements the acme.ChallengeProvider interface +type DNSProvider struct { + api *doapi.API + config *Config +} + +// NewDNSProvider returns a DNSProvider instance configured for IIJ DO +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get("IIJ_API_ACCESS_KEY", "IIJ_API_SECRET_KEY", "IIJ_DO_SERVICE_CODE") + if err != nil { + return nil, fmt.Errorf("IIJ: %v", err) + } + + return NewDNSProviderConfig(&Config{ + AccessKey: values["IIJ_API_ACCESS_KEY"], + SecretKey: values["IIJ_API_SECRET_KEY"], + DoServiceCode: values["IIJ_DO_SERVICE_CODE"], + }) +} + +// NewDNSProviderConfig takes a given config ans returns a custom configured +// DNSProvider instance +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + return &DNSProvider{ + api: doapi.NewAPI(config.AccessKey, config.SecretKey), + config: config, + }, nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +func (p *DNSProvider) Timeout() (timeout, interval time.Duration) { + return time.Minute * 2, time.Second * 4 +} + +// Present creates a TXT record using the specified parameters +func (p *DNSProvider) Present(domain, token, keyAuth string) error { + _, value, _ := acme.DNS01Record(domain, keyAuth) + return p.addTxtRecord(domain, value) +} + +// CleanUp removes the TXT record matching the specified parameters +func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { + _, value, _ := acme.DNS01Record(domain, keyAuth) + return p.deleteTxtRecord(domain, value) +} + +func (p *DNSProvider) addTxtRecord(domain, value string) error { + zones, err := p.listZones() + if err != nil { + return err + } + + owner, zone, err := splitDomain(domain, zones) + if err != nil { + return err + } + + request := protocol.RecordAdd{ + DoServiceCode: p.config.DoServiceCode, + ZoneName: zone, + Owner: owner, + TTL: "300", + RecordType: "TXT", + RData: value, + } + + response := &protocol.RecordAddResponse{} + + if err := doapi.Call(*p.api, request, response); err != nil { + return err + } + + return p.commit() +} + +func (p *DNSProvider) deleteTxtRecord(domain, value string) error { + zones, err := p.listZones() + if err != nil { + return err + } + + owner, zone, err := splitDomain(domain, zones) + if err != nil { + return err + } + + id, err := p.findTxtRecord(owner, zone, value) + if err != nil { + return err + } + + request := protocol.RecordDelete{ + DoServiceCode: p.config.DoServiceCode, + ZoneName: zone, + RecordID: id, + } + + response := &protocol.RecordDeleteResponse{} + + if err := doapi.Call(*p.api, request, response); err != nil { + return err + } + + return p.commit() +} + +func (p *DNSProvider) commit() error { + request := protocol.Commit{ + DoServiceCode: p.config.DoServiceCode, + } + + response := &protocol.CommitResponse{} + + return doapi.Call(*p.api, request, response) +} + +func (p *DNSProvider) findTxtRecord(owner, zone, value string) (string, error) { + request := protocol.RecordListGet{ + DoServiceCode: p.config.DoServiceCode, + ZoneName: zone, + } + + response := &protocol.RecordListGetResponse{} + + if err := doapi.Call(*p.api, request, response); err != nil { + return "", err + } + + var id string + + for _, record := range response.RecordList { + if record.Owner == owner && record.RecordType == "TXT" && record.RData == "\""+value+"\"" { + id = record.Id + } + } + + if id == "" { + return "", fmt.Errorf("%s record in %s not found", owner, zone) + } + + return id, nil +} + +func (p *DNSProvider) listZones() ([]string, error) { + request := protocol.ZoneListGet{ + DoServiceCode: p.config.DoServiceCode, + } + + response := &protocol.ZoneListGetResponse{} + + if err := doapi.Call(*p.api, request, response); err != nil { + return nil, err + } + + return response.ZoneList, nil +} + +func splitDomain(domain string, zones []string) (string, string, error) { + parts := strings.Split(strings.Trim(domain, "."), ".") + + var owner string + var zone string + + for i := 0; i < len(parts)-1; i++ { + zone = strings.Join(parts[i:], ".") + if zoneContains(zone, zones) { + baseOwner := strings.Join(parts[0:i], ".") + if len(baseOwner) > 0 { + baseOwner = "." + baseOwner + } + owner = "_acme-challenge" + baseOwner + break + } + } + + if len(owner) == 0 { + return "", "", fmt.Errorf("%s not found", domain) + } + + return owner, zone, nil +} + +func zoneContains(zone string, zones []string) bool { + for _, z := range zones { + if zone == z { + return true + } + } + return false +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go new file mode 100644 index 000000000..e498d694c --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go @@ -0,0 +1,327 @@ +package netcup + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/xenolf/lego/acme" +) + +// netcupBaseURL for reaching the jSON-based API-Endpoint of netcup +const netcupBaseURL = "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON" + +// success response status +const success = "success" + +// Request wrapper as specified in netcup wiki +// needed for every request to netcup API around *Msg +// https://www.netcup-wiki.de/wiki/CCP_API#Anmerkungen_zu_JSON-Requests +type Request struct { + Action string `json:"action"` + Param interface{} `json:"param"` +} + +// LoginMsg as specified in netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#login +type LoginMsg struct { + CustomerNumber string `json:"customernumber"` + APIKey string `json:"apikey"` + APIPassword string `json:"apipassword"` + ClientRequestID string `json:"clientrequestid,omitempty"` +} + +// LogoutMsg as specified in netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#logout +type LogoutMsg struct { + CustomerNumber string `json:"customernumber"` + APIKey string `json:"apikey"` + APISessionID string `json:"apisessionid"` + ClientRequestID string `json:"clientrequestid,omitempty"` +} + +// UpdateDNSRecordsMsg as specified in netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#updateDnsRecords +type UpdateDNSRecordsMsg struct { + DomainName string `json:"domainname"` + CustomerNumber string `json:"customernumber"` + APIKey string `json:"apikey"` + APISessionID string `json:"apisessionid"` + ClientRequestID string `json:"clientrequestid,omitempty"` + DNSRecordSet DNSRecordSet `json:"dnsrecordset"` +} + +// DNSRecordSet as specified in netcup WSDL +// needed in UpdateDNSRecordsMsg +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#Dnsrecordset +type DNSRecordSet struct { + DNSRecords []DNSRecord `json:"dnsrecords"` +} + +// InfoDNSRecordsMsg as specified in netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#infoDnsRecords +type InfoDNSRecordsMsg struct { + DomainName string `json:"domainname"` + CustomerNumber string `json:"customernumber"` + APIKey string `json:"apikey"` + APISessionID string `json:"apisessionid"` + ClientRequestID string `json:"clientrequestid,omitempty"` +} + +// DNSRecord as specified in netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#Dnsrecord +type DNSRecord struct { + ID int `json:"id,string,omitempty"` + Hostname string `json:"hostname"` + RecordType string `json:"type"` + Priority string `json:"priority,omitempty"` + Destination string `json:"destination"` + DeleteRecord bool `json:"deleterecord,omitempty"` + State string `json:"state,omitempty"` +} + +// ResponseMsg as specified in netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php#Responsemessage +type ResponseMsg struct { + ServerRequestID string `json:"serverrequestid"` + ClientRequestID string `json:"clientrequestid,omitempty"` + Action string `json:"action"` + Status string `json:"status"` + StatusCode int `json:"statuscode"` + ShortMessage string `json:"shortmessage"` + LongMessage string `json:"longmessage"` + ResponseData ResponseData `json:"responsedata,omitempty"` +} + +// LogoutResponseMsg similar to ResponseMsg +// allows empty ResponseData field whilst unmarshaling +type LogoutResponseMsg struct { + ServerRequestID string `json:"serverrequestid"` + ClientRequestID string `json:"clientrequestid,omitempty"` + Action string `json:"action"` + Status string `json:"status"` + StatusCode int `json:"statuscode"` + ShortMessage string `json:"shortmessage"` + LongMessage string `json:"longmessage"` + ResponseData string `json:"responsedata,omitempty"` +} + +// ResponseData to enable correct unmarshaling of ResponseMsg +type ResponseData struct { + APISessionID string `json:"apisessionid"` + DNSRecords []DNSRecord `json:"dnsrecords"` +} + +// Client netcup DNS client +type Client struct { + customerNumber string + apiKey string + apiPassword string + client *http.Client +} + +// NewClient creates a netcup DNS client +func NewClient(httpClient *http.Client, customerNumber string, apiKey string, apiPassword string) *Client { + client := http.DefaultClient + if httpClient != nil { + client = httpClient + } + + return &Client{ + customerNumber: customerNumber, + apiKey: apiKey, + apiPassword: apiPassword, + client: client, + } +} + +// Login performs the login as specified by the netcup WSDL +// returns sessionID needed to perform remaining actions +// https://ccp.netcup.net/run/webservice/servers/endpoint.php +func (c *Client) Login() (string, error) { + payload := &Request{ + Action: "login", + Param: &LoginMsg{ + CustomerNumber: c.customerNumber, + APIKey: c.apiKey, + APIPassword: c.apiPassword, + ClientRequestID: "", + }, + } + + response, err := c.sendRequest(payload) + if err != nil { + return "", fmt.Errorf("netcup: error sending request to DNS-API, %v", err) + } + + var r ResponseMsg + + err = json.Unmarshal(response, &r) + if err != nil { + return "", fmt.Errorf("netcup: error decoding response of DNS-API, %v", err) + } + if r.Status != success { + return "", fmt.Errorf("netcup: error logging into DNS-API, %v", r.LongMessage) + } + return r.ResponseData.APISessionID, nil +} + +// Logout performs the logout with the supplied sessionID as specified by the netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php +func (c *Client) Logout(sessionID string) error { + payload := &Request{ + Action: "logout", + Param: &LogoutMsg{ + CustomerNumber: c.customerNumber, + APIKey: c.apiKey, + APISessionID: sessionID, + ClientRequestID: "", + }, + } + + response, err := c.sendRequest(payload) + if err != nil { + return fmt.Errorf("netcup: error logging out of DNS-API: %v", err) + } + + var r LogoutResponseMsg + + err = json.Unmarshal(response, &r) + if err != nil { + return fmt.Errorf("netcup: error logging out of DNS-API: %v", err) + } + + if r.Status != success { + return fmt.Errorf("netcup: error logging out of DNS-API: %v", r.ShortMessage) + } + return nil +} + +// UpdateDNSRecord performs an update of the DNSRecords as specified by the netcup WSDL +// https://ccp.netcup.net/run/webservice/servers/endpoint.php +func (c *Client) UpdateDNSRecord(sessionID, domainName string, record DNSRecord) error { + payload := &Request{ + Action: "updateDnsRecords", + Param: UpdateDNSRecordsMsg{ + DomainName: domainName, + CustomerNumber: c.customerNumber, + APIKey: c.apiKey, + APISessionID: sessionID, + ClientRequestID: "", + DNSRecordSet: DNSRecordSet{DNSRecords: []DNSRecord{record}}, + }, + } + + response, err := c.sendRequest(payload) + if err != nil { + return fmt.Errorf("netcup: %v", err) + } + + var r ResponseMsg + + err = json.Unmarshal(response, &r) + if err != nil { + return fmt.Errorf("netcup: %v", err) + } + + if r.Status != success { + return fmt.Errorf("netcup: %s: %+v", r.ShortMessage, r) + } + return nil +} + +// GetDNSRecords retrieves all dns records of an DNS-Zone as specified by the netcup WSDL +// returns an array of DNSRecords +// https://ccp.netcup.net/run/webservice/servers/endpoint.php +func (c *Client) GetDNSRecords(hostname, apiSessionID string) ([]DNSRecord, error) { + payload := &Request{ + Action: "infoDnsRecords", + Param: InfoDNSRecordsMsg{ + DomainName: hostname, + CustomerNumber: c.customerNumber, + APIKey: c.apiKey, + APISessionID: apiSessionID, + ClientRequestID: "", + }, + } + + response, err := c.sendRequest(payload) + if err != nil { + return nil, fmt.Errorf("netcup: %v", err) + } + + var r ResponseMsg + + err = json.Unmarshal(response, &r) + if err != nil { + return nil, fmt.Errorf("netcup: %v", err) + } + + if r.Status != success { + return nil, fmt.Errorf("netcup: %s", r.ShortMessage) + } + return r.ResponseData.DNSRecords, nil + +} + +// sendRequest marshals given body to JSON, send the request to netcup API +// and returns body of response +func (c *Client) sendRequest(payload interface{}) ([]byte, error) { + body, err := json.Marshal(payload) + if err != nil { + return nil, fmt.Errorf("netcup: %v", err) + } + + req, err := http.NewRequest(http.MethodPost, netcupBaseURL, bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("netcup: %v", err) + } + req.Close = true + + req.Header.Set("content-type", "application/json") + req.Header.Set("User-Agent", acme.UserAgent) + + resp, err := c.client.Do(req) + if err != nil { + return nil, fmt.Errorf("netcup: %v", err) + } + + if resp.StatusCode > 299 { + return nil, fmt.Errorf("netcup: API request failed with HTTP Status code %d", resp.StatusCode) + } + + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("netcup: read of response body failed, %v", err) + } + defer resp.Body.Close() + + return body, nil +} + +// GetDNSRecordIdx searches a given array of DNSRecords for a given DNSRecord +// equivalence is determined by Destination and RecortType attributes +// returns index of given DNSRecord in given array of DNSRecords +func GetDNSRecordIdx(records []DNSRecord, record DNSRecord) (int, error) { + for index, element := range records { + if record.Destination == element.Destination && record.RecordType == element.RecordType { + return index, nil + } + } + return -1, fmt.Errorf("netcup: no DNS Record found") +} + +// CreateTxtRecord uses the supplied values to return a DNSRecord of type TXT for the dns-01 challenge +func CreateTxtRecord(hostname, value string) DNSRecord { + return DNSRecord{ + ID: 0, + Hostname: hostname, + RecordType: "TXT", + Priority: "", + Destination: value, + DeleteRecord: false, + State: "", + } +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go new file mode 100644 index 000000000..e7cc4c6b7 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go @@ -0,0 +1,116 @@ +// Package netcup implements a DNS Provider for solving the DNS-01 challenge using the netcup DNS API. +package netcup + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" +) + +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { + client *Client +} + +// NewDNSProvider returns a DNSProvider instance configured for netcup. +// Credentials must be passed in the environment variables: NETCUP_CUSTOMER_NUMBER, +// NETCUP_API_KEY, NETCUP_API_PASSWORD +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get("NETCUP_CUSTOMER_NUMBER", "NETCUP_API_KEY", "NETCUP_API_PASSWORD") + if err != nil { + return nil, fmt.Errorf("netcup: %v", err) + } + + return NewDNSProviderCredentials(values["NETCUP_CUSTOMER_NUMBER"], values["NETCUP_API_KEY"], values["NETCUP_API_PASSWORD"]) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for netcup. +func NewDNSProviderCredentials(customer, key, password string) (*DNSProvider, error) { + if customer == "" || key == "" || password == "" { + return nil, fmt.Errorf("netcup: netcup credentials missing") + } + + httpClient := &http.Client{ + Timeout: 10 * time.Second, + } + + return &DNSProvider{ + client: NewClient(httpClient, customer, key, password), + }, nil +} + +// Present creates a TXT record to fulfill the dns-01 challenge +func (d *DNSProvider) Present(domainName, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domainName, keyAuth) + + zone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("netcup: failed to find DNSZone, %v", err) + } + + sessionID, err := d.client.Login() + if err != nil { + return err + } + + hostname := strings.Replace(fqdn, "."+zone, "", 1) + record := CreateTxtRecord(hostname, value) + + err = d.client.UpdateDNSRecord(sessionID, acme.UnFqdn(zone), record) + if err != nil { + if errLogout := d.client.Logout(sessionID); errLogout != nil { + return fmt.Errorf("failed to add TXT-Record: %v; %v", err, errLogout) + } + return fmt.Errorf("failed to add TXT-Record: %v", err) + } + + return d.client.Logout(sessionID) +} + +// CleanUp removes the TXT record matching the specified parameters +func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domainname, keyAuth) + + zone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("failed to find DNSZone, %v", err) + } + + sessionID, err := d.client.Login() + if err != nil { + return err + } + + hostname := strings.Replace(fqdn, "."+zone, "", 1) + + zone = acme.UnFqdn(zone) + + records, err := d.client.GetDNSRecords(zone, sessionID) + if err != nil { + return err + } + + record := CreateTxtRecord(hostname, value) + + idx, err := GetDNSRecordIdx(records, record) + if err != nil { + return err + } + + records[idx].DeleteRecord = true + + err = d.client.UpdateDNSRecord(sessionID, zone, records[idx]) + if err != nil { + if errLogout := d.client.Logout(sessionID); errLogout != nil { + return fmt.Errorf("%v; %v", err, errLogout) + } + return err + } + + return d.client.Logout(sessionID) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go index e1999ea50..0a35f896c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go @@ -50,13 +50,17 @@ func NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, c return nil, fmt.Errorf("OVH credentials missing") } - ovhClient, _ := ovh.NewClient( + ovhClient, err := ovh.NewClient( apiEndpoint, applicationKey, applicationSecret, consumerKey, ) + if err != nil { + return nil, err + } + return &DNSProvider{ client: ovhClient, recordIDs: make(map[string]int), @@ -65,31 +69,12 @@ func NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, c // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - - // txtRecordRequest represents the request body to DO's API to make a TXT record - type txtRecordRequest struct { - FieldType string `json:"fieldType"` - SubDomain string `json:"subDomain"` - Target string `json:"target"` - TTL int `json:"ttl"` - } - - // txtRecordResponse represents a response from DO's API after making a TXT record - type txtRecordResponse struct { - ID int `json:"id"` - FieldType string `json:"fieldType"` - SubDomain string `json:"subDomain"` - Target string `json:"target"` - TTL int `json:"ttl"` - Zone string `json:"zone"` - } - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) // Parse domain name authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) + return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) @@ -133,7 +118,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) + return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) @@ -160,3 +145,21 @@ func (d *DNSProvider) extractRecordName(fqdn, domain string) string { } return name } + +// txtRecordRequest represents the request body to DO's API to make a TXT record +type txtRecordRequest struct { + FieldType string `json:"fieldType"` + SubDomain string `json:"subDomain"` + Target string `json:"target"` + TTL int `json:"ttl"` +} + +// txtRecordResponse represents a response from DO's API after making a TXT record +type txtRecordResponse struct { + ID int `json:"id"` + FieldType string `json:"fieldType"` + SubDomain string `json:"subDomain"` + Target string `json:"target"` + TTL int `json:"ttl"` + Zone string `json:"zone"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go index d7cc4c719..71fd4e859 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go +++ b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go @@ -16,6 +16,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/route53" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // Config is used to configure the creation of the DNSProvider @@ -29,11 +30,13 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { + propagationMins := env.GetOrDefaultInt("AWS_PROPAGATION_TIMEOUT", 2) + intervalSecs := env.GetOrDefaultInt("AWS_POLLING_INTERVAL", 4) return &Config{ - MaxRetries: 5, - TTL: 10, - PropagationTimeout: time.Minute * 2, - PollingInterval: time.Second * 4, + MaxRetries: env.GetOrDefaultInt("AWS_MAX_RETRIES", 5), + TTL: env.GetOrDefaultInt("AWS_TTL", 10), + PropagationTimeout: time.Second * time.Duration(propagationMins), + PollingInterval: time.Second * time.Duration(intervalSecs), HostedZoneID: os.Getenv("AWS_HOSTED_ZONE_ID"), } } From c52f4b043dab3a39097f158f59250b6b1a594d6a Mon Sep 17 00:00:00 2001 From: NicoMen Date: Fri, 14 Sep 2018 13:34:03 +0200 Subject: [PATCH 04/10] =?UTF-8?q?Add=20interface=20to=20Tr=C3=A6fik=20logg?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- log/logger.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/log/logger.go b/log/logger.go index ed5270775..768352520 100644 --- a/log/logger.go +++ b/log/logger.go @@ -10,8 +10,14 @@ import ( "github.com/sirupsen/logrus" ) +// Logger allows overriding the logrus logger behavior +type Logger interface { + logrus.FieldLogger + WriterLevel(logrus.Level) *io.PipeWriter +} + var ( - logger *logrus.Entry + logger Logger logFilePath string logFile *os.File ) @@ -41,6 +47,11 @@ func SetLevel(level logrus.Level) { logrus.SetLevel(level) } +// SetLogger sets the logger. +func SetLogger(l Logger) { + logger = l +} + // GetLevel returns the standard logger level. func GetLevel() logrus.Level { return logrus.GetLevel() From a80cca95a28f282a9565ac6450fe485ee35cc84c Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 17 Sep 2018 15:16:03 +0200 Subject: [PATCH 05/10] Update lego --- Gopkg.lock | 3 +- docs/configuration/acme.md | 1 + .../xenolf/lego/acme/dns_challenge.go | 20 +- .../xenolf/lego/platform/config/env/env.go | 34 ++ .../lego/providers/dns/alidns/alidns.go | 102 +++-- .../lego/providers/dns/auroradns/auroradns.go | 114 ++++-- .../xenolf/lego/providers/dns/azure/azure.go | 141 ++++--- .../lego/providers/dns/bluecat/bluecat.go | 376 +++++++++--------- .../lego/providers/dns/bluecat/client.go | 16 + .../lego/providers/dns/cloudflare/client.go | 212 ++++++++++ .../providers/dns/cloudflare/cloudflare.go | 236 +++-------- .../lego/providers/dns/cloudxns/client.go | 208 ++++++++++ .../lego/providers/dns/cloudxns/cloudxns.go | 220 +++------- .../lego/providers/dns/digitalocean/client.go | 26 ++ .../dns/digitalocean/digitalocean.go | 254 +++++++----- .../lego/providers/dns/dns_providers.go | 3 + .../lego/providers/dns/dnsimple/dnsimple.go | 84 +++- .../lego/providers/dns/dnsmadeeasy/client.go | 168 ++++++++ .../providers/dns/dnsmadeeasy/dnsmadeeasy.go | 227 ++++------- .../lego/providers/dns/dnspod/dnspod.go | 82 +++- .../lego/providers/dns/duckdns/duckdns.go | 65 ++- .../xenolf/lego/providers/dns/dyn/client.go | 35 ++ .../xenolf/lego/providers/dns/dyn/dyn.go | 302 +++++++------- .../lego/providers/dns/exoscale/exoscale.go | 84 +++- .../lego/providers/dns/fastdns/fastdns.go | 91 +++-- .../xenolf/lego/providers/dns/gandi/client.go | 94 +++++ .../xenolf/lego/providers/dns/gandi/gandi.go | 266 +++++-------- .../lego/providers/dns/gandiv5/client.go | 18 + .../lego/providers/dns/gandiv5/gandiv5.go | 170 ++++---- .../lego/providers/dns/gcloud/googlecloud.go | 132 +++--- .../lego/providers/dns/glesys/client.go | 24 ++ .../lego/providers/dns/glesys/glesys.go | 122 +++--- .../lego/providers/dns/godaddy/godaddy.go | 90 +++-- .../lego/providers/dns/hostingde/client.go | 91 +++++ .../lego/providers/dns/hostingde/hostingde.go | 209 ++++++++++ .../xenolf/lego/providers/dns/iij/iij.go | 98 +++-- .../lego/providers/dns/lightsail/lightsail.go | 92 +++-- .../lego/providers/dns/linode/linode.go | 70 +++- .../lego/providers/dns/namecheap/client.go | 44 ++ .../lego/providers/dns/namecheap/namecheap.go | 328 +++++++-------- .../providers/dns/namedotcom/namedotcom.go | 106 +++-- .../lego/providers/dns/netcup/client.go | 64 +-- .../lego/providers/dns/netcup/netcup.go | 106 +++-- .../lego/providers/dns/nifcloud/client.go | 38 +- .../lego/providers/dns/nifcloud/nifcloud.go | 100 ++++- .../xenolf/lego/providers/dns/ns1/ns1.go | 78 +++- .../xenolf/lego/providers/dns/otc/client.go | 68 ++++ .../xenolf/lego/providers/dns/otc/otc.go | 362 +++++++++-------- .../xenolf/lego/providers/dns/ovh/ovh.go | 107 +++-- .../xenolf/lego/providers/dns/pdns/pdns.go | 119 ++++-- .../lego/providers/dns/rackspace/client.go | 47 +++ .../lego/providers/dns/rackspace/rackspace.go | 172 ++++---- .../lego/providers/dns/rfc2136/rfc2136.go | 166 +++++--- .../lego/providers/dns/route53/route53.go | 36 +- .../providers/dns/sakuracloud/sakuracloud.go | 83 +++- .../lego/providers/dns/vegadns/vegadns.go | 98 +++-- .../xenolf/lego/providers/dns/vultr/vultr.go | 96 ++++- 57 files changed, 4479 insertions(+), 2319 deletions(-) create mode 100644 vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/cloudflare/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/dyn/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/gandi/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/glesys/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/otc/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go diff --git a/Gopkg.lock b/Gopkg.lock index 1cf5788a7..111a16957 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1352,6 +1352,7 @@ "providers/dns/gcloud", "providers/dns/glesys", "providers/dns/godaddy", + "providers/dns/hostingde", "providers/dns/iij", "providers/dns/lightsail", "providers/dns/linode", @@ -1370,7 +1371,7 @@ "providers/dns/vegadns", "providers/dns/vultr" ] - revision = "ad34a85dada244c4e4a50d71963a90ea70736033" + revision = "83e2300e01226dcb006946873ca5434291fb16ef" [[projects]] branch = "master" diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md index d510c87dc..acc9d3e70 100644 --- a/docs/configuration/acme.md +++ b/docs/configuration/acme.md @@ -273,6 +273,7 @@ Here is a list of supported `provider`s, that can automate the DNS verification, | [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet | | [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet | | [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES | +| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet | | [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet | | [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet | | [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet | diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go index 2a43ce374..739566251 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go @@ -24,12 +24,20 @@ var ( const defaultResolvConf = "/etc/resolv.conf" +const ( + // DefaultPropagationTimeout default propagation timeout + DefaultPropagationTimeout = 60 * time.Second + + // DefaultPollingInterval default polling interval + DefaultPollingInterval = 2 * time.Second +) + var defaultNameservers = []string{ "google-public-dns-a.google.com:53", "google-public-dns-b.google.com:53", } -// RecursiveNameservers are used to pre-check DNS propagations +// RecursiveNameservers are used to pre-check DNS propagation var RecursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers) // DNSTimeout is used to override the default DNS timeout of 10 seconds. @@ -112,7 +120,7 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { case ChallengeProviderTimeout: timeout, interval = provider.Timeout() default: - timeout, interval = 60*time.Second, 2*time.Second + timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval } err = WaitFor(timeout, interval, func() (bool, error) { @@ -227,7 +235,7 @@ func lookupNameservers(fqdn string) ([]string, error) { zone, err := FindZoneByFqdn(fqdn, RecursiveNameservers) if err != nil { - return nil, fmt.Errorf("Could not determine the zone: %v", err) + return nil, fmt.Errorf("could not determine the zone: %v", err) } r, err := dnsQuery(zone, dns.TypeNS, RecursiveNameservers, true) @@ -244,7 +252,7 @@ func lookupNameservers(fqdn string) ([]string, error) { if len(authoritativeNss) > 0 { return authoritativeNss, nil } - return nil, fmt.Errorf("Could not determine authoritative nameservers") + return nil, fmt.Errorf("could not determine authoritative nameservers") } // FindZoneByFqdn determines the zone apex for the given fqdn by recursing up the @@ -266,7 +274,7 @@ func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) { // Any response code other than NOERROR and NXDOMAIN is treated as error if in.Rcode != dns.RcodeNameError && in.Rcode != dns.RcodeSuccess { - return "", fmt.Errorf("Unexpected response code '%s' for %s", + return "", fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain) } @@ -289,7 +297,7 @@ func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) { } } - return "", fmt.Errorf("Could not find the start of authority") + return "", fmt.Errorf("could not find the start of authority") } // dnsMsgContainsCNAME checks for a CNAME answer in msg diff --git a/vendor/github.com/xenolf/lego/platform/config/env/env.go b/vendor/github.com/xenolf/lego/platform/config/env/env.go index 267adcda5..4da1b4b60 100644 --- a/vendor/github.com/xenolf/lego/platform/config/env/env.go +++ b/vendor/github.com/xenolf/lego/platform/config/env/env.go @@ -5,6 +5,7 @@ import ( "os" "strconv" "strings" + "time" ) // Get environment variables @@ -37,3 +38,36 @@ func GetOrDefaultInt(envVar string, defaultValue int) int { return v } + +// GetOrDefaultSecond returns the given environment variable value as an time.Duration (second). +// Returns the default if the envvar cannot be coopered to an int, or is not found. +func GetOrDefaultSecond(envVar string, defaultValue time.Duration) time.Duration { + v := GetOrDefaultInt(envVar, -1) + if v < 0 { + return defaultValue + } + + return time.Duration(v) * time.Second +} + +// GetOrDefaultString returns the given environment variable value as a string. +// Returns the default if the envvar cannot be find. +func GetOrDefaultString(envVar string, defaultValue string) string { + v := os.Getenv(envVar) + if len(v) == 0 { + return defaultValue + } + + return v +} + +// GetOrDefaultBool returns the given environment variable value as a boolean. +// Returns the default if the envvar cannot be coopered to a boolean, or is not found. +func GetOrDefaultBool(envVar string, defaultValue bool) bool { + v, err := strconv.ParseBool(os.Getenv(envVar)) + if err != nil { + return defaultValue + } + + return v +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go b/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go index 13fd853a7..2d3c38b64 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/alidns/alidns.go @@ -3,10 +3,14 @@ package alidns import ( + "errors" "fmt" "os" "strings" + "time" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/services/alidns" "github.com/xenolf/lego/acme" @@ -15,8 +19,30 @@ import ( const defaultRegionID = "cn-hangzhou" +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + SecretKey string + RegionID string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPTimeout time.Duration +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("ALICLOUD_TTL", 600), + PropagationTimeout: env.GetOrDefaultSecond("ALICLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("ALICLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPTimeout: env.GetOrDefaultSecond("ALICLOUD_HTTP_TIMEOUT", 10*time.Second), + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { + config *Config client *alidns.Client } @@ -25,48 +51,74 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("ALICLOUD_ACCESS_KEY", "ALICLOUD_SECRET_KEY") if err != nil { - return nil, fmt.Errorf("AliDNS: %v", err) + return nil, fmt.Errorf("alicloud: %v", err) } - regionID := os.Getenv("ALICLOUD_REGION_ID") + config := NewDefaultConfig() + config.APIKey = values["ALICLOUD_ACCESS_KEY"] + config.SecretKey = values["ALICLOUD_SECRET_KEY"] + config.RegionID = os.Getenv("ALICLOUD_REGION_ID") - return NewDNSProviderCredentials(values["ALICLOUD_ACCESS_KEY"], values["ALICLOUD_SECRET_KEY"], regionID) + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider instance configured for alidns. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for alidns. +// Deprecated func NewDNSProviderCredentials(apiKey, secretKey, regionID string) (*DNSProvider, error) { - if apiKey == "" || secretKey == "" { - return nil, fmt.Errorf("AliDNS: credentials missing") + config := NewDefaultConfig() + config.APIKey = apiKey + config.SecretKey = secretKey + config.RegionID = regionID + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for alidns. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("alicloud: the configuration of the DNS provider is nil") } - if len(regionID) == 0 { - regionID = defaultRegionID + if config.APIKey == "" || config.SecretKey == "" { + return nil, fmt.Errorf("alicloud: credentials missing") } - client, err := alidns.NewClientWithAccessKey(regionID, apiKey, secretKey) + if len(config.RegionID) == 0 { + config.RegionID = defaultRegionID + } + + conf := sdk.NewConfig().WithTimeout(config.HTTPTimeout) + credential := credentials.NewAccessKeyCredential(config.APIKey, config.SecretKey) + + client, err := alidns.NewClientWithOptions(config.RegionID, conf, credential) if err != nil { - return nil, fmt.Errorf("AliDNS: credentials failed: %v", err) + return nil, fmt.Errorf("alicloud: credentials failed: %v", err) } - return &DNSProvider{ - client: client, - }, nil + return &DNSProvider{config: config, client: client}, nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) _, zoneName, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("alicloud: %v", err) } - recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value) _, err = d.client.AddDomainRecord(recordAttributes) if err != nil { - return fmt.Errorf("AliDNS: API call failed: %v", err) + return fmt.Errorf("alicloud: API call failed: %v", err) } return nil } @@ -77,12 +129,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { records, err := d.findTxtRecords(domain, fqdn) if err != nil { - return err + return fmt.Errorf("alicloud: %v", err) } _, _, err = d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("alicloud: %v", err) } for _, rec := range records { @@ -90,7 +142,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { request.RecordId = rec.RecordId _, err = d.client.DeleteDomainRecord(request) if err != nil { - return err + return fmt.Errorf("alicloud: %v", err) } } return nil @@ -100,7 +152,7 @@ func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { request := alidns.CreateDescribeDomainsRequest() zones, err := d.client.DescribeDomains(request) if err != nil { - return "", "", fmt.Errorf("AliDNS: API call failed: %v", err) + return "", "", fmt.Errorf("API call failed: %v", err) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) @@ -116,18 +168,18 @@ func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { } if hostedZone.DomainId == "" { - return "", "", fmt.Errorf("AliDNS: zone %s not found in AliDNS for domain %s", authZone, domain) + return "", "", fmt.Errorf("zone %s not found in AliDNS for domain %s", authZone, domain) } return fmt.Sprintf("%v", hostedZone.DomainId), hostedZone.DomainName, nil } -func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *alidns.AddDomainRecordRequest { +func (d *DNSProvider) newTxtRecord(zone, fqdn, value string) *alidns.AddDomainRecordRequest { request := alidns.CreateAddDomainRecordRequest() request.Type = "TXT" request.DomainName = zone request.RR = d.extractRecordName(fqdn, zone) request.Value = value - request.TTL = requests.NewInteger(600) + request.TTL = requests.NewInteger(d.config.TTL) return request } @@ -145,7 +197,7 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]alidns.Record, erro result, err := d.client.DescribeDomainRecords(request) if err != nil { - return records, fmt.Errorf("AliDNS: API call has failed: %v", err) + return records, fmt.Errorf("API call has failed: %v", err) } recordName := d.extractRecordName(fqdn, zoneName) diff --git a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go index 5c821507b..5ddbc7af1 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go @@ -1,9 +1,11 @@ package auroradns import ( + "errors" "fmt" "os" "sync" + "time" "github.com/edeckers/auroradnsclient" "github.com/edeckers/auroradnsclient/records" @@ -12,68 +14,97 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +const defaultBaseURL = "https://api.auroradns.eu" + +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + UserID string + Key string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("AURORA_TTL", 300), + PropagationTimeout: env.GetOrDefaultSecond("AURORA_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("AURORA_POLLING_INTERVAL", acme.DefaultPollingInterval), + } +} + // DNSProvider describes a provider for AuroraDNS type DNSProvider struct { recordIDs map[string]string recordIDsMu sync.Mutex + config *Config client *auroradnsclient.AuroraDNSClient } // NewDNSProvider returns a DNSProvider instance configured for AuroraDNS. -// Credentials must be passed in the environment variables: AURORA_USER_ID -// and AURORA_KEY. +// Credentials must be passed in the environment variables: +// AURORA_USER_ID and AURORA_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("AURORA_USER_ID", "AURORA_KEY") if err != nil { - return nil, fmt.Errorf("AuroraDNS: %v", err) + return nil, fmt.Errorf("aurora: %v", err) } - endpoint := os.Getenv("AURORA_ENDPOINT") + config := NewDefaultConfig() + config.BaseURL = os.Getenv("AURORA_ENDPOINT") + config.UserID = values["AURORA_USER_ID"] + config.Key = values["AURORA_KEY"] - return NewDNSProviderCredentials(endpoint, values["AURORA_USER_ID"], values["AURORA_KEY"]) + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for AuroraDNS. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for AuroraDNS. +// Deprecated func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) { - if baseURL == "" { - baseURL = "https://api.auroradns.eu" + config := NewDefaultConfig() + config.BaseURL = baseURL + config.UserID = userID + config.Key = key + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for AuroraDNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("aurora: the configuration of the DNS provider is nil") } - client, err := auroradnsclient.NewAuroraDNSClient(baseURL, userID, key) + if config.UserID == "" || config.Key == "" { + return nil, errors.New("aurora: some credentials information are missing") + } + + if config.BaseURL == "" { + config.BaseURL = defaultBaseURL + } + + client, err := auroradnsclient.NewAuroraDNSClient(config.BaseURL, config.UserID, config.Key) if err != nil { - return nil, err + return nil, fmt.Errorf("aurora: %v", err) } return &DNSProvider{ + config: config, client: client, recordIDs: make(map[string]string), }, nil } -func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { - zs, err := d.client.GetZones() - - if err != nil { - return zones.ZoneRecord{}, err - } - - for _, element := range zs { - if element.Name == name { - return element, nil - } - } - - return zones.ZoneRecord{}, fmt.Errorf("could not find Zone record") -} - // Present creates a record with a secret func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + return fmt.Errorf("aurora: could not determine zone for domain: '%s'. %s", domain, err) } // 1. Aurora will happily create the TXT record when it is provided a fqdn, @@ -89,7 +120,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { zoneRecord, err := d.getZoneInformationByName(authZone) if err != nil { - return fmt.Errorf("could not create record: %v", err) + return fmt.Errorf("aurora: could not create record: %v", err) } reqData := @@ -97,12 +128,12 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { RecordType: "TXT", Name: subdomain, Content: value, - TTL: 300, + TTL: d.config.TTL, } respData, err := d.client.CreateRecord(zoneRecord.ID, reqData) if err != nil { - return fmt.Errorf("could not create record: %v", err) + return fmt.Errorf("aurora: could not create record: %v", err) } d.recordIDsMu.Lock() @@ -147,3 +178,24 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { + zs, err := d.client.GetZones() + if err != nil { + return zones.ZoneRecord{}, err + } + + for _, element := range zs { + if element.Name == name { + return element, nil + } + } + + return zones.ZoneRecord{}, fmt.Errorf("could not find Zone record") +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go index bbf2c7fb9..7f1569930 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go +++ b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "net/http" "strings" "time" @@ -19,14 +20,31 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + ClientID string + ClientSecret string + SubscriptionID string + TenantID string + ResourceGroup string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("AZURE_TTL", 60), + PropagationTimeout: env.GetOrDefaultSecond("AZURE_PROPAGATION_TIMEOUT", 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond("AZURE_POLLING_INTERVAL", 2*time.Second), + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { - clientID string - clientSecret string - subscriptionID string - tenantID string - resourceGroup string - context context.Context + config *Config } // NewDNSProvider returns a DNSProvider instance configured for azure. @@ -35,54 +53,66 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID", "AZURE_TENANT_ID", "AZURE_RESOURCE_GROUP") if err != nil { - return nil, fmt.Errorf("Azure: %v", err) + return nil, fmt.Errorf("azure: %v", err) } - return NewDNSProviderCredentials( - values["AZURE_CLIENT_ID"], - values["AZURE_CLIENT_SECRET"], - values["AZURE_SUBSCRIPTION_ID"], - values["AZURE_TENANT_ID"], - values["AZURE_RESOURCE_GROUP"], - ) + config := NewDefaultConfig() + config.ClientID = values["AZURE_CLIENT_ID"] + config.ClientSecret = values["AZURE_CLIENT_SECRET"] + config.SubscriptionID = values["AZURE_SUBSCRIPTION_ID"] + config.TenantID = values["AZURE_TENANT_ID"] + config.ResourceGroup = values["AZURE_RESOURCE_GROUP"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for azure. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for azure. +// Deprecated func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup string) (*DNSProvider, error) { - if clientID == "" || clientSecret == "" || subscriptionID == "" || tenantID == "" || resourceGroup == "" { - return nil, errors.New("Azure: some credentials information are missing") + config := NewDefaultConfig() + config.ClientID = clientID + config.ClientSecret = clientSecret + config.SubscriptionID = subscriptionID + config.TenantID = tenantID + config.ResourceGroup = resourceGroup + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Azure. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("azure: the configuration of the DNS provider is nil") } - return &DNSProvider{ - clientID: clientID, - clientSecret: clientSecret, - subscriptionID: subscriptionID, - tenantID: tenantID, - resourceGroup: resourceGroup, - // TODO: A timeout can be added here for cancellation purposes. - context: context.Background(), - }, nil + if config.ClientID == "" || config.ClientSecret == "" || config.SubscriptionID == "" || config.TenantID == "" || config.ResourceGroup == "" { + return nil, errors.New("azure: some credentials information are missing") + } + + return &DNSProvider{config: config}, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record to fulfil the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { + ctx := context.Background() fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := d.getHostedZoneID(fqdn) + + zone, err := d.getHostedZoneID(ctx, fqdn) if err != nil { - return err + return fmt.Errorf("azure: %v", err) } - rsc := dns.NewRecordSetsClient(d.subscriptionID) - spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + rsc := dns.NewRecordSetsClient(d.config.SubscriptionID) + spt, err := d.newServicePrincipalToken(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { - return err + return fmt.Errorf("azure: %v", err) } rsc.Authorizer = autorest.NewBearerAuthorizer(spt) @@ -91,59 +121,55 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { rec := dns.RecordSet{ Name: &relative, RecordSetProperties: &dns.RecordSetProperties{ - TTL: to.Int64Ptr(60), + TTL: to.Int64Ptr(int64(d.config.TTL)), TxtRecords: &[]dns.TxtRecord{{Value: &[]string{value}}}, }, } - _, err = rsc.CreateOrUpdate(d.context, d.resourceGroup, zone, relative, dns.TXT, rec, "", "") - return err -} - -// Returns the relative record to the domain -func toRelativeRecord(domain, zone string) string { - return acme.UnFqdn(strings.TrimSuffix(domain, zone)) + _, err = rsc.CreateOrUpdate(ctx, d.config.ResourceGroup, zone, relative, dns.TXT, rec, "", "") + return fmt.Errorf("azure: %v", err) } // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + ctx := context.Background() fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := d.getHostedZoneID(fqdn) + zone, err := d.getHostedZoneID(ctx, fqdn) if err != nil { - return err + return fmt.Errorf("azure: %v", err) } relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) - rsc := dns.NewRecordSetsClient(d.subscriptionID) - spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + rsc := dns.NewRecordSetsClient(d.config.SubscriptionID) + spt, err := d.newServicePrincipalToken(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { - return err + return fmt.Errorf("azure: %v", err) } rsc.Authorizer = autorest.NewBearerAuthorizer(spt) - _, err = rsc.Delete(d.context, d.resourceGroup, zone, relative, dns.TXT, "") - return err + _, err = rsc.Delete(ctx, d.config.ResourceGroup, zone, relative, dns.TXT, "") + return fmt.Errorf("azure: %v", err) } // Checks that azure has a zone for this domain name. -func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(ctx context.Context, fqdn string) (string, error) { authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { return "", err } // Now we want to to Azure and get the zone. - spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + spt, err := d.newServicePrincipalToken(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return "", err } - dc := dns.NewZonesClient(d.subscriptionID) + dc := dns.NewZonesClient(d.config.SubscriptionID) dc.Authorizer = autorest.NewBearerAuthorizer(spt) - zone, err := dc.Get(d.context, d.resourceGroup, acme.UnFqdn(authZone)) + zone, err := dc.Get(ctx, d.config.ResourceGroup, acme.UnFqdn(authZone)) if err != nil { return "", err } @@ -154,10 +180,15 @@ func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the // passed credentials map. -func (d *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, d.tenantID) +func (d *DNSProvider) newServicePrincipalToken(scope string) (*adal.ServicePrincipalToken, error) { + oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, d.config.TenantID) if err != nil { return nil, err } - return adal.NewServicePrincipalToken(*oauthConfig, d.clientID, d.clientSecret, scope) + return adal.NewServicePrincipalToken(*oauthConfig, d.config.ClientID, d.config.ClientSecret, scope) +} + +// Returns the relative record to the domain +func toRelativeRecord(domain, zone string) string { + return acme.UnFqdn(strings.TrimSuffix(domain, zone)) } diff --git a/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go b/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go index d88d73b1c..cc4369ba4 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go +++ b/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go @@ -5,6 +5,7 @@ package bluecat import ( "bytes" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -17,91 +18,221 @@ import ( "github.com/xenolf/lego/platform/config/env" ) -const bluecatURLTemplate = "%s/Services/REST/v1" const configType = "Configuration" const viewType = "View" const txtType = "TXTRecord" const zoneType = "Zone" -type entityResponse struct { - ID uint `json:"id"` - Name string `json:"name"` - Type string `json:"type"` - Properties string `json:"properties"` +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + UserName string + Password string + ConfigName string + DNSView string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("BLUECAT_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("BLUECAT_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("BLUECAT_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("BLUECAT_HTTP_TIMEOUT", 30*time.Second), + }, + } } // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses // Bluecat's Address Manager REST API to manage TXT records for a domain. type DNSProvider struct { - baseURL string - userName string - password string - configName string - dnsView string - token string - client *http.Client + config *Config + token string } // NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS. -// Credentials must be passed in the environment variables: BLUECAT_SERVER_URL, -// BLUECAT_USER_NAME and BLUECAT_PASSWORD. BLUECAT_SERVER_URL should have the -// scheme, hostname, and port (if required) of the authoritative Bluecat BAM -// server. The REST endpoint will be appended. In addition, the Configuration name -// and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and -// BLUECAT_DNS_VIEW +// Credentials must be passed in the environment variables: BLUECAT_SERVER_URL, BLUECAT_USER_NAME and BLUECAT_PASSWORD. +// BLUECAT_SERVER_URL should have the scheme, hostname, and port (if required) of the authoritative Bluecat BAM server. +// The REST endpoint will be appended. In addition, the Configuration name +// and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and BLUECAT_DNS_VIEW func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("BLUECAT_SERVER_URL", "BLUECAT_USER_NAME", "BLUECAT_CONFIG_NAME", "BLUECAT_CONFIG_NAME", "BLUECAT_DNS_VIEW") if err != nil { - return nil, fmt.Errorf("BlueCat: %v", err) + return nil, fmt.Errorf("bluecat: %v", err) } - httpClient := &http.Client{Timeout: 30 * time.Second} + config := NewDefaultConfig() + config.BaseURL = values["BLUECAT_SERVER_URL"] + config.UserName = values["BLUECAT_USER_NAME"] + config.Password = values["BLUECAT_PASSWORD"] + config.ConfigName = values["BLUECAT_CONFIG_NAME"] + config.DNSView = values["BLUECAT_DNS_VIEW"] - return NewDNSProviderCredentials( - values["BLUECAT_SERVER_URL"], - values["BLUECAT_USER_NAME"], - values["BLUECAT_PASSWORD"], - values["BLUECAT_CONFIG_NAME"], - values["BLUECAT_DNS_VIEW"], - httpClient, - ) + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Bluecat DNS. -func NewDNSProviderCredentials(server, userName, password, configName, dnsView string, httpClient *http.Client) (*DNSProvider, error) { - if server == "" || userName == "" || password == "" || configName == "" || dnsView == "" { - return nil, fmt.Errorf("Bluecat credentials missing") - } +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Bluecat DNS. +// Deprecated +func NewDNSProviderCredentials(baseURL, userName, password, configName, dnsView string, httpClient *http.Client) (*DNSProvider, error) { + config := NewDefaultConfig() + config.BaseURL = baseURL + config.UserName = userName + config.Password = password + config.ConfigName = configName + config.DNSView = dnsView - client := http.DefaultClient if httpClient != nil { - client = httpClient + config.HTTPClient = httpClient } - return &DNSProvider{ - baseURL: fmt.Sprintf(bluecatURLTemplate, server), - userName: userName, - password: password, - configName: configName, - dnsView: dnsView, - client: client, - }, nil + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Bluecat DNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("bluecat: the configuration of the DNS provider is nil") + } + + if config.BaseURL == "" || config.UserName == "" || config.Password == "" || config.ConfigName == "" || config.DNSView == "" { + return nil, fmt.Errorf("bluecat: credentials missing") + } + + return &DNSProvider{config: config}, nil +} + +// Present creates a TXT record using the specified parameters +// This will *not* create a subzone to contain the TXT record, +// so make sure the FQDN specified is within an extant zone. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + err := d.login() + if err != nil { + return err + } + + viewID, err := d.lookupViewID(d.config.DNSView) + if err != nil { + return err + } + + parentZoneID, name, err := d.lookupParentZoneID(viewID, fqdn) + if err != nil { + return err + } + + queryArgs := map[string]string{ + "parentId": strconv.FormatUint(uint64(parentZoneID), 10), + } + + body := bluecatEntity{ + Name: name, + Type: "TXTRecord", + Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", d.config.TTL, fqdn, value), + } + + resp, err := d.sendRequest(http.MethodPost, "addEntity", body, queryArgs) + if err != nil { + return err + } + defer resp.Body.Close() + + addTxtBytes, _ := ioutil.ReadAll(resp.Body) + addTxtResp := string(addTxtBytes) + // addEntity responds only with body text containing the ID of the created record + _, err = strconv.ParseUint(addTxtResp, 10, 64) + if err != nil { + return fmt.Errorf("bluecat: addEntity request failed: %s", addTxtResp) + } + + err = d.deploy(parentZoneID) + if err != nil { + return err + } + + return d.logout() +} + +// CleanUp removes the TXT record matching the specified parameters +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + + err := d.login() + if err != nil { + return err + } + + viewID, err := d.lookupViewID(d.config.DNSView) + if err != nil { + return err + } + + parentID, name, err := d.lookupParentZoneID(viewID, fqdn) + if err != nil { + return err + } + + queryArgs := map[string]string{ + "parentId": strconv.FormatUint(uint64(parentID), 10), + "name": name, + "type": txtType, + } + + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) + if err != nil { + return err + } + defer resp.Body.Close() + + var txtRec entityResponse + err = json.NewDecoder(resp.Body).Decode(&txtRec) + if err != nil { + return fmt.Errorf("bluecat: %v", err) + } + queryArgs = map[string]string{ + "objectId": strconv.FormatUint(uint64(txtRec.ID), 10), + } + + resp, err = d.sendRequest(http.MethodDelete, http.MethodDelete, nil, queryArgs) + if err != nil { + return err + } + defer resp.Body.Close() + + err = d.deploy(parentID) + if err != nil { + return err + } + + return d.logout() +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // Send a REST request, using query parameters specified. The Authorization // header will be set if we have an active auth token func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, queryArgs map[string]string) (*http.Response, error) { - url := fmt.Sprintf("%s/%s", d.baseURL, resource) + url := fmt.Sprintf("%s/Services/REST/v1/%s", d.config.BaseURL, resource) body, err := json.Marshal(payload) if err != nil { - return nil, err + return nil, fmt.Errorf("bluecat: %v", err) } req, err := http.NewRequest(method, url, bytes.NewReader(body)) if err != nil { - return nil, err + return nil, fmt.Errorf("bluecat: %v", err) } req.Header.Set("Content-Type", "application/json") if len(d.token) > 0 { @@ -114,15 +245,15 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, q.Add(argName, argVal) } req.URL.RawQuery = q.Encode() - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { - return nil, err + return nil, fmt.Errorf("bluecat: %v", err) } if resp.StatusCode >= 400 { errBytes, _ := ioutil.ReadAll(resp.Body) errResp := string(errBytes) - return nil, fmt.Errorf("Bluecat API request failed with HTTP status code %d\n Full message: %s", + return nil, fmt.Errorf("bluecat: request failed with HTTP status code %d\n Full message: %s", resp.StatusCode, errResp) } @@ -133,8 +264,8 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, // password and receives a token to be used in for subsequent requests. func (d *DNSProvider) login() error { queryArgs := map[string]string{ - "username": d.userName, - "password": d.password, + "username": d.config.UserName, + "password": d.config.Password, } resp, err := d.sendRequest(http.MethodGet, "login", nil, queryArgs) @@ -145,18 +276,16 @@ func (d *DNSProvider) login() error { authBytes, err := ioutil.ReadAll(resp.Body) if err != nil { - return err + return fmt.Errorf("bluecat: %v", err) } authResp := string(authBytes) if strings.Contains(authResp, "Authentication Error") { msg := strings.Trim(authResp, "\"") - return fmt.Errorf("Bluecat API request failed: %s", msg) + return fmt.Errorf("bluecat: request failed: %s", msg) } // Upon success, API responds with "Session Token-> BAMAuthToken: dQfuRMTUxNjc3MjcyNDg1ODppcGFybXM= <- for User : username" - re := regexp.MustCompile("BAMAuthToken: [^ ]+") - token := re.FindString(authResp) - d.token = token + d.token = regexp.MustCompile("BAMAuthToken: [^ ]+").FindString(authResp) return nil } @@ -174,7 +303,7 @@ func (d *DNSProvider) logout() error { defer resp.Body.Close() if resp.StatusCode != 200 { - return fmt.Errorf("Bluecat API request failed to delete session with HTTP status code %d", resp.StatusCode) + return fmt.Errorf("bluecat: request failed to delete session with HTTP status code %d", resp.StatusCode) } authBytes, err := ioutil.ReadAll(resp.Body) @@ -185,7 +314,7 @@ func (d *DNSProvider) logout() error { if !strings.Contains(authResp, "successfully") { msg := strings.Trim(authResp, "\"") - return fmt.Errorf("Bluecat API request failed to delete session: %s", msg) + return fmt.Errorf("bluecat: request failed to delete session: %s", msg) } d.token = "" @@ -197,7 +326,7 @@ func (d *DNSProvider) logout() error { func (d *DNSProvider) lookupConfID() (uint, error) { queryArgs := map[string]string{ "parentId": strconv.Itoa(0), - "name": d.configName, + "name": d.config.ConfigName, "type": configType, } @@ -210,7 +339,7 @@ func (d *DNSProvider) lookupConfID() (uint, error) { var conf entityResponse err = json.NewDecoder(resp.Body).Decode(&conf) if err != nil { - return 0, err + return 0, fmt.Errorf("bluecat: %v", err) } return conf.ID, nil } @@ -224,7 +353,7 @@ func (d *DNSProvider) lookupViewID(viewName string) (uint, error) { queryArgs := map[string]string{ "parentId": strconv.FormatUint(uint64(confID), 10), - "name": d.dnsView, + "name": d.config.DNSView, "type": viewType, } @@ -237,7 +366,7 @@ func (d *DNSProvider) lookupViewID(viewName string) (uint, error) { var view entityResponse err = json.NewDecoder(resp.Body).Decode(&view) if err != nil { - return 0, err + return 0, fmt.Errorf("bluecat: %v", err) } return view.ID, nil @@ -280,7 +409,7 @@ func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) { resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) // Return an empty zone if the named zone doesn't exist if resp != nil && resp.StatusCode == 404 { - return 0, fmt.Errorf("Bluecat API could not find zone named %s", name) + return 0, fmt.Errorf("bluecat: could not find zone named %s", name) } if err != nil { return 0, err @@ -290,65 +419,12 @@ func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) { var zone entityResponse err = json.NewDecoder(resp.Body).Decode(&zone) if err != nil { - return 0, err + return 0, fmt.Errorf("bluecat: %v", err) } return zone.ID, nil } -// Present creates a TXT record using the specified parameters -// This will *not* create a subzone to contain the TXT record, -// so make sure the FQDN specified is within an extant zone. -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - err := d.login() - if err != nil { - return err - } - - viewID, err := d.lookupViewID(d.dnsView) - if err != nil { - return err - } - - parentZoneID, name, err := d.lookupParentZoneID(viewID, fqdn) - if err != nil { - return err - } - - queryArgs := map[string]string{ - "parentId": strconv.FormatUint(uint64(parentZoneID), 10), - } - - body := bluecatEntity{ - Name: name, - Type: "TXTRecord", - Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", ttl, fqdn, value), - } - - resp, err := d.sendRequest(http.MethodPost, "addEntity", body, queryArgs) - if err != nil { - return err - } - defer resp.Body.Close() - - addTxtBytes, _ := ioutil.ReadAll(resp.Body) - addTxtResp := string(addTxtBytes) - // addEntity responds only with body text containing the ID of the created record - _, err = strconv.ParseUint(addTxtResp, 10, 64) - if err != nil { - return fmt.Errorf("Bluecat API addEntity request failed: %s", addTxtResp) - } - - err = d.deploy(parentZoneID) - if err != nil { - return err - } - - return d.logout() -} - // Deploy the DNS config for the specified entity to the authoritative servers func (d *DNSProvider) deploy(entityID uint) error { queryArgs := map[string]string{ @@ -363,65 +439,3 @@ func (d *DNSProvider) deploy(entityID uint) error { return nil } - -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - err := d.login() - if err != nil { - return err - } - - viewID, err := d.lookupViewID(d.dnsView) - if err != nil { - return err - } - - parentID, name, err := d.lookupParentZoneID(viewID, fqdn) - if err != nil { - return err - } - - queryArgs := map[string]string{ - "parentId": strconv.FormatUint(uint64(parentID), 10), - "name": name, - "type": txtType, - } - - resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) - if err != nil { - return err - } - defer resp.Body.Close() - - var txtRec entityResponse - err = json.NewDecoder(resp.Body).Decode(&txtRec) - if err != nil { - return err - } - queryArgs = map[string]string{ - "objectId": strconv.FormatUint(uint64(txtRec.ID), 10), - } - - resp, err = d.sendRequest(http.MethodDelete, http.MethodDelete, nil, queryArgs) - if err != nil { - return err - } - defer resp.Body.Close() - - err = d.deploy(parentID) - if err != nil { - return err - } - - return d.logout() -} - -// JSON body for Bluecat entity requests and responses -type bluecatEntity struct { - ID string `json:"id,omitempty"` - Name string `json:"name"` - Type string `json:"type"` - Properties string `json:"properties"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go b/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go new file mode 100644 index 000000000..55deeed45 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/bluecat/client.go @@ -0,0 +1,16 @@ +package bluecat + +// JSON body for Bluecat entity requests and responses +type bluecatEntity struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Type string `json:"type"` + Properties string `json:"properties"` +} + +type entityResponse struct { + ID uint `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Properties string `json:"properties"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/client.go b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/client.go new file mode 100644 index 000000000..24c4ae0f8 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/client.go @@ -0,0 +1,212 @@ +package cloudflare + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + + "github.com/xenolf/lego/acme" +) + +// defaultBaseURL represents the API endpoint to call. +const defaultBaseURL = "https://api.cloudflare.com/client/v4" + +// APIError contains error details for failed requests +type APIError struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + ErrorChain []APIError `json:"error_chain,omitempty"` +} + +// APIResponse represents a response from Cloudflare API +type APIResponse struct { + Success bool `json:"success"` + Errors []*APIError `json:"errors"` + Result json.RawMessage `json:"result"` +} + +// TxtRecord represents a Cloudflare DNS record +type TxtRecord struct { + Name string `json:"name"` + Type string `json:"type"` + Content string `json:"content"` + ID string `json:"id,omitempty"` + TTL int `json:"ttl,omitempty"` + ZoneID string `json:"zone_id,omitempty"` +} + +// HostedZone represents a Cloudflare DNS zone +type HostedZone struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// Client Cloudflare API client +type Client struct { + authEmail string + authKey string + BaseURL string + HTTPClient *http.Client +} + +// NewClient create a Cloudflare API client +func NewClient(authEmail string, authKey string) (*Client, error) { + if authEmail == "" { + return nil, errors.New("cloudflare: some credentials information are missing: email") + } + + if authKey == "" { + return nil, errors.New("cloudflare: some credentials information are missing: key") + } + + return &Client{ + authEmail: authEmail, + authKey: authKey, + BaseURL: defaultBaseURL, + HTTPClient: http.DefaultClient, + }, nil +} + +// GetHostedZoneID get hosted zone +func (c *Client) GetHostedZoneID(fqdn string) (string, error) { + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return "", err + } + + result, err := c.doRequest(http.MethodGet, "/zones?name="+acme.UnFqdn(authZone), nil) + if err != nil { + return "", err + } + + var hostedZone []HostedZone + err = json.Unmarshal(result, &hostedZone) + if err != nil { + return "", fmt.Errorf("cloudflare: HostedZone unmarshaling error: %v", err) + } + + count := len(hostedZone) + if count == 0 { + return "", fmt.Errorf("cloudflare: zone %s not found for domain %s", authZone, fqdn) + } else if count > 1 { + return "", fmt.Errorf("cloudflare: zone %s cannot be find for domain %s: too many hostedZone: %v", authZone, fqdn, hostedZone) + } + + return hostedZone[0].ID, nil +} + +// FindTxtRecord Find a TXT record +func (c *Client) FindTxtRecord(zoneID, fqdn string) (*TxtRecord, error) { + result, err := c.doRequest( + http.MethodGet, + fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), + nil, + ) + if err != nil { + return nil, err + } + + var records []TxtRecord + err = json.Unmarshal(result, &records) + if err != nil { + return nil, fmt.Errorf("cloudflare: record unmarshaling error: %v", err) + } + + for _, rec := range records { + fmt.Println(rec.Name, acme.UnFqdn(fqdn)) + if rec.Name == acme.UnFqdn(fqdn) { + return &rec, nil + } + } + + return nil, fmt.Errorf("cloudflare: no existing record found for %s", fqdn) +} + +// AddTxtRecord add a TXT record +func (c *Client) AddTxtRecord(fqdn string, record TxtRecord) error { + zoneID, err := c.GetHostedZoneID(fqdn) + if err != nil { + return err + } + + body, err := json.Marshal(record) + if err != nil { + return fmt.Errorf("cloudflare: record marshaling error: %v", err) + } + + _, err = c.doRequest(http.MethodPost, fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) + return err +} + +// RemoveTxtRecord Remove a TXT record +func (c *Client) RemoveTxtRecord(fqdn string) error { + zoneID, err := c.GetHostedZoneID(fqdn) + if err != nil { + return err + } + + record, err := c.FindTxtRecord(zoneID, fqdn) + if err != nil { + return err + } + + _, err = c.doRequest(http.MethodDelete, fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) + return err +} + +func (c *Client) doRequest(method, uri string, body io.Reader) (json.RawMessage, error) { + req, err := http.NewRequest(method, fmt.Sprintf("%s%s", c.BaseURL, uri), body) + if err != nil { + return nil, err + } + + req.Header.Set("X-Auth-Email", c.authEmail) + req.Header.Set("X-Auth-Key", c.authKey) + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("cloudflare: error querying API: %v", err) + } + + defer resp.Body.Close() + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("cloudflare: %s", toUnreadableBodyMessage(req, content)) + } + + var r APIResponse + err = json.Unmarshal(content, &r) + if err != nil { + return nil, fmt.Errorf("cloudflare: APIResponse unmarshaling error: %v: %s", err, toUnreadableBodyMessage(req, content)) + } + + if !r.Success { + if len(r.Errors) > 0 { + return nil, fmt.Errorf("cloudflare: error \n%s", toError(r)) + } + + return nil, fmt.Errorf("cloudflare: %s", toUnreadableBodyMessage(req, content)) + } + + return r.Result, nil +} + +func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { + return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) +} + +func toError(r APIResponse) error { + errStr := "" + for _, apiErr := range r.Errors { + errStr += fmt.Sprintf("\t Error: %d: %s", apiErr.Code, apiErr.Message) + for _, chainErr := range apiErr.ErrorChain { + errStr += fmt.Sprintf("<- %d: %s", chainErr.Code, chainErr.Message) + } + } + return fmt.Errorf("cloudflare: error \n%s", errStr) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go index 7d8979ef5..25d6fef3c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go @@ -3,12 +3,8 @@ package cloudflare import ( - "bytes" - "encoding/json" "errors" "fmt" - "io" - "io/ioutil" "net/http" "time" @@ -17,208 +13,108 @@ import ( ) // CloudFlareAPIURL represents the API endpoint to call. -// TODO: Unexport? -const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" +const CloudFlareAPIURL = defaultBaseURL // Deprecated + +// Config is used to configure the creation of the DNSProvider +type Config struct { + AuthEmail string + AuthKey string + TTL int + PropagationTimeout time.Duration + PollingInterval time.Duration + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("CLOUDFLARE_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("CLOUDFLARE_PROPAGATION_TIMEOUT", 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond("CLOUDFLARE_POLLING_INTERVAL", 2*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("CLOUDFLARE_HTTP_TIMEOUT", 30*time.Second), + }, + } +} // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { - authEmail string - authKey string - client *http.Client + client *Client + config *Config } -// NewDNSProvider returns a DNSProvider instance configured for cloudflare. -// Credentials must be passed in the environment variables: CLOUDFLARE_EMAIL -// and CLOUDFLARE_API_KEY. +// NewDNSProvider returns a DNSProvider instance configured for Cloudflare. +// Credentials must be passed in the environment variables: +// CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("CLOUDFLARE_EMAIL", "CLOUDFLARE_API_KEY") if err != nil { - return nil, fmt.Errorf("CloudFlare: %v", err) + return nil, fmt.Errorf("cloudflare: %v", err) } - return NewDNSProviderCredentials(values["CLOUDFLARE_EMAIL"], values["CLOUDFLARE_API_KEY"]) + config := NewDefaultConfig() + config.AuthEmail = values["CLOUDFLARE_EMAIL"] + config.AuthKey = values["CLOUDFLARE_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for cloudflare. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Cloudflare. +// Deprecated func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { - if email == "" || key == "" { - return nil, errors.New("CloudFlare: some credentials information are missing") + config := NewDefaultConfig() + config.AuthEmail = email + config.AuthKey = key + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Cloudflare. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("cloudflare: the configuration of the DNS provider is nil") } + client, err := NewClient(config.AuthEmail, config.AuthKey) + if err != nil { + return nil, err + } + + client.HTTPClient = config.HTTPClient + + // TODO: must be remove. keep only for compatibility reason. + client.BaseURL = CloudFlareAPIURL + return &DNSProvider{ - authEmail: email, - authKey: key, - client: &http.Client{Timeout: 30 * time.Second}, + client: client, + config: config, }, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record to fulfil the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, err := d.getHostedZoneID(fqdn) - if err != nil { - return err - } + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - rec := cloudFlareRecord{ + rec := TxtRecord{ Type: "TXT", Name: acme.UnFqdn(fqdn), Content: value, - TTL: ttl, + TTL: d.config.TTL, } - body, err := json.Marshal(rec) - if err != nil { - return err - } - - _, err = d.doRequest(http.MethodPost, fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) - return err + return d.client.AddTxtRecord(fqdn, rec) } // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - record, err := d.findTxtRecord(fqdn) - if err != nil { - return err - } - - _, err = d.doRequest(http.MethodDelete, fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) - return err -} - -func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { - // HostedZone represents a CloudFlare DNS zone - type HostedZone struct { - ID string `json:"id"` - Name string `json:"name"` - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return "", err - } - - result, err := d.doRequest(http.MethodGet, "/zones?name="+acme.UnFqdn(authZone), nil) - if err != nil { - return "", err - } - - var hostedZone []HostedZone - err = json.Unmarshal(result, &hostedZone) - if err != nil { - return "", err - } - - if len(hostedZone) != 1 { - return "", fmt.Errorf("zone %s not found in CloudFlare for domain %s", authZone, fqdn) - } - - return hostedZone[0].ID, nil -} - -func (d *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { - zoneID, err := d.getHostedZoneID(fqdn) - if err != nil { - return nil, err - } - - result, err := d.doRequest( - http.MethodGet, - fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), - nil, - ) - if err != nil { - return nil, err - } - - var records []cloudFlareRecord - err = json.Unmarshal(result, &records) - if err != nil { - return nil, err - } - - for _, rec := range records { - if rec.Name == acme.UnFqdn(fqdn) { - return &rec, nil - } - } - - return nil, fmt.Errorf("no existing record found for %s", fqdn) -} - -func (d *DNSProvider) doRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - req, err := http.NewRequest(method, fmt.Sprintf("%s%s", CloudFlareAPIURL, uri), body) - if err != nil { - return nil, err - } - - req.Header.Set("X-Auth-Email", d.authEmail) - req.Header.Set("X-Auth-Key", d.authKey) - - resp, err := d.client.Do(req) - if err != nil { - return nil, fmt.Errorf("error querying Cloudflare API -> %v", err) - } - - defer resp.Body.Close() - - var r APIResponse - err = json.NewDecoder(resp.Body).Decode(&r) - if err != nil { - return nil, err - } - - if !r.Success { - if len(r.Errors) > 0 { - errStr := "" - for _, apiErr := range r.Errors { - errStr += fmt.Sprintf("\t Error: %d: %s", apiErr.Code, apiErr.Message) - for _, chainErr := range apiErr.ErrorChain { - errStr += fmt.Sprintf("<- %d: %s", chainErr.Code, chainErr.Message) - } - } - return nil, fmt.Errorf("Cloudflare API Error \n%s", errStr) - } - strBody := "Unreadable body" - if body, err := ioutil.ReadAll(resp.Body); err == nil { - strBody = string(body) - } - return nil, fmt.Errorf("Cloudflare API error: the request %s sent a response with a body which is not in JSON format: %s", req.URL.String(), strBody) - } - - return r.Result, nil -} - -// APIError contains error details for failed requests -type APIError struct { - Code int `json:"code,omitempty"` - Message string `json:"message,omitempty"` - ErrorChain []APIError `json:"error_chain,omitempty"` -} - -// APIResponse represents a response from CloudFlare API -type APIResponse struct { - Success bool `json:"success"` - Errors []*APIError `json:"errors"` - Result json.RawMessage `json:"result"` -} - -// cloudFlareRecord represents a CloudFlare DNS record -type cloudFlareRecord struct { - Name string `json:"name"` - Type string `json:"type"` - Content string `json:"content"` - ID string `json:"id,omitempty"` - TTL int `json:"ttl,omitempty"` - ZoneID string `json:"zone_id,omitempty"` + return d.client.RemoveTxtRecord(fqdn) } diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go new file mode 100644 index 000000000..4cf1dd201 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/client.go @@ -0,0 +1,208 @@ +package cloudxns + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strconv" + "strings" + "time" + + "github.com/xenolf/lego/acme" +) + +const defaultBaseURL = "https://www.cloudxns.net/api2/" + +type apiResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data json.RawMessage `json:"data,omitempty"` +} + +// Data Domain information +type Data struct { + ID string `json:"id"` + Domain string `json:"domain"` + TTL int `json:"ttl,omitempty"` +} + +// TXTRecord a TXT record +type TXTRecord struct { + ID int `json:"domain_id,omitempty"` + RecordID string `json:"record_id,omitempty"` + + Host string `json:"host"` + Value string `json:"value"` + Type string `json:"type"` + LineID int `json:"line_id,string"` + TTL int `json:"ttl,string"` +} + +// NewClient creates a CloudXNS client +func NewClient(apiKey string, secretKey string) (*Client, error) { + if apiKey == "" { + return nil, fmt.Errorf("CloudXNS: credentials missing: apiKey") + } + + if secretKey == "" { + return nil, fmt.Errorf("CloudXNS: credentials missing: secretKey") + } + + return &Client{ + apiKey: apiKey, + secretKey: secretKey, + HTTPClient: &http.Client{}, + BaseURL: defaultBaseURL, + }, nil +} + +// Client CloudXNS client +type Client struct { + apiKey string + secretKey string + HTTPClient *http.Client + BaseURL string +} + +// GetDomainInformation Get domain name information for a FQDN +func (c *Client) GetDomainInformation(fqdn string) (*Data, error) { + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return nil, err + } + + result, err := c.doRequest(http.MethodGet, "domain", nil) + if err != nil { + return nil, err + } + + var domains []Data + if len(result) > 0 { + err = json.Unmarshal(result, &domains) + if err != nil { + return nil, fmt.Errorf("CloudXNS: domains unmarshaling error: %v", err) + } + } + + for _, data := range domains { + if data.Domain == authZone { + return &data, nil + } + } + + return nil, fmt.Errorf("CloudXNS: zone %s not found for domain %s", authZone, fqdn) +} + +// FindTxtRecord return the TXT record a zone ID and a FQDN +func (c *Client) FindTxtRecord(zoneID, fqdn string) (*TXTRecord, error) { + result, err := c.doRequest(http.MethodGet, fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil) + if err != nil { + return nil, err + } + + var records []TXTRecord + err = json.Unmarshal(result, &records) + if err != nil { + return nil, fmt.Errorf("CloudXNS: TXT record unmarshaling error: %v", err) + } + + for _, record := range records { + if record.Host == acme.UnFqdn(fqdn) && record.Type == "TXT" { + return &record, nil + } + } + + return nil, fmt.Errorf("CloudXNS: no existing record found for %q", fqdn) +} + +// AddTxtRecord add a TXT record +func (c *Client) AddTxtRecord(info *Data, fqdn, value string, ttl int) error { + id, err := strconv.Atoi(info.ID) + if err != nil { + return fmt.Errorf("CloudXNS: invalid zone ID: %v", err) + } + + payload := TXTRecord{ + ID: id, + Host: acme.UnFqdn(strings.TrimSuffix(fqdn, info.Domain)), + Value: value, + Type: "TXT", + LineID: 1, + TTL: ttl, + } + + body, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("CloudXNS: record unmarshaling error: %v", err) + } + + _, err = c.doRequest(http.MethodPost, "record", body) + return err +} + +// RemoveTxtRecord remove a TXT record +func (c *Client) RemoveTxtRecord(recordID, zoneID string) error { + _, err := c.doRequest(http.MethodDelete, fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) + return err +} + +func (c *Client) doRequest(method, uri string, body []byte) (json.RawMessage, error) { + req, err := c.buildRequest(method, uri, body) + if err != nil { + return nil, err + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("CloudXNS: %v", err) + } + + defer resp.Body.Close() + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("CloudXNS: %s", toUnreadableBodyMessage(req, content)) + } + + var r apiResponse + err = json.Unmarshal(content, &r) + if err != nil { + return nil, fmt.Errorf("CloudXNS: response unmashaling error: %v: %s", err, toUnreadableBodyMessage(req, content)) + } + + if r.Code != 1 { + return nil, fmt.Errorf("CloudXNS: invalid code (%v), error: %s", r.Code, r.Message) + } + return r.Data, nil +} + +func (c *Client) buildRequest(method, uri string, body []byte) (*http.Request, error) { + url := c.BaseURL + uri + + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("CloudXNS: invalid request: %v", err) + } + + requestDate := time.Now().Format(time.RFC1123Z) + + req.Header.Set("API-KEY", c.apiKey) + req.Header.Set("API-REQUEST-DATE", requestDate) + req.Header.Set("API-HMAC", c.hmac(url, requestDate, string(body))) + req.Header.Set("API-FORMAT", "json") + + return req, nil +} + +func (c *Client) hmac(url, date, body string) string { + sum := md5.Sum([]byte(c.apiKey + url + body + date + c.secretKey)) + return hex.EncodeToString(sum[:]) +} + +func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { + return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go index ae819f12c..4b73564a8 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go @@ -1,32 +1,49 @@ // Package cloudxns implements a DNS provider for solving the DNS-01 challenge -// using cloudxns DNS. +// using CloudXNS DNS. package cloudxns import ( - "bytes" - "crypto/md5" - "encoding/hex" - "encoding/json" + "errors" "fmt" "net/http" - "strconv" "time" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) -const cloudXNSBaseURL = "https://www.cloudxns.net/api2/" +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + SecretKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + client := acme.HTTPClient + client.Timeout = time.Second * time.Duration(env.GetOrDefaultInt("CLOUDXNS_HTTP_TIMEOUT", 30)) + + return &Config{ + PropagationTimeout: env.GetOrDefaultSecond("AKAMAI_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("AKAMAI_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("CLOUDXNS_TTL", 120), + HTTPClient: &client, + } +} // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { - apiKey string - secretKey string + config *Config + client *Client } -// NewDNSProvider returns a DNSProvider instance configured for cloudxns. -// Credentials must be passed in the environment variables: CLOUDXNS_API_KEY -// and CLOUDXNS_SECRET_KEY. +// NewDNSProvider returns a DNSProvider instance configured for CloudXNS. +// Credentials must be passed in the environment variables: +// CLOUDXNS_API_KEY and CLOUDXNS_SECRET_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("CLOUDXNS_API_KEY", "CLOUDXNS_SECRET_KEY") if err != nil { @@ -37,177 +54,62 @@ func NewDNSProvider() (*DNSProvider, error) { } // NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for cloudxns. +// DNSProvider instance configured for CloudXNS. func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) { - if apiKey == "" || secretKey == "" { - return nil, fmt.Errorf("CloudXNS credentials missing") + config := NewDefaultConfig() + config.APIKey = apiKey + config.SecretKey = secretKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for CloudXNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("CloudXNS: the configuration of the DNS provider is nil") } - return &DNSProvider{ - apiKey: apiKey, - secretKey: secretKey, - }, nil + client, err := NewClient(config.APIKey, config.SecretKey) + if err != nil { + return nil, err + } + + client.HTTPClient = config.HTTPClient + + return &DNSProvider{client: client}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, err := d.getHostedZoneID(fqdn) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + info, err := d.client.GetDomainInformation(fqdn) if err != nil { return err } - return d.addTxtRecord(zoneID, fqdn, value, ttl) + return d.client.AddTxtRecord(info, fqdn, value, d.config.TTL) } // CleanUp removes the TXT record matching the specified parameters. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := d.getHostedZoneID(fqdn) + + info, err := d.client.GetDomainInformation(fqdn) if err != nil { return err } - recordID, err := d.findTxtRecord(zoneID, fqdn) + record, err := d.client.FindTxtRecord(info.ID, fqdn) if err != nil { return err } - return d.delTxtRecord(recordID, zoneID) + return d.client.RemoveTxtRecord(record.RecordID, info.ID) } -func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { - type Data struct { - ID string `json:"id"` - Domain string `json:"domain"` - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return "", err - } - - result, err := d.makeRequest(http.MethodGet, "domain", nil) - if err != nil { - return "", err - } - - var domains []Data - err = json.Unmarshal(result, &domains) - if err != nil { - return "", err - } - - for _, data := range domains { - if data.Domain == authZone { - return data.ID, nil - } - } - - return "", fmt.Errorf("zone %s not found in cloudxns for domain %s", authZone, fqdn) -} - -func (d *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { - result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil) - if err != nil { - return "", err - } - - var records []cloudXNSRecord - err = json.Unmarshal(result, &records) - if err != nil { - return "", err - } - - for _, record := range records { - if record.Host == acme.UnFqdn(fqdn) && record.Type == "TXT" { - return record.RecordID, nil - } - } - - return "", fmt.Errorf("no existing record found for %s", fqdn) -} - -func (d *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { - id, err := strconv.Atoi(zoneID) - if err != nil { - return err - } - - payload := cloudXNSRecord{ - ID: id, - Host: acme.UnFqdn(fqdn), - Value: value, - Type: "TXT", - LineID: 1, - TTL: ttl, - } - - body, err := json.Marshal(payload) - if err != nil { - return err - } - - _, err = d.makeRequest(http.MethodPost, "record", body) - return err -} - -func (d *DNSProvider) delTxtRecord(recordID, zoneID string) error { - _, err := d.makeRequest(http.MethodDelete, fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) - return err -} - -func (d *DNSProvider) hmac(url, date, body string) string { - sum := md5.Sum([]byte(d.apiKey + url + body + date + d.secretKey)) - return hex.EncodeToString(sum[:]) -} - -func (d *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) { - type APIResponse struct { - Code int `json:"code"` - Message string `json:"message"` - Data json.RawMessage `json:"data,omitempty"` - } - - url := cloudXNSBaseURL + uri - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - - requestDate := time.Now().Format(time.RFC1123Z) - - req.Header.Set("API-KEY", d.apiKey) - req.Header.Set("API-REQUEST-DATE", requestDate) - req.Header.Set("API-HMAC", d.hmac(url, requestDate, string(body))) - req.Header.Set("API-FORMAT", "json") - - resp, err := acme.HTTPClient.Do(req) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - var r APIResponse - err = json.NewDecoder(resp.Body).Decode(&r) - if err != nil { - return nil, err - } - - if r.Code != 1 { - return nil, fmt.Errorf("CloudXNS API Error: %s", r.Message) - } - return r.Data, nil -} - -type cloudXNSRecord struct { - ID int `json:"domain_id,omitempty"` - RecordID string `json:"record_id,omitempty"` - - Host string `json:"host"` - Value string `json:"value"` - Type string `json:"type"` - LineID int `json:"line_id,string"` - TTL int `json:"ttl,string"` +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } diff --git a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go new file mode 100644 index 000000000..d2f6c95f8 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/client.go @@ -0,0 +1,26 @@ +package digitalocean + +const defaultBaseURL = "https://api.digitalocean.com" + +// txtRecordRequest represents the request body to DO's API to make a TXT record +type txtRecordRequest struct { + RecordType string `json:"type"` + Name string `json:"name"` + Data string `json:"data"` + TTL int `json:"ttl"` +} + +// txtRecordResponse represents a response from DO's API after making a TXT record +type txtRecordResponse struct { + DomainRecord struct { + ID int `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Data string `json:"data"` + } `json:"domain_record"` +} + +type digitalOceanAPIError struct { + ID string `json:"id"` + Message string `json:"message"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go index e4247046a..97620dbf3 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go +++ b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go @@ -5,7 +5,10 @@ package digitalocean import ( "bytes" "encoding/json" + "errors" "fmt" + "io" + "io/ioutil" "net/http" "sync" "time" @@ -14,13 +17,35 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + AuthToken string + TTL int + PropagationTimeout time.Duration + PollingInterval time.Duration + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + BaseURL: defaultBaseURL, + TTL: env.GetOrDefaultInt("DO_TTL", 30), + PropagationTimeout: env.GetOrDefaultSecond("DO_PROPAGATION_TIMEOUT", 60*time.Second), + PollingInterval: env.GetOrDefaultSecond("DO_POLLING_INTERVAL", 5*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("DO_HTTP_TIMEOUT", 30*time.Second), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface // that uses DigitalOcean's REST API to manage TXT records for a domain. type DNSProvider struct { - apiAuthToken string - recordIDs map[string]int - recordIDsMu sync.Mutex - client *http.Client + config *Config + recordIDs map[string]int + recordIDsMu sync.Mutex } // NewDNSProvider returns a DNSProvider instance configured for Digital @@ -29,74 +54,60 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("DO_AUTH_TOKEN") if err != nil { - return nil, fmt.Errorf("DigitalOcean: %v", err) + return nil, fmt.Errorf("digitalocean: %v", err) } - return NewDNSProviderCredentials(values["DO_AUTH_TOKEN"]) + config := NewDefaultConfig() + config.AuthToken = values["DO_AUTH_TOKEN"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Digital Ocean. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Digital Ocean. +// Deprecated func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) { - if apiAuthToken == "" { - return nil, fmt.Errorf("DigitalOcean credentials missing") + config := NewDefaultConfig() + config.AuthToken = apiAuthToken + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Digital Ocean. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("digitalocean: the configuration of the DNS provider is nil") } + + if config.AuthToken == "" { + return nil, fmt.Errorf("digitalocean: credentials missing") + } + + if config.BaseURL == "" { + config.BaseURL = defaultBaseURL + } + return &DNSProvider{ - apiAuthToken: apiAuthToken, - recordIDs: make(map[string]int), - client: &http.Client{Timeout: 30 * time.Second}, + config: config, + recordIDs: make(map[string]int), }, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 60 * time.Second, 5 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + respData, err := d.addTxtRecord(domain, fqdn, value) if err != nil { - return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + return fmt.Errorf("digitalocean: %v", err) } - authZone = acme.UnFqdn(authZone) - - reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, authZone) - reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value, TTL: 30} - body, err := json.Marshal(reqData) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) - - resp, err := d.client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - var errInfo digitalOceanAPIError - json.NewDecoder(resp.Body).Decode(&errInfo) - return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) - } - - // Everything looks good; but we'll need the ID later to delete the record - var respData txtRecordResponse - err = json.NewDecoder(resp.Body).Decode(&respData) - if err != nil { - return err - } d.recordIDsMu.Lock() d.recordIDs[fqdn] = respData.DomainRecord.ID d.recordIDsMu.Unlock() @@ -113,35 +124,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { - return fmt.Errorf("unknown record ID for '%s'", fqdn) + return fmt.Errorf("digitalocean: unknown record ID for '%s'", fqdn) } - authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + err := d.removeTxtRecord(domain, recordID) if err != nil { - return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) - } - - authZone = acme.UnFqdn(authZone) - - reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID) - req, err := http.NewRequest(http.MethodDelete, reqURL, nil) - if err != nil { - return err - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) - - resp, err := d.client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - var errInfo digitalOceanAPIError - json.NewDecoder(resp.Body).Decode(&errInfo) - return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) + return fmt.Errorf("digitalocean: %v", err) } // Delete record ID from map @@ -152,27 +140,101 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -type digitalOceanAPIError struct { - ID string `json:"id"` - Message string `json:"message"` +func (d *DNSProvider) removeTxtRecord(domain string, recordID int) error { + authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + } + + reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", d.config.BaseURL, acme.UnFqdn(authZone), recordID) + req, err := d.newRequest(http.MethodDelete, reqURL, nil) + if err != nil { + return err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return readError(req, resp) + } + + return nil } -var digitalOceanBaseURL = "https://api.digitalocean.com" +func (d *DNSProvider) addTxtRecord(domain, fqdn, value string) (*txtRecordResponse, error) { + authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + if err != nil { + return nil, fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + } -// txtRecordRequest represents the request body to DO's API to make a TXT record -type txtRecordRequest struct { - RecordType string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - TTL int `json:"ttl"` + reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value, TTL: d.config.TTL} + body, err := json.Marshal(reqData) + if err != nil { + return nil, err + } + + reqURL := fmt.Sprintf("%s/v2/domains/%s/records", d.config.BaseURL, acme.UnFqdn(authZone)) + req, err := d.newRequest(http.MethodPost, reqURL, bytes.NewReader(body)) + if err != nil { + return nil, err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, readError(req, resp) + } + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.New(toUnreadableBodyMessage(req, content)) + } + + // Everything looks good; but we'll need the ID later to delete the record + respData := &txtRecordResponse{} + err = json.Unmarshal(content, respData) + if err != nil { + return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(req, content)) + } + + return respData, nil } -// txtRecordResponse represents a response from DO's API after making a TXT record -type txtRecordResponse struct { - DomainRecord struct { - ID int `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Data string `json:"data"` - } `json:"domain_record"` +func (d *DNSProvider) newRequest(method, reqURL string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, reqURL, body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.config.AuthToken)) + + return req, nil +} + +func readError(req *http.Request, resp *http.Response) error { + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return errors.New(toUnreadableBodyMessage(req, content)) + } + + var errInfo digitalOceanAPIError + err = json.Unmarshal(content, &errInfo) + if err != nil { + return fmt.Errorf("digitalOceanAPIError unmarshaling error: %v: %s", err, toUnreadableBodyMessage(req, content)) + } + + return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) +} + +func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { + return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) } diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go index 5ef34da5c..38a1fd4c2 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go @@ -25,6 +25,7 @@ import ( "github.com/xenolf/lego/providers/dns/gcloud" "github.com/xenolf/lego/providers/dns/glesys" "github.com/xenolf/lego/providers/dns/godaddy" + "github.com/xenolf/lego/providers/dns/hostingde" "github.com/xenolf/lego/providers/dns/iij" "github.com/xenolf/lego/providers/dns/lightsail" "github.com/xenolf/lego/providers/dns/linode" @@ -87,6 +88,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) return gcloud.NewDNSProvider() case "godaddy": return godaddy.NewDNSProvider() + case "hostingde": + return hostingde.NewDNSProvider() case "iij": return iij.NewDNSProvider() case "lightsail": diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go index a9f8424ad..c9c9ce3df 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go @@ -3,17 +3,39 @@ package dnsimple import ( + "errors" "fmt" "os" "strconv" "strings" + "time" "github.com/dnsimple/dnsimple-go/dnsimple" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + AccessToken string + BaseURL string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("DNSIMPLE_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("DNSIMPLE_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DNSIMPLE_POLLING_INTERVAL", acme.DefaultPollingInterval), + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { + config *Config client *dnsimple.Client } @@ -22,24 +44,39 @@ type DNSProvider struct { // // See: https://developer.dnsimple.com/v2/#authentication func NewDNSProvider() (*DNSProvider, error) { - accessToken := os.Getenv("DNSIMPLE_OAUTH_TOKEN") - baseURL := os.Getenv("DNSIMPLE_BASE_URL") + config := NewDefaultConfig() + config.AccessToken = os.Getenv("DNSIMPLE_OAUTH_TOKEN") + config.BaseURL = os.Getenv("DNSIMPLE_BASE_URL") - return NewDNSProviderCredentials(accessToken, baseURL) + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for dnsimple. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for DNSimple. +// Deprecated func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error) { - if accessToken == "" { - return nil, fmt.Errorf("DNSimple OAuth token is missing") + config := NewDefaultConfig() + config.AccessToken = accessToken + config.BaseURL = baseURL + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for DNSimple. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("dnsimple: the configuration of the DNS provider is nil") } - client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(accessToken)) - client.UserAgent = "lego" + if config.AccessToken == "" { + return nil, fmt.Errorf("dnsimple: OAuth token is missing") + } - if baseURL != "" { - client.BaseURL = baseURL + client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(config.AccessToken)) + client.UserAgent = acme.UserAgent + + if config.BaseURL != "" { + client.BaseURL = config.BaseURL } return &DNSProvider{client: client}, nil @@ -47,10 +84,9 @@ func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneName, err := d.getHostedZone(domain) - if err != nil { return err } @@ -60,10 +96,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return err } - recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) - _, err = d.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, d.config.TTL) + _, err = d.client.Zones.CreateRecord(accountID, zoneName, recordAttributes) if err != nil { - return fmt.Errorf("DNSimple API call failed: %v", err) + return fmt.Errorf("API call failed: %v", err) } return nil @@ -93,6 +129,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + func (d *DNSProvider) getHostedZone(domain string) (string, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { @@ -108,7 +150,7 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) { zones, err := d.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) if err != nil { - return "", fmt.Errorf("DNSimple API call failed: %v", err) + return "", fmt.Errorf("API call failed: %v", err) } var hostedZone dnsimple.Zone @@ -140,16 +182,16 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord result, err := d.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}}) if err != nil { - return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err) + return []dnsimple.ZoneRecord{}, fmt.Errorf("API call has failed: %v", err) } return result.Data, nil } -func (d *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord { +func (d *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) dnsimple.ZoneRecord { name := d.extractRecordName(fqdn, zoneName) - return &dnsimple.ZoneRecord{ + return dnsimple.ZoneRecord{ Type: "TXT", Name: name, Content: value, @@ -172,7 +214,7 @@ func (d *DNSProvider) getAccountID() (string, error) { } if whoamiResponse.Data.Account == nil { - return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token") + return "", fmt.Errorf("user tokens are not supported, please use an account token") } return strconv.FormatInt(whoamiResponse.Data.Account.ID, 10), nil diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go new file mode 100644 index 000000000..54e87dccb --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/client.go @@ -0,0 +1,168 @@ +package dnsmadeeasy + +import ( + "bytes" + "crypto/hmac" + "crypto/sha1" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "time" +) + +// Domain holds the DNSMadeEasy API representation of a Domain +type Domain struct { + ID int `json:"id"` + Name string `json:"name"` +} + +// Record holds the DNSMadeEasy API representation of a Domain Record +type Record struct { + ID int `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Value string `json:"value"` + TTL int `json:"ttl"` + SourceID int `json:"sourceId"` +} + +// Client DNSMadeEasy client +type Client struct { + apiKey string + apiSecret string + BaseURL string + HTTPClient *http.Client +} + +// NewClient creates a DNSMadeEasy client +func NewClient(apiKey string, apiSecret string) (*Client, error) { + if apiKey == "" { + return nil, fmt.Errorf("DNSMadeEasy: credentials missing: API key") + } + + if apiSecret == "" { + return nil, fmt.Errorf("DNSMadeEasy: credentials missing: API secret") + } + + return &Client{ + apiKey: apiKey, + apiSecret: apiSecret, + HTTPClient: &http.Client{}, + }, nil +} + +// GetDomain gets a domain +func (c *Client) GetDomain(authZone string) (*Domain, error) { + domainName := authZone[0 : len(authZone)-1] + resource := fmt.Sprintf("%s%s", "/dns/managed/name?domainname=", domainName) + + resp, err := c.sendRequest(http.MethodGet, resource, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + domain := &Domain{} + err = json.NewDecoder(resp.Body).Decode(&domain) + if err != nil { + return nil, err + } + + return domain, nil +} + +// GetRecords gets all TXT records +func (c *Client) GetRecords(domain *Domain, recordName, recordType string) (*[]Record, error) { + resource := fmt.Sprintf("%s/%d/%s%s%s%s", "/dns/managed", domain.ID, "records?recordName=", recordName, "&type=", recordType) + + resp, err := c.sendRequest(http.MethodGet, resource, nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + type recordsResponse struct { + Records *[]Record `json:"data"` + } + + records := &recordsResponse{} + err = json.NewDecoder(resp.Body).Decode(&records) + if err != nil { + return nil, err + } + + return records.Records, nil +} + +// CreateRecord creates a TXT records +func (c *Client) CreateRecord(domain *Domain, record *Record) error { + url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records") + + resp, err := c.sendRequest(http.MethodPost, url, record) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil +} + +// DeleteRecord deletes a TXT records +func (c *Client) DeleteRecord(record Record) error { + resource := fmt.Sprintf("%s/%d/%s/%d", "/dns/managed", record.SourceID, "records", record.ID) + + resp, err := c.sendRequest(http.MethodDelete, resource, nil) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil +} + +func (c *Client) sendRequest(method, resource string, payload interface{}) (*http.Response, error) { + url := fmt.Sprintf("%s%s", c.BaseURL, resource) + + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + timestamp := time.Now().UTC().Format(time.RFC1123) + signature, err := computeHMAC(timestamp, c.apiSecret) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + req.Header.Set("x-dnsme-apiKey", c.apiKey) + req.Header.Set("x-dnsme-requestDate", timestamp) + req.Header.Set("x-dnsme-hmac", signature) + req.Header.Set("accept", "application/json") + req.Header.Set("content-type", "application/json") + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode > 299 { + return nil, fmt.Errorf("DNSMadeEasy API request failed with HTTP status code %d", resp.StatusCode) + } + + return resp, nil +} + +func computeHMAC(message string, secret string) (string, error) { + key := []byte(secret) + h := hmac.New(sha1.New, key) + _, err := h.Write([]byte(message)) + if err != nil { + return "", err + } + return hex.EncodeToString(h.Sum(nil)), nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go index 9f7f31a9a..262a30203 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go @@ -1,12 +1,8 @@ package dnsmadeeasy import ( - "bytes" - "crypto/hmac" - "crypto/sha1" "crypto/tls" - "encoding/hex" - "encoding/json" + "errors" "fmt" "net/http" "os" @@ -18,38 +14,46 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + APIKey string + APISecret string + HTTPClient *http.Client + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("DNSMADEEASY_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("DNSMADEEASY_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DNSMADEEASY_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("DNSMADEEASY_HTTP_TIMEOUT", 10*time.Second), + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses // DNSMadeEasy's DNS API to manage TXT records for a domain. type DNSProvider struct { - baseURL string - apiKey string - apiSecret string - client *http.Client -} - -// Domain holds the DNSMadeEasy API representation of a Domain -type Domain struct { - ID int `json:"id"` - Name string `json:"name"` -} - -// Record holds the DNSMadeEasy API representation of a Domain Record -type Record struct { - ID int `json:"id"` - Type string `json:"type"` - Name string `json:"name"` - Value string `json:"value"` - TTL int `json:"ttl"` - SourceID int `json:"sourceId"` + config *Config + client *Client } // NewDNSProvider returns a DNSProvider instance configured for DNSMadeEasy DNS. -// Credentials must be passed in the environment variables: DNSMADEEASY_API_KEY -// and DNSMADEEASY_API_SECRET. +// Credentials must be passed in the environment variables: +// DNSMADEEASY_API_KEY and DNSMADEEASY_API_SECRET. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("DNSMADEEASY_API_KEY", "DNSMADEEASY_API_SECRET") if err != nil { - return nil, fmt.Errorf("DNSMadeEasy: %v", err) + return nil, fmt.Errorf("dnsmadeeasy: %v", err) } var baseURL string @@ -59,35 +63,53 @@ func NewDNSProvider() (*DNSProvider, error) { baseURL = "https://api.dnsmadeeasy.com/V2.0" } - return NewDNSProviderCredentials(baseURL, values["DNSMADEEASY_API_KEY"], values["DNSMADEEASY_API_SECRET"]) + config := NewDefaultConfig() + config.BaseURL = baseURL + config.APIKey = values["DNSMADEEASY_API_KEY"] + config.APISecret = values["DNSMADEEASY_API_SECRET"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for DNSMadeEasy. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for DNS Made Easy. +// Deprecated func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider, error) { - if baseURL == "" || apiKey == "" || apiSecret == "" { - return nil, fmt.Errorf("DNS Made Easy credentials missing") + config := NewDefaultConfig() + config.BaseURL = baseURL + config.APIKey = apiKey + config.APISecret = apiSecret + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for DNS Made Easy. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("dnsmadeeasy: the configuration of the DNS provider is nil") } - transport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + if config.BaseURL == "" { + return nil, fmt.Errorf("dnsmadeeasy: base URL missing") } - client := &http.Client{ - Transport: transport, - Timeout: 10 * time.Second, + + client, err := NewClient(config.APIKey, config.APISecret) + if err != nil { + return nil, fmt.Errorf("dnsmadeeasy: %v", err) } + client.HTTPClient = config.HTTPClient + client.BaseURL = config.BaseURL + return &DNSProvider{ - baseURL: baseURL, - apiKey: apiKey, - apiSecret: apiSecret, - client: client, + client: client, + config: config, }, nil } // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domainName, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domainName, keyAuth) + fqdn, value, _ := acme.DNS01Record(domainName, keyAuth) authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { @@ -95,16 +117,16 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error { } // fetch the domain details - domain, err := d.getDomain(authZone) + domain, err := d.client.GetDomain(authZone) if err != nil { return err } // create the TXT record name := strings.Replace(fqdn, "."+authZone, "", 1) - record := &Record{Type: "TXT", Name: name, Value: value, TTL: ttl} + record := &Record{Type: "TXT", Name: name, Value: value, TTL: d.config.TTL} - err = d.createRecord(domain, record) + err = d.client.CreateRecord(domain, record) return err } @@ -118,21 +140,21 @@ func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { } // fetch the domain details - domain, err := d.getDomain(authZone) + domain, err := d.client.GetDomain(authZone) if err != nil { return err } // find matching records name := strings.Replace(fqdn, "."+authZone, "", 1) - records, err := d.getRecords(domain, name, "TXT") + records, err := d.client.GetRecords(domain, name, "TXT") if err != nil { return err } // delete records for _, record := range *records { - err = d.deleteRecord(record) + err = d.client.DeleteRecord(record) if err != nil { return err } @@ -141,107 +163,8 @@ func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error { return nil } -func (d *DNSProvider) getDomain(authZone string) (*Domain, error) { - domainName := authZone[0 : len(authZone)-1] - resource := fmt.Sprintf("%s%s", "/dns/managed/name?domainname=", domainName) - - resp, err := d.sendRequest(http.MethodGet, resource, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - domain := &Domain{} - err = json.NewDecoder(resp.Body).Decode(&domain) - if err != nil { - return nil, err - } - - return domain, nil -} - -func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) (*[]Record, error) { - resource := fmt.Sprintf("%s/%d/%s%s%s%s", "/dns/managed", domain.ID, "records?recordName=", recordName, "&type=", recordType) - - resp, err := d.sendRequest(http.MethodGet, resource, nil) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - type recordsResponse struct { - Records *[]Record `json:"data"` - } - - records := &recordsResponse{} - err = json.NewDecoder(resp.Body).Decode(&records) - if err != nil { - return nil, err - } - - return records.Records, nil -} - -func (d *DNSProvider) createRecord(domain *Domain, record *Record) error { - url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records") - - resp, err := d.sendRequest(http.MethodPost, url, record) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} - -func (d *DNSProvider) deleteRecord(record Record) error { - resource := fmt.Sprintf("%s/%d/%s/%d", "/dns/managed", record.SourceID, "records", record.ID) - - resp, err := d.sendRequest(http.MethodDelete, resource, nil) - if err != nil { - return err - } - defer resp.Body.Close() - - return nil -} - -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*http.Response, error) { - url := fmt.Sprintf("%s%s", d.baseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err - } - - timestamp := time.Now().UTC().Format(time.RFC1123) - signature := computeHMAC(timestamp, d.apiSecret) - - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("x-dnsme-apiKey", d.apiKey) - req.Header.Set("x-dnsme-requestDate", timestamp) - req.Header.Set("x-dnsme-hmac", signature) - req.Header.Set("accept", "application/json") - req.Header.Set("content-type", "application/json") - - resp, err := d.client.Do(req) - if err != nil { - return nil, err - } - - if resp.StatusCode > 299 { - return nil, fmt.Errorf("DNSMadeEasy API request failed with HTTP status code %d", resp.StatusCode) - } - - return resp, nil -} - -func computeHMAC(message string, secret string) string { - key := []byte(secret) - h := hmac.New(sha1.New, key) - h.Write([]byte(message)) - return hex.EncodeToString(h.Sum(nil)) +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } diff --git a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go index e42e36338..35b748689 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go @@ -3,16 +3,42 @@ package dnspod import ( + "errors" "fmt" + "net/http" + "strconv" "strings" + "time" "github.com/decker502/dnspod-go" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + LoginToken string + TTL int + PropagationTimeout time.Duration + PollingInterval time.Duration + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("DNSPOD_TTL", 600), + PropagationTimeout: env.GetOrDefaultSecond("ALICLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("ALICLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("DNSPOD_HTTP_TIMEOUT", 0), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { + config *Config client *dnspod.Client } @@ -21,37 +47,55 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("DNSPOD_API_KEY") if err != nil { - return nil, fmt.Errorf("DNSPod: %v", err) + return nil, fmt.Errorf("dnspod: %v", err) } - return NewDNSProviderCredentials(values["DNSPOD_API_KEY"]) + config := NewDefaultConfig() + config.LoginToken = values["DNSPOD_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for dnspod. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for dnspod. +// Deprecated func NewDNSProviderCredentials(key string) (*DNSProvider, error) { - if key == "" { - return nil, fmt.Errorf("dnspod credentials missing") + config := NewDefaultConfig() + config.LoginToken = key + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for dnspod. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("dnspod: the configuration of the DNS provider is nil") } - params := dnspod.CommonParams{LoginToken: key, Format: "json"} - return &DNSProvider{ - client: dnspod.NewClient(params), - }, nil + if config.LoginToken == "" { + return nil, fmt.Errorf("dnspod: credentials missing") + } + + params := dnspod.CommonParams{LoginToken: config.LoginToken, Format: "json"} + + client := dnspod.NewClient(params) + client.HttpClient = config.HTTPClient + + return &DNSProvider{client: client}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneID, zoneName, err := d.getHostedZone(domain) if err != nil { return err } - recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, d.config.TTL) _, _, err = d.client.Domains.CreateRecord(zoneID, *recordAttributes) if err != nil { - return fmt.Errorf("dnspod API call failed: %v", err) + return fmt.Errorf("API call failed: %v", err) } return nil @@ -80,10 +124,16 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { zones, _, err := d.client.Domains.List() if err != nil { - return "", "", fmt.Errorf("dnspod API call failed: %v", err) + return "", "", fmt.Errorf("API call failed: %v", err) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) @@ -114,7 +164,7 @@ func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Re Name: name, Value: value, Line: "默认", - TTL: "600", + TTL: strconv.Itoa(ttl), } } @@ -127,7 +177,7 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, erro var records []dnspod.Record result, _, err := d.client.Domains.ListRecords(zoneID, "") if err != nil { - return records, fmt.Errorf("dnspod API call has failed: %v", err) + return records, fmt.Errorf("API call has failed: %v", err) } recordName := d.extractRecordName(fqdn, zoneName) diff --git a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go index a8e25ba8b..7e2a55a41 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go @@ -6,15 +6,36 @@ import ( "errors" "fmt" "io/ioutil" + "net/http" + "time" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + Token string + PropagationTimeout time.Duration + PollingInterval time.Duration + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + client := acme.HTTPClient + client.Timeout = env.GetOrDefaultSecond("DUCKDNS_HTTP_TIMEOUT", 30*time.Second) + + return &Config{ + PropagationTimeout: env.GetOrDefaultSecond("DUCKDNS_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DUCKDNS_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &client, + } +} + // DNSProvider adds and removes the record for the DNS challenge type DNSProvider struct { - // The api token - token string + config *Config } // NewDNSProvider returns a new DNS provider using @@ -22,31 +43,53 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("DUCKDNS_TOKEN") if err != nil { - return nil, fmt.Errorf("DuckDNS: %v", err) + return nil, fmt.Errorf("duckdns: %v", err) } - return NewDNSProviderCredentials(values["DUCKDNS_TOKEN"]) + config := NewDefaultConfig() + config.Token = values["DUCKDNS_TOKEN"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for http://duckdns.org . +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for http://duckdns.org +// Deprecated func NewDNSProviderCredentials(token string) (*DNSProvider, error) { - if token == "" { - return nil, errors.New("DuckDNS: credentials missing") + config := NewDefaultConfig() + config.Token = token + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for DuckDNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("duckdns: the configuration of the DNS provider is nil") } - return &DNSProvider{token: token}, nil + if config.Token == "" { + return nil, errors.New("duckdns: credentials missing") + } + + return &DNSProvider{config: config}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { _, txtRecord, _ := acme.DNS01Record(domain, keyAuth) - return updateTxtRecord(domain, d.token, txtRecord, false) + return updateTxtRecord(domain, d.config.Token, txtRecord, false) } // CleanUp clears DuckDNS TXT record func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - return updateTxtRecord(domain, d.token, "", true) + return updateTxtRecord(domain, d.config.Token, "", true) +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // updateTxtRecord Update the domains TXT record diff --git a/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go b/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go new file mode 100644 index 000000000..1ca7b8e56 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/dyn/client.go @@ -0,0 +1,35 @@ +package dyn + +import "encoding/json" + +const defaultBaseURL = "https://api.dynect.net/REST" + +type dynResponse struct { + // One of 'success', 'failure', or 'incomplete' + Status string `json:"status"` + + // The structure containing the actual results of the request + Data json.RawMessage `json:"data"` + + // The ID of the job that was created in response to a request. + JobID int `json:"job_id"` + + // A list of zero or more messages + Messages json.RawMessage `json:"msgs"` +} + +type creds struct { + Customer string `json:"customer_name"` + User string `json:"user_name"` + Pass string `json:"password"` +} + +type session struct { + Token string `json:"token"` + Version string `json:"version"` +} + +type publish struct { + Publish bool `json:"publish"` + Notes string `json:"notes"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go index 187b1b48d..8ec7753f5 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go @@ -5,6 +5,7 @@ package dyn import ( "bytes" "encoding/json" + "errors" "fmt" "net/http" "strconv" @@ -14,122 +15,166 @@ import ( "github.com/xenolf/lego/platform/config/env" ) -var dynBaseURL = "https://api.dynect.net/REST" +// Config is used to configure the creation of the DNSProvider +type Config struct { + CustomerName string + UserName string + Password string + HTTPClient *http.Client + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} -type dynResponse struct { - // One of 'success', 'failure', or 'incomplete' - Status string `json:"status"` - - // The structure containing the actual results of the request - Data json.RawMessage `json:"data"` - - // The ID of the job that was created in response to a request. - JobID int `json:"job_id"` - - // A list of zero or more messages - Messages json.RawMessage `json:"msgs"` +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("DYN_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("DYN_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("DYN_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("DYN_HTTP_TIMEOUT", 10*time.Second), + }, + } } // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses // Dyn's Managed DNS API to manage TXT records for a domain. type DNSProvider struct { - customerName string - userName string - password string - token string - client *http.Client + config *Config + token string } // NewDNSProvider returns a DNSProvider instance configured for Dyn DNS. -// Credentials must be passed in the environment variables: DYN_CUSTOMER_NAME, -// DYN_USER_NAME and DYN_PASSWORD. +// Credentials must be passed in the environment variables: +// DYN_CUSTOMER_NAME, DYN_USER_NAME and DYN_PASSWORD. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("DYN_CUSTOMER_NAME", "DYN_USER_NAME", "DYN_PASSWORD") if err != nil { - return nil, fmt.Errorf("DynDNS: %v", err) + return nil, fmt.Errorf("dyn: %v", err) } - return NewDNSProviderCredentials(values["DYN_CUSTOMER_NAME"], values["DYN_USER_NAME"], values["DYN_PASSWORD"]) + config := NewDefaultConfig() + config.CustomerName = values["DYN_CUSTOMER_NAME"] + config.UserName = values["DYN_USER_NAME"] + config.Password = values["DYN_PASSWORD"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Dyn DNS. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Dyn DNS. +// Deprecated func NewDNSProviderCredentials(customerName, userName, password string) (*DNSProvider, error) { - if customerName == "" || userName == "" || password == "" { - return nil, fmt.Errorf("DynDNS credentials missing") - } + config := NewDefaultConfig() + config.CustomerName = customerName + config.UserName = userName + config.Password = password - return &DNSProvider{ - customerName: customerName, - userName: userName, - password: password, - client: &http.Client{Timeout: 10 * time.Second}, - }, nil + return NewDNSProviderConfig(config) } -func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*dynResponse, error) { - url := fmt.Sprintf("%s/%s", dynBaseURL, resource) - - body, err := json.Marshal(payload) - if err != nil { - return nil, err +// NewDNSProviderConfig return a DNSProvider instance configured for Dyn DNS +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("dyn: the configuration of the DNS provider is nil") } - req, err := http.NewRequest(method, url, bytes.NewReader(body)) - if err != nil { - return nil, err + if config.CustomerName == "" || config.UserName == "" || config.Password == "" { + return nil, fmt.Errorf("dyn: credentials missing") } + + return &DNSProvider{config: config}, nil +} + +// Present creates a TXT record using the specified parameters +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + + err = d.login() + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + + data := map[string]interface{}{ + "rdata": map[string]string{ + "txtdata": value, + }, + "ttl": strconv.Itoa(d.config.TTL), + } + + resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) + _, err = d.sendRequest(http.MethodPost, resource, data) + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + + err = d.publish(authZone, "Added TXT record for ACME dns-01 challenge using lego client") + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + + return d.logout() +} + +// CleanUp removes the TXT record matching the specified parameters +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + + err = d.login() + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + + resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) + url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) + + req, err := http.NewRequest(http.MethodDelete, url, nil) + if err != nil { + return fmt.Errorf("dyn: %v", err) + } + req.Header.Set("Content-Type", "application/json") - if len(d.token) > 0 { - req.Header.Set("Auth-Token", d.token) - } + req.Header.Set("Auth-Token", d.token) - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { - return nil, err + return fmt.Errorf("dyn: %v", err) } - defer resp.Body.Close() + resp.Body.Close() - if resp.StatusCode >= 500 { - return nil, fmt.Errorf("Dyn API request failed with HTTP status code %d", resp.StatusCode) + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("dyn: API request failed to delete TXT record HTTP status code %d", resp.StatusCode) } - var dynRes dynResponse - err = json.NewDecoder(resp.Body).Decode(&dynRes) + err = d.publish(authZone, "Removed TXT record for ACME dns-01 challenge using lego client") if err != nil { - return nil, err + return fmt.Errorf("dyn: %v", err) } - if resp.StatusCode >= 400 { - return nil, fmt.Errorf("Dyn API request failed with HTTP status code %d: %s", resp.StatusCode, dynRes.Messages) - } else if resp.StatusCode == 307 { - // TODO add support for HTTP 307 response and long running jobs - return nil, fmt.Errorf("Dyn API request returned HTTP 307. This is currently unsupported") - } + return d.logout() +} - if dynRes.Status == "failure" { - // TODO add better error handling - return nil, fmt.Errorf("Dyn API request failed: %s", dynRes.Messages) - } - - return &dynRes, nil +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // Starts a new Dyn API Session. Authenticates using customerName, userName, // password and receives a token to be used in for subsequent requests. func (d *DNSProvider) login() error { - type creds struct { - Customer string `json:"customer_name"` - User string `json:"user_name"` - Pass string `json:"password"` - } - - type session struct { - Token string `json:"token"` - Version string `json:"version"` - } - - payload := &creds{Customer: d.customerName, User: d.userName, Pass: d.password} + payload := &creds{Customer: d.config.CustomerName, User: d.config.UserName, Pass: d.config.Password} dynRes, err := d.sendRequest(http.MethodPost, "Session", payload) if err != nil { return err @@ -153,7 +198,7 @@ func (d *DNSProvider) logout() error { return nil } - url := fmt.Sprintf("%s/Session", dynBaseURL) + url := fmt.Sprintf("%s/Session", defaultBaseURL) req, err := http.NewRequest(http.MethodDelete, url, nil) if err != nil { return err @@ -161,14 +206,14 @@ func (d *DNSProvider) logout() error { req.Header.Set("Content-Type", "application/json") req.Header.Set("Auth-Token", d.token) - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { return err } resp.Body.Close() - if resp.StatusCode != 200 { - return fmt.Errorf("Dyn API request failed to delete session with HTTP status code %d", resp.StatusCode) + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("API request failed to delete session with HTTP status code %d", resp.StatusCode) } d.token = "" @@ -176,47 +221,7 @@ func (d *DNSProvider) logout() error { return nil } -// Present creates a TXT record using the specified parameters -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - data := map[string]interface{}{ - "rdata": map[string]string{ - "txtdata": value, - }, - "ttl": strconv.Itoa(ttl), - } - - resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) - _, err = d.sendRequest(http.MethodPost, resource, data) - if err != nil { - return err - } - - err = d.publish(authZone, "Added TXT record for ACME dns-01 challenge using lego client") - if err != nil { - return err - } - - return d.logout() -} - func (d *DNSProvider) publish(zone, notes string) error { - type publish struct { - Publish bool `json:"publish"` - Notes string `json:"notes"` - } - pub := &publish{Publish: true, Notes: notes} resource := fmt.Sprintf("Zone/%s/", zone) @@ -224,45 +229,50 @@ func (d *DNSProvider) publish(zone, notes string) error { return err } -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) +func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (*dynResponse, error) { + url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + body, err := json.Marshal(payload) if err != nil { - return err + return nil, err } - err = d.login() + req, err := http.NewRequest(method, url, bytes.NewReader(body)) if err != nil { - return err + return nil, err } - - resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) - url := fmt.Sprintf("%s/%s", dynBaseURL, resource) - - req, err := http.NewRequest(http.MethodDelete, url, nil) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Auth-Token", d.token) + if len(d.token) > 0 { + req.Header.Set("Auth-Token", d.token) + } - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { - return err + return nil, err } - resp.Body.Close() + defer resp.Body.Close() - if resp.StatusCode != 200 { - return fmt.Errorf("Dyn API request failed to delete TXT record HTTP status code %d", resp.StatusCode) + if resp.StatusCode >= 500 { + return nil, fmt.Errorf("API request failed with HTTP status code %d", resp.StatusCode) } - err = d.publish(authZone, "Removed TXT record for ACME dns-01 challenge using lego client") + var dynRes dynResponse + err = json.NewDecoder(resp.Body).Decode(&dynRes) if err != nil { - return err + return nil, err } - return d.logout() + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("API request failed with HTTP status code %d: %s", resp.StatusCode, dynRes.Messages) + } else if resp.StatusCode == 307 { + // TODO add support for HTTP 307 response and long running jobs + return nil, fmt.Errorf("API request returned HTTP 307. This is currently unsupported") + } + + if dynRes.Status == "failure" { + // TODO add better error handling + return nil, fmt.Errorf("API request failed: %s", dynRes.Messages) + } + + return &dynRes, nil } diff --git a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go index e619b1663..72015d36e 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go @@ -5,15 +5,43 @@ package exoscale import ( "errors" "fmt" + "net/http" "os" + "time" "github.com/exoscale/egoscale" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) +const defaultBaseURL = "https://api.exoscale.ch/dns" + +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + APISecret string + Endpoint string + HTTPClient *http.Client + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("EXOSCALE_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("EXOSCALE_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("EXOSCALE_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("EXOSCALE_HTTP_TIMEOUT", 0), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { + config *Config client *egoscale.Client } @@ -22,32 +50,52 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("EXOSCALE_API_KEY", "EXOSCALE_API_SECRET") if err != nil { - return nil, fmt.Errorf("Exoscale: %v", err) + return nil, fmt.Errorf("exoscale: %v", err) } - endpoint := os.Getenv("EXOSCALE_ENDPOINT") - return NewDNSProviderClient(values["EXOSCALE_API_KEY"], values["EXOSCALE_API_SECRET"], endpoint) + config := NewDefaultConfig() + config.APIKey = values["EXOSCALE_API_KEY"] + config.APISecret = values["EXOSCALE_API_SECRET"] + config.Endpoint = os.Getenv("EXOSCALE_ENDPOINT") + + return NewDNSProviderConfig(config) } -// NewDNSProviderClient Uses the supplied parameters to return a DNSProvider instance -// configured for Exoscale. +// NewDNSProviderClient Uses the supplied parameters +// to return a DNSProvider instance configured for Exoscale. +// Deprecated func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) { - if key == "" || secret == "" { - return nil, fmt.Errorf("Exoscale credentials missing") + config := NewDefaultConfig() + config.APIKey = key + config.APISecret = secret + config.Endpoint = endpoint + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Exoscale. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("the configuration of the DNS provider is nil") } - if endpoint == "" { - endpoint = "https://api.exoscale.ch/dns" + if config.APIKey == "" || config.APISecret == "" { + return nil, fmt.Errorf("exoscale: credentials missing") } - return &DNSProvider{ - client: egoscale.NewClient(endpoint, key, secret), - }, nil + if config.Endpoint == "" { + config.Endpoint = defaultBaseURL + } + + client := egoscale.NewClient(config.Endpoint, config.APIKey, config.APISecret) + client.HTTPClient = config.HTTPClient + + return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err @@ -61,7 +109,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { if recordID == 0 { record := egoscale.DNSRecord{ Name: recordName, - TTL: ttl, + TTL: d.config.TTL, Content: value, RecordType: "TXT", } @@ -74,7 +122,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { record := egoscale.UpdateDNSRecord{ ID: recordID, Name: recordName, - TTL: ttl, + TTL: d.config.TTL, Content: value, RecordType: "TXT", } @@ -111,6 +159,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + // FindExistingRecordID Query Exoscale to find an existing record for this name. // Returns nil if no record could be found func (d *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) { diff --git a/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go b/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go index 4bc794007..fb8a9133a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go @@ -1,8 +1,10 @@ package fastdns import ( + "errors" "fmt" "reflect" + "time" configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1" "github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid" @@ -10,9 +12,26 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + edgegrid.Config + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + PropagationTimeout: env.GetOrDefaultSecond("AKAMAI_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("AKAMAI_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("AKAMAI_TTL", 120), + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { - config edgegrid.Config + config *Config } // NewDNSProvider uses the supplied environment variables to return a DNSProvider instance: @@ -20,24 +39,27 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("AKAMAI_HOST", "AKAMAI_CLIENT_TOKEN", "AKAMAI_CLIENT_SECRET", "AKAMAI_ACCESS_TOKEN") if err != nil { - return nil, fmt.Errorf("FastDNS: %v", err) + return nil, fmt.Errorf("fastdns: %v", err) } - return NewDNSProviderClient( - values["AKAMAI_HOST"], - values["AKAMAI_CLIENT_TOKEN"], - values["AKAMAI_CLIENT_SECRET"], - values["AKAMAI_ACCESS_TOKEN"], - ) + config := NewDefaultConfig() + config.Config = edgegrid.Config{ + Host: values["AKAMAI_HOST"], + ClientToken: values["AKAMAI_CLIENT_TOKEN"], + ClientSecret: values["AKAMAI_CLIENT_SECRET"], + AccessToken: values["AKAMAI_ACCESS_TOKEN"], + MaxBody: 131072, + } + + return NewDNSProviderConfig(config) } -// NewDNSProviderClient uses the supplied parameters to return a DNSProvider instance -// configured for FastDNS. +// NewDNSProviderClient uses the supplied parameters +// to return a DNSProvider instance configured for FastDNS. +// Deprecated func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (*DNSProvider, error) { - if clientToken == "" || clientSecret == "" || accessToken == "" || host == "" { - return nil, fmt.Errorf("FastDNS credentials are missing") - } - config := edgegrid.Config{ + config := NewDefaultConfig() + config.Config = edgegrid.Config{ Host: host, ClientToken: clientToken, ClientSecret: clientSecret, @@ -45,29 +67,40 @@ func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) ( MaxBody: 131072, } - return &DNSProvider{ - config: config, - }, nil + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for FastDNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("fastdns: the configuration of the DNS provider is nil") + } + + if config.ClientToken == "" || config.ClientSecret == "" || config.AccessToken == "" || config.Host == "" { + return nil, fmt.Errorf("FastDNS credentials are missing") + } + + return &DNSProvider{config: config}, nil } // Present creates a TXT record to fullfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { - return err + return fmt.Errorf("fastdns: %v", err) } - configdns.Init(d.config) + configdns.Init(d.config.Config) zone, err := configdns.GetZone(zoneName) if err != nil { - return err + return fmt.Errorf("fastdns: %v", err) } record := configdns.NewTxtRecord() record.SetField("name", recordName) - record.SetField("ttl", ttl) + record.SetField("ttl", d.config.TTL) record.SetField("target", value) record.SetField("active", true) @@ -89,14 +122,14 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { - return err + return fmt.Errorf("fastdns: %v", err) } - configdns.Init(d.config) + configdns.Init(d.config.Config) zone, err := configdns.GetZone(zoneName) if err != nil { - return err + return fmt.Errorf("fastdns: %v", err) } existingRecord := d.findExistingRecord(zone, recordName) @@ -104,7 +137,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { if existingRecord != nil { err := zone.RemoveRecord(existingRecord) if err != nil { - return err + return fmt.Errorf("fastdns: %v", err) } return zone.Save() } @@ -112,6 +145,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + func (d *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) { zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go b/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go new file mode 100644 index 000000000..3af340ecb --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/gandi/client.go @@ -0,0 +1,94 @@ +package gandi + +import ( + "encoding/xml" + "fmt" +) + +// types for XML-RPC method calls and parameters + +type param interface { + param() +} + +type paramString struct { + XMLName xml.Name `xml:"param"` + Value string `xml:"value>string"` +} + +type paramInt struct { + XMLName xml.Name `xml:"param"` + Value int `xml:"value>int"` +} + +type structMember interface { + structMember() +} +type structMemberString struct { + Name string `xml:"name"` + Value string `xml:"value>string"` +} +type structMemberInt struct { + Name string `xml:"name"` + Value int `xml:"value>int"` +} +type paramStruct struct { + XMLName xml.Name `xml:"param"` + StructMembers []structMember `xml:"value>struct>member"` +} + +func (p paramString) param() {} +func (p paramInt) param() {} +func (m structMemberString) structMember() {} +func (m structMemberInt) structMember() {} +func (p paramStruct) param() {} + +type methodCall struct { + XMLName xml.Name `xml:"methodCall"` + MethodName string `xml:"methodName"` + Params []param `xml:"params"` +} + +// types for XML-RPC responses + +type response interface { + faultCode() int + faultString() string +} + +type responseFault struct { + FaultCode int `xml:"fault>value>struct>member>value>int"` + FaultString string `xml:"fault>value>struct>member>value>string"` +} + +func (r responseFault) faultCode() int { return r.FaultCode } +func (r responseFault) faultString() string { return r.FaultString } + +type responseStruct struct { + responseFault + StructMembers []struct { + Name string `xml:"name"` + ValueInt int `xml:"value>int"` + } `xml:"params>param>value>struct>member"` +} + +type responseInt struct { + responseFault + Value int `xml:"params>param>value>int"` +} + +type responseBool struct { + responseFault + Value bool `xml:"params>param>value>boolean"` +} + +// POSTing/Marshalling/Unmarshalling + +type rpcError struct { + faultCode int + faultString string +} + +func (e rpcError) Error() string { + return fmt.Sprintf("Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go index d7243009e..1ac1a6fab 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go @@ -5,6 +5,7 @@ package gandi import ( "bytes" "encoding/xml" + "errors" "fmt" "io" "io/ioutil" @@ -20,15 +21,38 @@ import ( // Gandi API reference: http://doc.rpc.gandi.net/index.html // Gandi API domain examples: http://doc.rpc.gandi.net/domain/faq.html -var ( - // endpoint is the Gandi XML-RPC endpoint used by Present and - // CleanUp. It is overridden during tests. - endpoint = "https://rpc.gandi.net/xmlrpc/" - // findZoneByFqdn determines the DNS zone of an fqdn. It is overridden - // during tests. - findZoneByFqdn = acme.FindZoneByFqdn +const ( + // defaultBaseURL Gandi XML-RPC endpoint used by Present and CleanUp + defaultBaseURL = "https://rpc.gandi.net/xmlrpc/" + minTTL = 300 ) +// findZoneByFqdn determines the DNS zone of an fqdn. +// It is overridden during tests. +var findZoneByFqdn = acme.FindZoneByFqdn + +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("GANDI_TTL", minTTL), + PropagationTimeout: env.GetOrDefaultSecond("GANDI_PROPAGATION_TIMEOUT", 40*time.Minute), + PollingInterval: env.GetOrDefaultSecond("GANDI_POLLING_INTERVAL", 60*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("GANDI_HTTP_TIMEOUT", 60*time.Second), + }, + } +} + // inProgressInfo contains information about an in-progress challenge type inProgressInfo struct { zoneID int // zoneID of gandi zone to restore in CleanUp @@ -40,11 +64,10 @@ type inProgressInfo struct { // acme.ChallengeProviderTimeout interface that uses Gandi's XML-RPC // API to manage TXT records for a domain. type DNSProvider struct { - apiKey string inProgressFQDNs map[string]inProgressInfo inProgressAuthZones map[string]struct{} inProgressMu sync.Mutex - client *http.Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for Gandi. @@ -52,23 +75,43 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("GANDI_API_KEY") if err != nil { - return nil, fmt.Errorf("GandiDNS: %v", err) + return nil, fmt.Errorf("gandi: %v", err) } - return NewDNSProviderCredentials(values["GANDI_API_KEY"]) + config := NewDefaultConfig() + config.APIKey = values["GANDI_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Gandi. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Gandi. +// Deprecated func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if apiKey == "" { - return nil, fmt.Errorf("no Gandi API Key given") + config := NewDefaultConfig() + config.APIKey = apiKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Gandi. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("gandi: the configuration of the DNS provider is nil") } + + if config.APIKey == "" { + return nil, fmt.Errorf("gandi: no API Key given") + } + + if config.BaseURL == "" { + config.BaseURL = defaultBaseURL + } + return &DNSProvider{ - apiKey: apiKey, + config: config, inProgressFQDNs: make(map[string]inProgressInfo), inProgressAuthZones: make(map[string]struct{}), - client: &http.Client{Timeout: 60 * time.Second}, }, nil } @@ -76,27 +119,27 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { // does this by creating and activating a new temporary Gandi DNS // zone. This new zone contains the TXT record. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - if ttl < 300 { - ttl = 300 // 300 is gandi minimum value for ttl + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + if d.config.TTL < minTTL { + d.config.TTL = minTTL // 300 is gandi minimum value for ttl } // find authZone and Gandi zone_id for fqdn authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err) + return fmt.Errorf("gandi: findZoneByFqdn failure: %v", err) } zoneID, err := d.getZoneID(authZone) if err != nil { - return err + return fmt.Errorf("gandi: %v", err) } // determine name of TXT record if !strings.HasSuffix( strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf( - "Gandi DNS: unexpected authZone %s for fqdn %s", authZone, fqdn) + return fmt.Errorf("gandi: unexpected authZone %s for fqdn %s", authZone, fqdn) } name := fqdn[:len(fqdn)-len("."+authZone)] @@ -106,16 +149,12 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer d.inProgressMu.Unlock() if _, ok := d.inProgressAuthZones[authZone]; ok { - return fmt.Errorf( - "Gandi DNS: challenge already in progress for authZone %s", - authZone) + return fmt.Errorf("gandi: challenge already in progress for authZone %s", authZone) } // perform API actions to create and activate new gandi zone // containing the required TXT record - newZoneName := fmt.Sprintf( - "%s [ACME Challenge %s]", - acme.UnFqdn(authZone), time.Now().Format(time.RFC822Z)) + newZoneName := fmt.Sprintf("%s [ACME Challenge %s]", acme.UnFqdn(authZone), time.Now().Format(time.RFC822Z)) newZoneID, err := d.cloneZone(zoneID, newZoneName) if err != nil { @@ -124,22 +163,22 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { newZoneVersion, err := d.newZoneVersion(newZoneID) if err != nil { - return err + return fmt.Errorf("gandi: %v", err) } - err = d.addTXTRecord(newZoneID, newZoneVersion, name, value, ttl) + err = d.addTXTRecord(newZoneID, newZoneVersion, name, value, d.config.TTL) if err != nil { - return err + return fmt.Errorf("gandi: %v", err) } err = d.setZoneVersion(newZoneID, newZoneVersion) if err != nil { - return err + return fmt.Errorf("gandi: %v", err) } err = d.setZone(authZone, newZoneID) if err != nil { - return err + return fmt.Errorf("gandi: %v", err) } // save data necessary for CleanUp @@ -149,6 +188,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { authZone: authZone, } d.inProgressAuthZones[authZone] = struct{}{} + return nil } @@ -157,6 +197,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // removing the temporary one created by Present. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + // acquire lock and retrieve zoneID, newZoneID and authZone d.inProgressMu.Lock() defer d.inProgressMu.Unlock() @@ -175,7 +216,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { // perform API actions to restore old gandi zone for authZone err := d.setZone(authZone, zoneID) if err != nil { - return err + return fmt.Errorf("gandi: %v", err) } return d.deleteZone(newZoneID) @@ -185,109 +226,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { // are used by the acme package as timeout and check interval values // when checking for DNS record propagation with Gandi. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 40 * time.Minute, 60 * time.Second -} - -// types for XML-RPC method calls and parameters - -type param interface { - param() -} -type paramString struct { - XMLName xml.Name `xml:"param"` - Value string `xml:"value>string"` -} -type paramInt struct { - XMLName xml.Name `xml:"param"` - Value int `xml:"value>int"` -} - -type structMember interface { - structMember() -} -type structMemberString struct { - Name string `xml:"name"` - Value string `xml:"value>string"` -} -type structMemberInt struct { - Name string `xml:"name"` - Value int `xml:"value>int"` -} -type paramStruct struct { - XMLName xml.Name `xml:"param"` - StructMembers []structMember `xml:"value>struct>member"` -} - -func (p paramString) param() {} -func (p paramInt) param() {} -func (m structMemberString) structMember() {} -func (m structMemberInt) structMember() {} -func (p paramStruct) param() {} - -type methodCall struct { - XMLName xml.Name `xml:"methodCall"` - MethodName string `xml:"methodName"` - Params []param `xml:"params"` -} - -// types for XML-RPC responses - -type response interface { - faultCode() int - faultString() string -} - -type responseFault struct { - FaultCode int `xml:"fault>value>struct>member>value>int"` - FaultString string `xml:"fault>value>struct>member>value>string"` -} - -func (r responseFault) faultCode() int { return r.FaultCode } -func (r responseFault) faultString() string { return r.FaultString } - -type responseStruct struct { - responseFault - StructMembers []struct { - Name string `xml:"name"` - ValueInt int `xml:"value>int"` - } `xml:"params>param>value>struct>member"` -} - -type responseInt struct { - responseFault - Value int `xml:"params>param>value>int"` -} - -type responseBool struct { - responseFault - Value bool `xml:"params>param>value>boolean"` -} - -// POSTing/Marshalling/Unmarshalling - -type rpcError struct { - faultCode int - faultString string -} - -func (e rpcError) Error() string { - return fmt.Sprintf( - "Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString) -} - -func (d *DNSProvider) httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { - resp, err := d.client.Post(url, bodyType, body) - if err != nil { - return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err) - } - defer resp.Body.Close() - - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err) - } - - return b, nil + return d.config.PropagationTimeout, d.config.PollingInterval } // rpcCall makes an XML-RPC call to Gandi's RPC endpoint by @@ -298,12 +237,12 @@ func (d *DNSProvider) rpcCall(call *methodCall, resp response) error { // marshal b, err := xml.MarshalIndent(call, "", " ") if err != nil { - return fmt.Errorf("Gandi DNS: Marshal Error: %v", err) + return fmt.Errorf("marshal error: %v", err) } // post b = append([]byte(``+"\n"), b...) - respBody, err := d.httpPost(endpoint, "text/xml", bytes.NewReader(b)) + respBody, err := d.httpPost(d.config.BaseURL, "text/xml", bytes.NewReader(b)) if err != nil { return err } @@ -311,7 +250,7 @@ func (d *DNSProvider) rpcCall(call *methodCall, resp response) error { // unmarshal err = xml.Unmarshal(respBody, resp) if err != nil { - return fmt.Errorf("Gandi DNS: Unmarshal Error: %v", err) + return fmt.Errorf("unmarshal error: %v", err) } if resp.faultCode() != 0 { return rpcError{ @@ -327,7 +266,7 @@ func (d *DNSProvider) getZoneID(domain string) (int, error) { err := d.rpcCall(&methodCall{ MethodName: "domain.info", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramString{Value: domain}, }, }, resp) @@ -343,8 +282,7 @@ func (d *DNSProvider) getZoneID(domain string) (int, error) { } if zoneID == 0 { - return 0, fmt.Errorf( - "Gandi DNS: Could not determine zone_id for %s", domain) + return 0, fmt.Errorf("could not determine zone_id for %s", domain) } return zoneID, nil } @@ -354,7 +292,7 @@ func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { err := d.rpcCall(&methodCall{ MethodName: "domain.zone.clone", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramInt{Value: zoneID}, paramInt{Value: 0}, paramStruct{ @@ -378,7 +316,7 @@ func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { } if newZoneID == 0 { - return 0, fmt.Errorf("Gandi DNS: Could not determine cloned zone_id") + return 0, fmt.Errorf("could not determine cloned zone_id") } return newZoneID, nil } @@ -388,7 +326,7 @@ func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { err := d.rpcCall(&methodCall{ MethodName: "domain.zone.version.new", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramInt{Value: zoneID}, }, }, resp) @@ -397,7 +335,7 @@ func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { } if resp.Value == 0 { - return 0, fmt.Errorf("Gandi DNS: Could not create new zone version") + return 0, fmt.Errorf("could not create new zone version") } return resp.Value, nil } @@ -407,7 +345,7 @@ func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value s err := d.rpcCall(&methodCall{ MethodName: "domain.zone.record.add", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramInt{Value: zoneID}, paramInt{Value: version}, paramStruct{ @@ -436,7 +374,7 @@ func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { err := d.rpcCall(&methodCall{ MethodName: "domain.zone.version.set", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramInt{Value: zoneID}, paramInt{Value: version}, }, @@ -446,7 +384,7 @@ func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { } if !resp.Value { - return fmt.Errorf("Gandi DNS: could not set zone version") + return fmt.Errorf("could not set zone version") } return nil } @@ -456,7 +394,7 @@ func (d *DNSProvider) setZone(domain string, zoneID int) error { err := d.rpcCall(&methodCall{ MethodName: "domain.zone.set", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramString{Value: domain}, paramInt{Value: zoneID}, }, @@ -473,8 +411,7 @@ func (d *DNSProvider) setZone(domain string, zoneID int) error { } if respZoneID != zoneID { - return fmt.Errorf( - "Gandi DNS: Could not set new zone_id for %s", domain) + return fmt.Errorf("could not set new zone_id for %s", domain) } return nil } @@ -484,7 +421,7 @@ func (d *DNSProvider) deleteZone(zoneID int) error { err := d.rpcCall(&methodCall{ MethodName: "domain.zone.delete", Params: []param{ - paramString{Value: d.apiKey}, + paramString{Value: d.config.APIKey}, paramInt{Value: zoneID}, }, }, resp) @@ -493,7 +430,22 @@ func (d *DNSProvider) deleteZone(zoneID int) error { } if !resp.Value { - return fmt.Errorf("Gandi DNS: could not delete zone_id") + return fmt.Errorf("could not delete zone_id") } return nil } + +func (d *DNSProvider) httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { + resp, err := d.config.HTTPClient.Post(url, bodyType, body) + if err != nil { + return nil, fmt.Errorf("HTTP Post Error: %v", err) + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("HTTP Post Error: %v", err) + } + + return b, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go new file mode 100644 index 000000000..0116fdef3 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/client.go @@ -0,0 +1,18 @@ +package gandiv5 + +// types for JSON method calls and parameters + +type addFieldRequest struct { + RRSetTTL int `json:"rrset_ttl"` + RRSetValues []string `json:"rrset_values"` +} + +type deleteFieldRequest struct { + Delete bool `json:"delete"` +} + +// types for JSON responses + +type responseStruct struct { + Message string `json:"message"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go index dea0f5f4b..054f71d56 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go @@ -5,6 +5,7 @@ package gandiv5 import ( "bytes" "encoding/json" + "errors" "fmt" "net/http" "strings" @@ -18,30 +19,51 @@ import ( // Gandi API reference: http://doc.livedns.gandi.net/ -var ( - // endpoint is the Gandi API endpoint used by Present and - // CleanUp. It is overridden during tests. - endpoint = "https://dns.api.gandi.net/api/v5" - - // findZoneByFqdn determines the DNS zone of an fqdn. It is overridden - // during tests. - findZoneByFqdn = acme.FindZoneByFqdn +const ( + // defaultBaseURL endpoint is the Gandi API endpoint used by Present and CleanUp. + defaultBaseURL = "https://dns.api.gandi.net/api/v5" + minTTL = 300 ) +// findZoneByFqdn determines the DNS zone of an fqdn. +// It is overridden during tests. +var findZoneByFqdn = acme.FindZoneByFqdn + // inProgressInfo contains information about an in-progress challenge type inProgressInfo struct { fieldName string authZone string } +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("GANDIV5_TTL", minTTL), + PropagationTimeout: env.GetOrDefaultSecond("GANDIV5_PROPAGATION_TIMEOUT", 20*time.Minute), + PollingInterval: env.GetOrDefaultSecond("GANDIV5_POLLING_INTERVAL", 20*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("GANDIV5_HTTP_TIMEOUT", 10*time.Second), + }, + } +} + // DNSProvider is an implementation of the // acme.ChallengeProviderTimeout interface that uses Gandi's LiveDNS // API to manage TXT records for a domain. type DNSProvider struct { - apiKey string + config *Config inProgressFQDNs map[string]inProgressInfo inProgressMu sync.Mutex - client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Gandi. @@ -49,43 +71,63 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("GANDIV5_API_KEY") if err != nil { - return nil, fmt.Errorf("GandiDNS: %v", err) + return nil, fmt.Errorf("gandi: %v", err) } - return NewDNSProviderCredentials(values["GANDIV5_API_KEY"]) + config := NewDefaultConfig() + config.APIKey = values["GANDIV5_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Gandi. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Gandi. +// Deprecated func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if apiKey == "" { - return nil, fmt.Errorf("Gandi DNS: No Gandi API Key given") + config := NewDefaultConfig() + config.APIKey = apiKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Gandi. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("gandiv5: the configuration of the DNS provider is nil") } + + if config.APIKey == "" { + return nil, fmt.Errorf("gandiv5: no API Key given") + } + + if config.BaseURL == "" { + config.BaseURL = defaultBaseURL + } + return &DNSProvider{ - apiKey: apiKey, + config: config, inProgressFQDNs: make(map[string]inProgressInfo), - client: &http.Client{Timeout: 10 * time.Second}, }, nil } // Present creates a TXT record using the specified parameters. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - if ttl < 300 { - ttl = 300 // 300 is gandi minimum value for ttl + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + if d.config.TTL < minTTL { + d.config.TTL = minTTL // 300 is gandi minimum value for ttl } // find authZone authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err) + return fmt.Errorf("gandiv5: findZoneByFqdn failure: %v", err) } // determine name of TXT record if !strings.HasSuffix( strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf( - "Gandi DNS: unexpected authZone %s for fqdn %s", authZone, fqdn) + return fmt.Errorf("gandiv5: unexpected authZone %s for fqdn %s", authZone, fqdn) } name := fqdn[:len(fqdn)-len("."+authZone)] @@ -95,7 +137,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer d.inProgressMu.Unlock() // add TXT record into authZone - err = d.addTXTRecord(acme.UnFqdn(authZone), name, value, ttl) + err = d.addTXTRecord(acme.UnFqdn(authZone), name, value, d.config.TTL) if err != nil { return err } @@ -125,37 +167,47 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { delete(d.inProgressFQDNs, fqdn) // delete TXT record from authZone - return d.deleteTXTRecord(acme.UnFqdn(authZone), fieldName) + err := d.deleteTXTRecord(acme.UnFqdn(authZone), fieldName) + if err != nil { + return fmt.Errorf("gandiv5: %v", err) + } + return nil } // Timeout returns the values (20*time.Minute, 20*time.Second) which // are used by the acme package as timeout and check interval values // when checking for DNS record propagation with Gandi. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 20 * time.Minute, 20 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } -// types for JSON method calls and parameters +// functions to perform API actions -type addFieldRequest struct { - RRSetTTL int `json:"rrset_ttl"` - RRSetValues []string `json:"rrset_values"` +func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error { + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + response, err := d.sendRequest(http.MethodPut, target, addFieldRequest{ + RRSetTTL: ttl, + RRSetValues: []string{value}, + }) + if response != nil { + log.Infof("gandiv5: %s", response.Message) + } + return err } -type deleteFieldRequest struct { - Delete bool `json:"delete"` +func (d *DNSProvider) deleteTXTRecord(domain string, name string) error { + target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) + response, err := d.sendRequest(http.MethodDelete, target, deleteFieldRequest{ + Delete: true, + }) + if response != nil && response.Message == "" { + log.Infof("gandiv5: Zone record deleted") + } + return err } -// types for JSON responses - -type responseStruct struct { - Message string `json:"message"` -} - -// POSTing/Marshalling/Unmarshalling - func (d *DNSProvider) sendRequest(method string, resource string, payload interface{}) (*responseStruct, error) { - url := fmt.Sprintf("%s/%s", endpoint, resource) + url := fmt.Sprintf("%s/%s", d.config.BaseURL, resource) body, err := json.Marshal(payload) if err != nil { @@ -168,19 +220,20 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf } req.Header.Set("Content-Type", "application/json") - if len(d.apiKey) > 0 { - req.Header.Set("X-Api-Key", d.apiKey) + if len(d.config.APIKey) > 0 { + req.Header.Set("X-Api-Key", d.config.APIKey) } - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode >= 400 { - return nil, fmt.Errorf("Gandi DNS: request failed with HTTP status code %d", resp.StatusCode) + return nil, fmt.Errorf("request failed with HTTP status code %d", resp.StatusCode) } + var response responseStruct err = json.NewDecoder(resp.Body).Decode(&response) if err != nil && method != http.MethodDelete { @@ -189,28 +242,3 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf return &response, nil } - -// functions to perform API actions - -func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error { - target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - response, err := d.sendRequest(http.MethodPut, target, addFieldRequest{ - RRSetTTL: ttl, - RRSetValues: []string{value}, - }) - if response != nil { - log.Infof("Gandi DNS: %s", response.Message) - } - return err -} - -func (d *DNSProvider) deleteTXTRecord(domain string, name string) error { - target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name) - response, err := d.sendRequest(http.MethodDelete, target, deleteFieldRequest{ - Delete: true, - }) - if response != nil && response.Message == "" { - log.Infof("Gandi DNS: Zone record deleted") - } - return err -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go b/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go index 0f169677a..36e067a59 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go @@ -4,29 +4,47 @@ package gcloud import ( "encoding/json" + "errors" "fmt" "io/ioutil" + "net/http" "os" "time" "github.com/xenolf/lego/acme" - + "github.com/xenolf/lego/platform/config/env" "golang.org/x/net/context" "golang.org/x/oauth2/google" - "google.golang.org/api/dns/v1" ) -// DNSProvider is an implementation of the DNSProvider interface. -type DNSProvider struct { - project string - client *dns.Service +// Config is used to configure the creation of the DNSProvider +type Config struct { + Project string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client } -// NewDNSProvider returns a DNSProvider instance configured for Google Cloud -// DNS. Project name must be passed in the environment variable: GCE_PROJECT. -// A Service Account file can be passed in the environment variable: -// GCE_SERVICE_ACCOUNT_FILE +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("GCE_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("GCE_PROPAGATION_TIMEOUT", 180*time.Second), + PollingInterval: env.GetOrDefaultSecond("GCE_POLLING_INTERVAL", 5*time.Second), + } +} + +// DNSProvider is an implementation of the DNSProvider interface. +type DNSProvider struct { + config *Config + client *dns.Service +} + +// NewDNSProvider returns a DNSProvider instance configured for Google Cloud DNS. +// Project name must be passed in the environment variable: GCE_PROJECT. +// A Service Account file can be passed in the environment variable: GCE_SERVICE_ACCOUNT_FILE func NewDNSProvider() (*DNSProvider, error) { if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok { return NewDNSProviderServiceAccount(saFile) @@ -36,37 +54,35 @@ func NewDNSProvider() (*DNSProvider, error) { return NewDNSProviderCredentials(project) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Google Cloud DNS. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Google Cloud DNS. func NewDNSProviderCredentials(project string) (*DNSProvider, error) { if project == "" { - return nil, fmt.Errorf("Google Cloud project name missing") + return nil, fmt.Errorf("googlecloud: project name missing") } client, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope) if err != nil { - return nil, fmt.Errorf("unable to get Google Cloud client: %v", err) + return nil, fmt.Errorf("googlecloud: unable to get Google Cloud client: %v", err) } - svc, err := dns.New(client) - if err != nil { - return nil, fmt.Errorf("unable to create Google Cloud DNS service: %v", err) - } - return &DNSProvider{ - project: project, - client: svc, - }, nil + + config := NewDefaultConfig() + config.Project = project + config.HTTPClient = client + + return NewDNSProviderConfig(config) } -// NewDNSProviderServiceAccount uses the supplied service account JSON file to -// return a DNSProvider instance configured for Google Cloud DNS. +// NewDNSProviderServiceAccount uses the supplied service account JSON file +// to return a DNSProvider instance configured for Google Cloud DNS. func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { if saFile == "" { - return nil, fmt.Errorf("Google Cloud Service Account file missing") + return nil, fmt.Errorf("googlecloud: Service Account file missing") } dat, err := ioutil.ReadFile(saFile) if err != nil { - return nil, fmt.Errorf("unable to read Service Account file: %v", err) + return nil, fmt.Errorf("googlecloud: unable to read Service Account file: %v", err) } // read project id from service account file @@ -75,39 +91,50 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { } err = json.Unmarshal(dat, &datJSON) if err != nil || datJSON.ProjectID == "" { - return nil, fmt.Errorf("project ID not found in Google Cloud Service Account file") + return nil, fmt.Errorf("googlecloud: project ID not found in Google Cloud Service Account file") } project := datJSON.ProjectID conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope) if err != nil { - return nil, fmt.Errorf("unable to acquire config: %v", err) + return nil, fmt.Errorf("googlecloud: unable to acquire config: %v", err) } client := conf.Client(context.Background()) - svc, err := dns.New(client) - if err != nil { - return nil, fmt.Errorf("unable to create Google Cloud DNS service: %v", err) + config := NewDefaultConfig() + config.Project = project + config.HTTPClient = client + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Google Cloud DNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("googlecloud: the configuration of the DNS provider is nil") } - return &DNSProvider{ - project: project, - client: svc, - }, nil + + svc, err := dns.New(config.HTTPClient) + if err != nil { + return nil, fmt.Errorf("googlecloud: unable to create Google Cloud DNS service: %v", err) + } + + return &DNSProvider{config: config, client: svc}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("googlecloud: %v", err) } rec := &dns.ResourceRecordSet{ Name: fqdn, Rrdatas: []string{value}, - Ttl: int64(ttl), + Ttl: int64(d.config.TTL), Type: "TXT", } change := &dns.Change{ @@ -117,25 +144,25 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // Look for existing records. existing, err := d.findTxtRecords(zone, fqdn) if err != nil { - return err + return fmt.Errorf("googlecloud: %v", err) } if len(existing) > 0 { // Attempt to delete the existing records when adding our new one. change.Deletions = existing } - chg, err := d.client.Changes.Create(d.project, zone, change).Do() + chg, err := d.client.Changes.Create(d.config.Project, zone, change).Do() if err != nil { - return err + return fmt.Errorf("googlecloud: %v", err) } // wait for change to be acknowledged for chg.Status == "pending" { time.Sleep(time.Second) - chg, err = d.client.Changes.Get(d.project, zone, chg.Id).Do() + chg, err = d.client.Changes.Get(d.config.Project, zone, chg.Id).Do() if err != nil { - return err + return fmt.Errorf("googlecloud: %v", err) } } @@ -148,26 +175,26 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { zone, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("googlecloud: %v", err) } records, err := d.findTxtRecords(zone, fqdn) if err != nil { - return err + return fmt.Errorf("googlecloud: %v", err) } if len(records) == 0 { return nil } - _, err = d.client.Changes.Create(d.project, zone, &dns.Change{Deletions: records}).Do() - return err + _, err = d.client.Changes.Create(d.config.Project, zone, &dns.Change{Deletions: records}).Do() + return fmt.Errorf("googlecloud: %v", err) } // Timeout customizes the timeout values used by the ACME package for checking // DNS record validity. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 180 * time.Second, 5 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } // getHostedZone returns the managed-zone @@ -178,23 +205,22 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) { } zones, err := d.client.ManagedZones. - List(d.project). + List(d.config.Project). DnsName(authZone). Do() if err != nil { - return "", fmt.Errorf("GoogleCloud API call failed: %v", err) + return "", fmt.Errorf("API call failed: %v", err) } if len(zones.ManagedZones) == 0 { - return "", fmt.Errorf("no matching GoogleCloud domain found for domain %s", authZone) + return "", fmt.Errorf("no matching domain found for domain %s", authZone) } return zones.ManagedZones[0].Name, nil } func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) { - - recs, err := d.client.ResourceRecordSets.List(d.project, zone).Name(fqdn).Type("TXT").Do() + recs, err := d.client.ResourceRecordSets.List(d.config.Project, zone).Name(fqdn).Type("TXT").Do() if err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go b/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go new file mode 100644 index 000000000..a7e8cf8ee --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/glesys/client.go @@ -0,0 +1,24 @@ +package glesys + +// types for JSON method calls, parameters, and responses + +type addRecordRequest struct { + DomainName string `json:"domainname"` + Host string `json:"host"` + Type string `json:"type"` + Data string `json:"data"` + TTL int `json:"ttl,omitempty"` +} + +type deleteRecordRequest struct { + RecordID int `json:"recordid"` +} + +type responseStruct struct { + Response struct { + Status struct { + Code int `json:"code"` + } `json:"status"` + Record deleteRecordRequest `json:"record"` + } `json:"response"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go b/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go index d6b071963..78a0cff1a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go +++ b/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go @@ -5,6 +5,7 @@ package glesys import ( "bytes" "encoding/json" + "errors" "fmt" "net/http" "strings" @@ -18,64 +19,102 @@ import ( // GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation -// domainAPI is the GleSYS API endpoint used by Present and CleanUp. -const domainAPI = "https://api.glesys.com/domain" +const ( + // defaultBaseURL is the GleSYS API endpoint used by Present and CleanUp. + defaultBaseURL = "https://api.glesys.com/domain" + minTTL = 60 +) + +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIUser string + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("GLESYS_TTL", minTTL), + PropagationTimeout: env.GetOrDefaultSecond("GLESYS_PROPAGATION_TIMEOUT", 20*time.Minute), + PollingInterval: env.GetOrDefaultSecond("GLESYS_POLLING_INTERVAL", 20*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("GLESYS_HTTP_TIMEOUT", 10*time.Second), + }, + } +} // DNSProvider is an implementation of the // acme.ChallengeProviderTimeout interface that uses GleSYS // API to manage TXT records for a domain. type DNSProvider struct { - apiUser string - apiKey string + config *Config activeRecords map[string]int inProgressMu sync.Mutex - client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for GleSYS. -// Credentials must be passed in the environment variables: GLESYS_API_USER -// and GLESYS_API_KEY. +// Credentials must be passed in the environment variables: +// GLESYS_API_USER and GLESYS_API_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("GLESYS_API_USER", "GLESYS_API_KEY") if err != nil { - return nil, fmt.Errorf("GleSYS DNS: %v", err) + return nil, fmt.Errorf("glesys: %v", err) } - return NewDNSProviderCredentials(values["GLESYS_API_USER"], values["GLESYS_API_KEY"]) + config := NewDefaultConfig() + config.APIUser = values["GLESYS_API_USER"] + config.APIKey = values["GLESYS_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for GleSYS. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for GleSYS. +// Deprecated func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, error) { - if apiUser == "" || apiKey == "" { - return nil, fmt.Errorf("GleSYS DNS: Incomplete credentials provided") + config := NewDefaultConfig() + config.APIUser = apiUser + config.APIKey = apiKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for GleSYS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("glesys: the configuration of the DNS provider is nil") + } + + if config.APIUser == "" || config.APIKey == "" { + return nil, fmt.Errorf("glesys: incomplete credentials provided") } return &DNSProvider{ - apiUser: apiUser, - apiKey: apiKey, activeRecords: make(map[string]int), - client: &http.Client{Timeout: 10 * time.Second}, }, nil } // Present creates a TXT record using the specified parameters. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - if ttl < 60 { - ttl = 60 // 60 is GleSYS minimum value for ttl + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + if d.config.TTL < minTTL { + d.config.TTL = minTTL // 60 is GleSYS minimum value for ttl } // find authZone authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("GleSYS DNS: findZoneByFqdn failure: %v", err) + return fmt.Errorf("glesys: findZoneByFqdn failure: %v", err) } // determine name of TXT record if !strings.HasSuffix( strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf( - "GleSYS DNS: unexpected authZone %s for fqdn %s", authZone, fqdn) + return fmt.Errorf("glesys: unexpected authZone %s for fqdn %s", authZone, fqdn) } name := fqdn[:len(fqdn)-len("."+authZone)] @@ -85,7 +124,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer d.inProgressMu.Unlock() // add TXT record into authZone - recordID, err := d.addTXTRecord(domain, acme.UnFqdn(authZone), name, value, ttl) + recordID, err := d.addTXTRecord(domain, acme.UnFqdn(authZone), name, value, d.config.TTL) if err != nil { return err } @@ -118,36 +157,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { // are used by the acme package as timeout and check interval values // when checking for DNS record propagation with GleSYS. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 20 * time.Minute, 20 * time.Second -} - -// types for JSON method calls, parameters, and responses - -type addRecordRequest struct { - DomainName string `json:"domainname"` - Host string `json:"host"` - Type string `json:"type"` - Data string `json:"data"` - TTL int `json:"ttl,omitempty"` -} - -type deleteRecordRequest struct { - RecordID int `json:"recordid"` -} - -type responseStruct struct { - Response struct { - Status struct { - Code int `json:"code"` - } `json:"status"` - Record deleteRecordRequest `json:"record"` - } `json:"response"` + return d.config.PropagationTimeout, d.config.PollingInterval } // POSTing/Marshalling/Unmarshalling func (d *DNSProvider) sendRequest(method string, resource string, payload interface{}) (*responseStruct, error) { - url := fmt.Sprintf("%s/%s", domainAPI, resource) + url := fmt.Sprintf("%s/%s", defaultBaseURL, resource) body, err := json.Marshal(payload) if err != nil { @@ -160,16 +176,16 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf } req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(d.apiUser, d.apiKey) + req.SetBasicAuth(d.config.APIUser, d.config.APIKey) - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode >= 400 { - return nil, fmt.Errorf("GleSYS DNS: request failed with HTTP status code %d", resp.StatusCode) + return nil, fmt.Errorf("request failed with HTTP status code %d", resp.StatusCode) } var response responseStruct @@ -190,7 +206,7 @@ func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, valu }) if response != nil && response.Response.Status.Code == http.StatusOK { - log.Infof("[%s] GleSYS DNS: Successfully created record id %d", fqdn, response.Response.Record.RecordID) + log.Infof("[%s]: Successfully created record id %d", fqdn, response.Response.Record.RecordID) return response.Response.Record.RecordID, nil } return 0, err @@ -201,7 +217,7 @@ func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error { RecordID: recordid, }) if response != nil && response.Response.Status.Code == 200 { - log.Infof("[%s] GleSYS DNS: Successfully deleted record id %d", fqdn, recordid) + log.Infof("[%s]: Successfully deleted record id %d", fqdn, recordid) } return err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go index c569adcbe..7c5d48a6c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go +++ b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go @@ -4,6 +4,7 @@ package godaddy import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -15,46 +16,83 @@ import ( "github.com/xenolf/lego/platform/config/env" ) -// GoDaddyAPIURL represents the API endpoint to call. -const apiURL = "https://api.godaddy.com" +const ( + // defaultBaseURL represents the API endpoint to call. + defaultBaseURL = "https://api.godaddy.com" + minTTL = 600 +) + +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + APISecret string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("GODADDY_TTL", minTTL), + PropagationTimeout: env.GetOrDefaultSecond("GODADDY_PROPAGATION_TIMEOUT", 120*time.Second), + PollingInterval: env.GetOrDefaultSecond("GODADDY_POLLING_INTERVAL", 2*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("GODADDY_HTTP_TIMEOUT", 30*time.Second), + }, + } +} // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { - apiKey string - apiSecret string - client *http.Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for godaddy. -// Credentials must be passed in the environment variables: GODADDY_API_KEY -// and GODADDY_API_SECRET. +// Credentials must be passed in the environment variables: +// GODADDY_API_KEY and GODADDY_API_SECRET. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("GODADDY_API_KEY", "GODADDY_API_SECRET") if err != nil { - return nil, fmt.Errorf("GoDaddy: %v", err) + return nil, fmt.Errorf("godaddy: %v", err) } - return NewDNSProviderCredentials(values["GODADDY_API_KEY"], values["GODADDY_API_SECRET"]) + config := NewDefaultConfig() + config.APIKey = values["GODADDY_API_KEY"] + config.APISecret = values["GODADDY_API_SECRET"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for godaddy. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for godaddy. +// Deprecated func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) { - if apiKey == "" || apiSecret == "" { - return nil, fmt.Errorf("GoDaddy credentials missing") + config := NewDefaultConfig() + config.APIKey = apiKey + config.APISecret = apiSecret + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for godaddy. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("godaddy: the configuration of the DNS provider is nil") } - return &DNSProvider{ - apiKey: apiKey, - apiSecret: apiSecret, - client: &http.Client{Timeout: 30 * time.Second}, - }, nil + if config.APIKey == "" || config.APISecret == "" { + return nil, fmt.Errorf("godaddy: credentials missing") + } + + return &DNSProvider{config: config}, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } func (d *DNSProvider) extractRecordName(fqdn, domain string) string { @@ -67,14 +105,14 @@ func (d *DNSProvider) extractRecordName(fqdn, domain string) string { // Present creates a TXT record to fulfil the dns-01 challenge func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) domainZone, err := d.getZone(fqdn) if err != nil { return err } - if ttl < 600 { - ttl = 600 + if d.config.TTL < minTTL { + d.config.TTL = minTTL } recordName := d.extractRecordName(fqdn, domainZone) @@ -83,7 +121,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Type: "TXT", Name: recordName, Data: value, - TTL: ttl, + TTL: d.config.TTL, }, } @@ -141,16 +179,16 @@ func (d *DNSProvider) getZone(fqdn string) (string, error) { } func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest(method, fmt.Sprintf("%s%s", apiURL, uri), body) + req, err := http.NewRequest(method, fmt.Sprintf("%s%s", defaultBaseURL, uri), body) if err != nil { return nil, err } req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.apiKey, d.apiSecret)) + req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.config.APIKey, d.config.APISecret)) - return d.client.Do(req) + return d.config.HTTPClient.Do(req) } // DNSRecord a DNS record diff --git a/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go b/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go new file mode 100644 index 000000000..0bee9cccb --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/hostingde/client.go @@ -0,0 +1,91 @@ +package hostingde + +// RecordsAddRequest represents a DNS record to add +type RecordsAddRequest struct { + Name string `json:"name"` + Type string `json:"type"` + Content string `json:"content"` + TTL int `json:"ttl"` +} + +// RecordsDeleteRequest represents a DNS record to remove +type RecordsDeleteRequest struct { + Name string `json:"name"` + Type string `json:"type"` + Content string `json:"content"` + ID string `json:"id"` +} + +// ZoneConfigObject represents the ZoneConfig-section of a hosting.de API response. +type ZoneConfigObject struct { + AccountID string `json:"accountId"` + EmailAddress string `json:"emailAddress"` + ID string `json:"id"` + LastChangeDate string `json:"lastChangeDate"` + MasterIP string `json:"masterIp"` + Name string `json:"name"` + NameUnicode string `json:"nameUnicode"` + SOAValues struct { + Expire int `json:"expire"` + NegativeTTL int `json:"negativeTtl"` + Refresh int `json:"refresh"` + Retry int `json:"retry"` + Serial string `json:"serial"` + TTL int `json:"ttl"` + } `json:"soaValues"` + Status string `json:"status"` + TemplateValues string `json:"templateValues"` + Type string `json:"type"` + ZoneTransferWhitelist []string `json:"zoneTransferWhitelist"` +} + +// ZoneUpdateError represents an error in a ZoneUpdateResponse +type ZoneUpdateError struct { + Code int `json:"code"` + ContextObject string `json:"contextObject"` + ContextPath string `json:"contextPath"` + Details []string `json:"details"` + Text string `json:"text"` + Value string `json:"value"` +} + +// ZoneUpdateMetadata represents the metadata in a ZoneUpdateResponse +type ZoneUpdateMetadata struct { + ClientTransactionID string `json:"clientTransactionId"` + ServerTransactionID string `json:"serverTransactionId"` +} + +// ZoneUpdateResponse represents a response from hosting.de API +type ZoneUpdateResponse struct { + Errors []ZoneUpdateError `json:"errors"` + Metadata ZoneUpdateMetadata `json:"metadata"` + Warnings []string `json:"warnings"` + Status string `json:"status"` + Response struct { + Records []struct { + Content string `json:"content"` + Type string `json:"type"` + ID string `json:"id"` + Name string `json:"name"` + LastChangeDate string `json:"lastChangeDate"` + Priority int `json:"priority"` + RecordTemplateID string `json:"recordTemplateId"` + ZoneConfigID string `json:"zoneConfigId"` + TTL int `json:"ttl"` + } `json:"records"` + ZoneConfig ZoneConfigObject `json:"zoneConfig"` + } `json:"response"` +} + +// ZoneConfigSelector represents a "minimal" ZoneConfig object used in hosting.de API requests +type ZoneConfigSelector struct { + Name string `json:"name"` +} + +// ZoneUpdateRequest represents a hosting.de API ZoneUpdate request +type ZoneUpdateRequest struct { + AuthToken string `json:"authToken"` + ZoneConfigSelector `json:"zoneConfig"` + RecordsToAdd []RecordsAddRequest `json:"recordsToAdd"` + RecordsToDelete []RecordsDeleteRequest `json:"recordsToDelete"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go b/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go new file mode 100644 index 000000000..7f8280aae --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/hostingde/hostingde.go @@ -0,0 +1,209 @@ +// Package hostingde implements a DNS provider for solving the DNS-01 +// challenge using hosting.de. +package hostingde + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "sync" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" +) + +const defaultBaseURL = "https://secure.hosting.de/api/dns/v1/json" + +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + ZoneName string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("HOSTINGDE_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("HOSTINGDE_PROPAGATION_TIMEOUT", 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond("HOSTINGDE_POLLING_INTERVAL", 2*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("HOSTINGDE_HTTP_TIMEOUT", 30*time.Second), + }, + } +} + +// DNSProvider is an implementation of the acme.ChallengeProvider interface +type DNSProvider struct { + config *Config + recordIDs map[string]string + recordIDsMu sync.Mutex +} + +// NewDNSProvider returns a DNSProvider instance configured for hosting.de. +// Credentials must be passed in the environment variables: +// HOSTINGDE_ZONE_NAME and HOSTINGDE_API_KEY +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get("HOSTINGDE_API_KEY", "HOSTINGDE_ZONE_NAME") + if err != nil { + return nil, fmt.Errorf("hostingde: %v", err) + } + + config := NewDefaultConfig() + config.APIKey = values["HOSTINGDE_API_KEY"] + config.ZoneName = values["HOSTINGDE_ZONE_NAME"] + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for hosting.de. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("hostingde: the configuration of the DNS provider is nil") + } + + if config.APIKey == "" { + return nil, errors.New("hostingde: API key missing") + } + + if config.ZoneName == "" { + return nil, errors.New("hostingde: Zone Name missing") + } + + return &DNSProvider{ + config: config, + recordIDs: make(map[string]string), + }, nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +// Present creates a TXT record to fulfil the dns-01 challenge +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + rec := []RecordsAddRequest{{ + Type: "TXT", + Name: acme.UnFqdn(fqdn), + Content: value, + TTL: d.config.TTL, + }} + + req := ZoneUpdateRequest{ + AuthToken: d.config.APIKey, + ZoneConfigSelector: ZoneConfigSelector{ + Name: d.config.ZoneName, + }, + RecordsToAdd: rec, + } + + resp, err := d.updateZone(req) + if err != nil { + return fmt.Errorf("hostingde: %v", err) + } + + for _, record := range resp.Response.Records { + if record.Name == acme.UnFqdn(fqdn) && record.Content == fmt.Sprintf(`"%s"`, value) { + d.recordIDsMu.Lock() + d.recordIDs[fqdn] = record.ID + d.recordIDsMu.Unlock() + } + } + + if d.recordIDs[fqdn] == "" { + return fmt.Errorf("hostingde: error getting ID of just created record, for domain %s", domain) + } + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + // get the record's unique ID from when we created it + d.recordIDsMu.Lock() + recordID, ok := d.recordIDs[fqdn] + d.recordIDsMu.Unlock() + if !ok { + return fmt.Errorf("hostingde: unknown record ID for %q", fqdn) + } + + rec := []RecordsDeleteRequest{{ + Type: "TXT", + Name: acme.UnFqdn(fqdn), + Content: value, + ID: recordID, + }} + + req := ZoneUpdateRequest{ + AuthToken: d.config.APIKey, + ZoneConfigSelector: ZoneConfigSelector{ + Name: d.config.ZoneName, + }, + RecordsToDelete: rec, + } + + // Delete record ID from map + d.recordIDsMu.Lock() + delete(d.recordIDs, fqdn) + d.recordIDsMu.Unlock() + + _, err := d.updateZone(req) + if err != nil { + return fmt.Errorf("hostingde: %v", err) + } + return nil +} + +func (d *DNSProvider) updateZone(updateRequest ZoneUpdateRequest) (*ZoneUpdateResponse, error) { + body, err := json.Marshal(updateRequest) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, defaultBaseURL+"/zoneUpdate", bytes.NewReader(body)) + if err != nil { + return nil, err + } + + resp, err := d.config.HTTPClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error querying API: %v", err) + } + + defer resp.Body.Close() + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.New(toUnreadableBodyMessage(req, content)) + } + + // Everything looks good; but we'll need the ID later to delete the record + updateResponse := &ZoneUpdateResponse{} + err = json.Unmarshal(content, updateResponse) + if err != nil { + return nil, fmt.Errorf("%v: %s", err, toUnreadableBodyMessage(req, content)) + } + + if updateResponse.Status != "success" && updateResponse.Status != "pending" { + return updateResponse, errors.New(toUnreadableBodyMessage(req, content)) + } + + return updateResponse, nil +} + +func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string { + return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody)) +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go b/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go index ea9e57785..028e335d4 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go +++ b/vendor/github.com/xenolf/lego/providers/dns/iij/iij.go @@ -3,6 +3,7 @@ package iij import ( "fmt" + "strconv" "strings" "time" @@ -14,9 +15,21 @@ import ( // Config is used to configure the creation of the DNSProvider type Config struct { - AccessKey string - SecretKey string - DoServiceCode string + AccessKey string + SecretKey string + DoServiceCode string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("IIJ_TTL", 300), + PropagationTimeout: env.GetOrDefaultSecond("IIJ_PROPAGATION_TIMEOUT", 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond("IIJ_POLLING_INTERVAL", 4*time.Second), + } } // DNSProvider implements the acme.ChallengeProvider interface @@ -29,19 +42,24 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("IIJ_API_ACCESS_KEY", "IIJ_API_SECRET_KEY", "IIJ_DO_SERVICE_CODE") if err != nil { - return nil, fmt.Errorf("IIJ: %v", err) + return nil, fmt.Errorf("iij: %v", err) } - return NewDNSProviderConfig(&Config{ - AccessKey: values["IIJ_API_ACCESS_KEY"], - SecretKey: values["IIJ_API_SECRET_KEY"], - DoServiceCode: values["IIJ_DO_SERVICE_CODE"], - }) + config := NewDefaultConfig() + config.AccessKey = values["IIJ_API_ACCESS_KEY"] + config.SecretKey = values["IIJ_API_SECRET_KEY"] + config.DoServiceCode = values["IIJ_DO_SERVICE_CODE"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderConfig takes a given config ans returns a custom configured -// DNSProvider instance +// NewDNSProviderConfig takes a given config +// and returns a custom configured DNSProvider instance func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config.SecretKey == "" || config.AccessKey == "" || config.DoServiceCode == "" { + return nil, fmt.Errorf("iij: credentials missing") + } + return &DNSProvider{ api: doapi.NewAPI(config.AccessKey, config.SecretKey), config: config, @@ -49,24 +67,28 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { } // Timeout returns the timeout and interval to use when checking for DNS propagation. -func (p *DNSProvider) Timeout() (timeout, interval time.Duration) { - return time.Minute * 2, time.Second * 4 +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record using the specified parameters -func (p *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { _, value, _ := acme.DNS01Record(domain, keyAuth) - return p.addTxtRecord(domain, value) + + err := d.addTxtRecord(domain, value) + return fmt.Errorf("iij: %v", err) } // CleanUp removes the TXT record matching the specified parameters -func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { _, value, _ := acme.DNS01Record(domain, keyAuth) - return p.deleteTxtRecord(domain, value) + + err := d.deleteTxtRecord(domain, value) + return fmt.Errorf("iij: %v", err) } -func (p *DNSProvider) addTxtRecord(domain, value string) error { - zones, err := p.listZones() +func (d *DNSProvider) addTxtRecord(domain, value string) error { + zones, err := d.listZones() if err != nil { return err } @@ -77,25 +99,25 @@ func (p *DNSProvider) addTxtRecord(domain, value string) error { } request := protocol.RecordAdd{ - DoServiceCode: p.config.DoServiceCode, + DoServiceCode: d.config.DoServiceCode, ZoneName: zone, Owner: owner, - TTL: "300", + TTL: strconv.Itoa(d.config.TTL), RecordType: "TXT", RData: value, } response := &protocol.RecordAddResponse{} - if err := doapi.Call(*p.api, request, response); err != nil { + if err := doapi.Call(*d.api, request, response); err != nil { return err } - return p.commit() + return d.commit() } -func (p *DNSProvider) deleteTxtRecord(domain, value string) error { - zones, err := p.listZones() +func (d *DNSProvider) deleteTxtRecord(domain, value string) error { + zones, err := d.listZones() if err != nil { return err } @@ -105,45 +127,45 @@ func (p *DNSProvider) deleteTxtRecord(domain, value string) error { return err } - id, err := p.findTxtRecord(owner, zone, value) + id, err := d.findTxtRecord(owner, zone, value) if err != nil { return err } request := protocol.RecordDelete{ - DoServiceCode: p.config.DoServiceCode, + DoServiceCode: d.config.DoServiceCode, ZoneName: zone, RecordID: id, } response := &protocol.RecordDeleteResponse{} - if err := doapi.Call(*p.api, request, response); err != nil { + if err := doapi.Call(*d.api, request, response); err != nil { return err } - return p.commit() + return d.commit() } -func (p *DNSProvider) commit() error { +func (d *DNSProvider) commit() error { request := protocol.Commit{ - DoServiceCode: p.config.DoServiceCode, + DoServiceCode: d.config.DoServiceCode, } response := &protocol.CommitResponse{} - return doapi.Call(*p.api, request, response) + return doapi.Call(*d.api, request, response) } -func (p *DNSProvider) findTxtRecord(owner, zone, value string) (string, error) { +func (d *DNSProvider) findTxtRecord(owner, zone, value string) (string, error) { request := protocol.RecordListGet{ - DoServiceCode: p.config.DoServiceCode, + DoServiceCode: d.config.DoServiceCode, ZoneName: zone, } response := &protocol.RecordListGetResponse{} - if err := doapi.Call(*p.api, request, response); err != nil { + if err := doapi.Call(*d.api, request, response); err != nil { return "", err } @@ -162,14 +184,14 @@ func (p *DNSProvider) findTxtRecord(owner, zone, value string) (string, error) { return id, nil } -func (p *DNSProvider) listZones() ([]string, error) { +func (d *DNSProvider) listZones() ([]string, error) { request := protocol.ZoneListGet{ - DoServiceCode: p.config.DoServiceCode, + DoServiceCode: d.config.DoServiceCode, } response := &protocol.ZoneListGetResponse{} - if err := doapi.Call(*p.api, request, response); err != nil { + if err := doapi.Call(*d.api, request, response); err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go b/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go index 6a3089a1d..4c116dbb1 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go +++ b/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go @@ -3,6 +3,8 @@ package lightsail import ( + "errors" + "fmt" "math/rand" "os" "time" @@ -13,21 +15,15 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/lightsail" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) const ( maxRetries = 5 ) -// DNSProvider implements the acme.ChallengeProvider interface -type DNSProvider struct { - client *lightsail.Lightsail - dnsZone string -} - -// customRetryer implements the client.Retryer interface by composing the -// DefaultRetryer. It controls the logic for retrying recoverable request -// errors (e.g. when rate limits are exceeded). +// customRetryer implements the client.Retryer interface by composing the DefaultRetryer. +// It controls the logic for retrying recoverable request errors (e.g. when rate limits are exceeded). type customRetryer struct { client.DefaultRetryer } @@ -47,13 +43,36 @@ func (c customRetryer) RetryRules(r *request.Request) time.Duration { return time.Duration(delay) * time.Millisecond } -// NewDNSProvider returns a DNSProvider instance configured for the AWS -// Lightsail service. +// Config is used to configure the creation of the DNSProvider +type Config struct { + DNSZone string + Region string + PropagationTimeout time.Duration + PollingInterval time.Duration +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + DNSZone: os.Getenv("DNS_ZONE"), + PropagationTimeout: env.GetOrDefaultSecond("LIGHTSAIL_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("LIGHTSAIL_POLLING_INTERVAL", acme.DefaultPollingInterval), + Region: env.GetOrDefaultString("LIGHTSAIL_REGION", "us-east-1"), + } +} + +// DNSProvider implements the acme.ChallengeProvider interface +type DNSProvider struct { + client *lightsail.Lightsail + config *Config +} + +// NewDNSProvider returns a DNSProvider instance configured for the AWS Lightsail service. // // AWS Credentials are automatically detected in the following locations // and prioritized in the following order: // 1. Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, -// [AWS_SESSION_TOKEN], [DNS_ZONE] +// [AWS_SESSION_TOKEN], [DNS_ZONE], [LIGHTSAIL_REGION] // 2. Shared credentials file (defaults to ~/.aws/credentials) // 3. Amazon EC2 IAM role // @@ -61,49 +80,70 @@ func (c customRetryer) RetryRules(r *request.Request) time.Duration { // // See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk func NewDNSProvider() (*DNSProvider, error) { - r := customRetryer{} - r.NumMaxRetries = maxRetries + return NewDNSProviderConfig(NewDefaultConfig()) +} - config := aws.NewConfig().WithRegion("us-east-1") - sess, err := session.NewSession(request.WithRetryer(config, r)) +// NewDNSProviderConfig return a DNSProvider instance configured for AWS Lightsail. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("lightsail: the configuration of the DNS provider is nil") + } + + retryer := customRetryer{} + retryer.NumMaxRetries = maxRetries + + conf := aws.NewConfig().WithRegion(config.Region) + sess, err := session.NewSession(request.WithRetryer(conf, retryer)) if err != nil { return nil, err } return &DNSProvider{ - dnsZone: os.Getenv("DNS_ZONE"), - client: lightsail.New(sess), + config: config, + client: lightsail.New(sess), }, nil } // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - value = `"` + value + `"` - err := d.newTxtRecord(domain, fqdn, value) - return err + err := d.newTxtRecord(domain, fqdn, `"`+value+`"`) + if err != nil { + return fmt.Errorf("lightsail: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - value = `"` + value + `"` + params := &lightsail.DeleteDomainEntryInput{ - DomainName: aws.String(d.dnsZone), + DomainName: aws.String(d.config.DNSZone), DomainEntry: &lightsail.DomainEntry{ Name: aws.String(fqdn), Type: aws.String("TXT"), - Target: aws.String(value), + Target: aws.String(`"` + value + `"`), }, } + _, err := d.client.DeleteDomainEntry(params) - return err + if err != nil { + return fmt.Errorf("lightsail: %v", err) + } + return nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } func (d *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error { params := &lightsail.CreateDomainEntryInput{ - DomainName: aws.String(d.dnsZone), + DomainName: aws.String(d.config.DNSZone), DomainEntry: &lightsail.DomainEntry{ Name: aws.String(fqdn), Target: aws.String(value), diff --git a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go b/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go index 087d8b199..48739c127 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go +++ b/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go @@ -19,6 +19,21 @@ const ( dnsUpdateFudgeSecs = 120 ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + PollingInterval: env.GetOrDefaultSecond("LINODE_POLLING_INTERVAL", 15*time.Second), + TTL: env.GetOrDefaultInt("LINODE_TTL", 60), + } +} + type hostedZoneInfo struct { domainID int resourceName string @@ -26,6 +41,7 @@ type hostedZoneInfo struct { // DNSProvider implements the acme.ChallengeProvider interface. type DNSProvider struct { + config *Config client *dns.DNS } @@ -34,27 +50,44 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("LINODE_API_KEY") if err != nil { - return nil, fmt.Errorf("Linode: %v", err) + return nil, fmt.Errorf("linode: %v", err) } - return NewDNSProviderCredentials(values["LINODE_API_KEY"]) + config := NewDefaultConfig() + config.APIKey = values["LINODE_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Linode. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Linode. +// Deprecated func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if len(apiKey) == 0 { - return nil, errors.New("Linode credentials missing") + config := NewDefaultConfig() + config.APIKey = apiKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Linode. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("linode: the configuration of the DNS provider is nil") + } + + if len(config.APIKey) == 0 { + return nil, errors.New("linode: credentials missing") } return &DNSProvider{ - client: dns.New(apiKey), + config: config, + client: dns.New(config.APIKey), }, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (p *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // Since Linode only updates their zone files every X minutes, we need // to figure out how many minutes we have to wait until we hit the next // interval of X. We then wait another couple of minutes, just to be @@ -65,19 +98,19 @@ func (p *DNSProvider) Timeout() (timeout, interval time.Duration) { timeout = (time.Duration(minsRemaining) * time.Minute) + (dnsMinTTLSecs * time.Second) + (dnsUpdateFudgeSecs * time.Second) - interval = 15 * time.Second + interval = d.config.PollingInterval return } // Present creates a TXT record using the specified parameters. -func (p *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := p.getHostedZoneInfo(fqdn) + zone, err := d.getHostedZoneInfo(fqdn) if err != nil { return err } - if _, err = p.client.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil { + if _, err = d.client.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil { return err } @@ -85,15 +118,15 @@ func (p *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := p.getHostedZoneInfo(fqdn) + zone, err := d.getHostedZoneInfo(fqdn) if err != nil { return err } // Get all TXT records for the specified domain. - resources, err := p.client.GetResourcesByType(zone.domainID, "TXT") + resources, err := d.client.GetResourcesByType(zone.domainID, "TXT") if err != nil { return err } @@ -101,7 +134,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { // Remove the specified resource, if it exists. for _, resource := range resources { if resource.Name == zone.resourceName && resource.Target == value { - resp, err := p.client.DeleteDomainResource(resource.DomainID, resource.ResourceID) + resp, err := d.client.DeleteDomainResource(resource.DomainID, resource.ResourceID) if err != nil { return err } @@ -115,16 +148,17 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (p *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { +func (d *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { // Lookup the zone that handles the specified FQDN. authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { return nil, err } + resourceName := strings.TrimSuffix(fqdn, "."+authZone) // Query the authority zone. - domain, err := p.client.GetDomain(acme.UnFqdn(authZone)) + domain, err := d.client.GetDomain(acme.UnFqdn(authZone)) if err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go b/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go new file mode 100644 index 000000000..b816e1b4d --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/namecheap/client.go @@ -0,0 +1,44 @@ +package namecheap + +import "encoding/xml" + +// host describes a DNS record returned by the Namecheap DNS gethosts API. +// Namecheap uses the term "host" to refer to all DNS records that include +// a host field (A, AAAA, CNAME, NS, TXT, URL). +type host struct { + Type string `xml:",attr"` + Name string `xml:",attr"` + Address string `xml:",attr"` + MXPref string `xml:",attr"` + TTL string `xml:",attr"` +} + +// apierror describes an error record in a namecheap API response. +type apierror struct { + Number int `xml:",attr"` + Description string `xml:",innerxml"` +} + +type setHostsResponse struct { + XMLName xml.Name `xml:"ApiResponse"` + Status string `xml:"Status,attr"` + Errors []apierror `xml:"Errors>Error"` + Result struct { + IsSuccess string `xml:",attr"` + } `xml:"CommandResponse>DomainDNSSetHostsResult"` +} + +type getHostsResponse struct { + XMLName xml.Name `xml:"ApiResponse"` + Status string `xml:"Status,attr"` + Errors []apierror `xml:"Errors>Error"` + Hosts []host `xml:"CommandResponse>DomainDNSGetHostsResult>host"` +} + +type getTldsResponse struct { + XMLName xml.Name `xml:"ApiResponse"` + Errors []apierror `xml:"Errors>Error"` + Result []struct { + Name string `xml:",attr"` + } `xml:"CommandResponse>Tlds>Tld"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go index f0ce56a85..c50cc628f 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go @@ -4,10 +4,12 @@ package namecheap import ( "encoding/xml" + "errors" "fmt" "io/ioutil" "net/http" "net/url" + "strconv" "strings" "time" @@ -29,84 +31,175 @@ import ( // address as a form or query string value. This code uses a namecheap // service to query the client's IP address. -var ( - debug = false +const ( defaultBaseURL = "https://api.namecheap.com/xml.response" getIPURL = "https://dynamicdns.park-your-domain.com/getip" ) +// A challenge represents all the data needed to specify a dns-01 challenge +// to lets-encrypt. +type challenge struct { + domain string + key string + keyFqdn string + keyValue string + tld string + sld string + host string +} + +// Config is used to configure the creation of the DNSProvider +type Config struct { + Debug bool + BaseURL string + APIUser string + APIKey string + ClientIP string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + BaseURL: defaultBaseURL, + Debug: env.GetOrDefaultBool("NAMECHEAP_DEBUG", false), + TTL: env.GetOrDefaultInt("NAMECHEAP_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("NAMECHEAP_PROPAGATION_TIMEOUT", 60*time.Minute), + PollingInterval: env.GetOrDefaultSecond("NAMECHEAP_POLLING_INTERVAL", 15*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("NAMECHEAP_HTTP_TIMEOUT", 60*time.Second), + }, + } +} + // DNSProvider is an implementation of the ChallengeProviderTimeout interface // that uses Namecheap's tool API to manage TXT records for a domain. type DNSProvider struct { - baseURL string - apiUser string - apiKey string - clientIP string - client *http.Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for namecheap. -// Credentials must be passed in the environment variables: NAMECHEAP_API_USER -// and NAMECHEAP_API_KEY. +// Credentials must be passed in the environment variables: +// NAMECHEAP_API_USER and NAMECHEAP_API_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("NAMECHEAP_API_USER", "NAMECHEAP_API_KEY") if err != nil { - return nil, fmt.Errorf("NameCheap: %v", err) + return nil, fmt.Errorf("namecheap: %v", err) } - return NewDNSProviderCredentials(values["NAMECHEAP_API_USER"], values["NAMECHEAP_API_KEY"]) + config := NewDefaultConfig() + config.APIUser = values["NAMECHEAP_API_USER"] + config.APIKey = values["NAMECHEAP_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for namecheap. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for namecheap. +// Deprecated func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { - if apiUser == "" || apiKey == "" { - return nil, fmt.Errorf("Namecheap credentials missing") - } + config := NewDefaultConfig() + config.APIUser = apiUser + config.APIKey = apiKey - client := &http.Client{Timeout: 60 * time.Second} - - clientIP, err := getClientIP(client) - if err != nil { - return nil, err - } - - return &DNSProvider{ - baseURL: defaultBaseURL, - apiUser: apiUser, - apiKey: apiKey, - clientIP: clientIP, - client: client, - }, nil + return NewDNSProviderConfig(config) } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Namecheap can sometimes take a long time to complete an -// update, so wait up to 60 minutes for the update to propagate. +// NewDNSProviderConfig return a DNSProvider instance configured for namecheap. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("namecheap: the configuration of the DNS provider is nil") + } + + if config.APIUser == "" || config.APIKey == "" { + return nil, fmt.Errorf("namecheap: credentials missing") + } + + if len(config.ClientIP) == 0 { + clientIP, err := getClientIP(config.HTTPClient, config.Debug) + if err != nil { + return nil, fmt.Errorf("namecheap: %v", err) + } + config.ClientIP = clientIP + } + + return &DNSProvider{config: config}, nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Namecheap can sometimes take a long time to complete an update, so wait up to 60 minutes for the update to propagate. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 60 * time.Minute, 15 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } -// host describes a DNS record returned by the Namecheap DNS gethosts API. -// Namecheap uses the term "host" to refer to all DNS records that include -// a host field (A, AAAA, CNAME, NS, TXT, URL). -type host struct { - Type string `xml:",attr"` - Name string `xml:",attr"` - Address string `xml:",attr"` - MXPref string `xml:",attr"` - TTL string `xml:",attr"` +// Present installs a TXT record for the DNS challenge. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + tlds, err := d.getTLDs() + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + + ch, err := newChallenge(domain, keyAuth, tlds) + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + + hosts, err := d.getHosts(ch) + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + + d.addChallengeRecord(ch, &hosts) + + if d.config.Debug { + for _, h := range hosts { + log.Printf( + "%-5.5s %-30.30s %-6s %-70.70s\n", + h.Type, h.Name, h.TTL, h.Address) + } + } + + err = d.setHosts(ch, hosts) + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + return nil } -// apierror describes an error record in a namecheap API response. -type apierror struct { - Number int `xml:",attr"` - Description string `xml:",innerxml"` +// CleanUp removes a TXT record used for a previous DNS challenge. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + tlds, err := d.getTLDs() + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + + ch, err := newChallenge(domain, keyAuth, tlds) + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + + hosts, err := d.getHosts(ch) + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + + if removed := d.removeChallengeRecord(ch, &hosts); !removed { + return nil + } + + err = d.setHosts(ch, hosts) + if err != nil { + return fmt.Errorf("namecheap: %v", err) + } + return nil } -// getClientIP returns the client's public IP address. It uses namecheap's -// IP discovery service to perform the lookup. -func getClientIP(client *http.Client) (addr string, err error) { +// getClientIP returns the client's public IP address. +// It uses namecheap's IP discovery service to perform the lookup. +func getClientIP(client *http.Client, debug bool) (addr string, err error) { resp, err := client.Get(getIPURL) if err != nil { return "", err @@ -124,18 +217,6 @@ func getClientIP(client *http.Client) (addr string, err error) { return string(clientIP), nil } -// A challenge represents all the data needed to specify a dns-01 challenge -// to lets-encrypt. -type challenge struct { - domain string - key string - keyFqdn string - keyValue string - tld string - sld string - host string -} - // newChallenge builds a challenge record from a domain name, a challenge // authentication key, and a map of available TLDs. func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, error) { @@ -178,11 +259,11 @@ func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, e // setGlobalParams adds the namecheap global parameters to the provided url // Values record. func (d *DNSProvider) setGlobalParams(v *url.Values, cmd string) { - v.Set("ApiUser", d.apiUser) - v.Set("ApiKey", d.apiKey) - v.Set("UserName", d.apiUser) - v.Set("ClientIp", d.clientIP) + v.Set("ApiUser", d.config.APIUser) + v.Set("ApiKey", d.config.APIKey) + v.Set("UserName", d.config.APIUser) v.Set("Command", cmd) + v.Set("ClientIp", d.config.ClientIP) } // getTLDs requests the list of available TLDs from namecheap. @@ -190,10 +271,13 @@ func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) { values := make(url.Values) d.setGlobalParams(&values, "namecheap.domains.getTldList") - reqURL, _ := url.Parse(d.baseURL) + reqURL, err := url.Parse(d.config.BaseURL) + if err != nil { + return nil, err + } reqURL.RawQuery = values.Encode() - resp, err := d.client.Get(reqURL.String()) + resp, err := d.config.HTTPClient.Get(reqURL.String()) if err != nil { return nil, err } @@ -208,21 +292,12 @@ func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) { return nil, err } - type GetTldsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Errors []apierror `xml:"Errors>Error"` - Result []struct { - Name string `xml:",attr"` - } `xml:"CommandResponse>Tlds>Tld"` - } - - var gtr GetTldsResponse + var gtr getTldsResponse if err := xml.Unmarshal(body, >r); err != nil { return nil, err } if len(gtr.Errors) > 0 { - return nil, fmt.Errorf("Namecheap error: %s [%d]", - gtr.Errors[0].Description, gtr.Errors[0].Number) + return nil, fmt.Errorf("%s [%d]", gtr.Errors[0].Description, gtr.Errors[0].Number) } tlds = make(map[string]string) @@ -236,13 +311,17 @@ func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) { func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) { values := make(url.Values) d.setGlobalParams(&values, "namecheap.domains.dns.getHosts") + values.Set("SLD", ch.sld) values.Set("TLD", ch.tld) - reqURL, _ := url.Parse(d.baseURL) + reqURL, err := url.Parse(d.config.BaseURL) + if err != nil { + return nil, err + } reqURL.RawQuery = values.Encode() - resp, err := d.client.Get(reqURL.String()) + resp, err := d.config.HTTPClient.Get(reqURL.String()) if err != nil { return nil, err } @@ -257,20 +336,12 @@ func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) { return nil, err } - type GetHostsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Status string `xml:"Status,attr"` - Errors []apierror `xml:"Errors>Error"` - Hosts []host `xml:"CommandResponse>DomainDNSGetHostsResult>host"` - } - - var ghr GetHostsResponse + var ghr getHostsResponse if err = xml.Unmarshal(body, &ghr); err != nil { return nil, err } if len(ghr.Errors) > 0 { - return nil, fmt.Errorf("Namecheap error: %s [%d]", - ghr.Errors[0].Description, ghr.Errors[0].Number) + return nil, fmt.Errorf("%s [%d]", ghr.Errors[0].Description, ghr.Errors[0].Number) } return ghr.Hosts, nil @@ -280,6 +351,7 @@ func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) { func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error { values := make(url.Values) d.setGlobalParams(&values, "namecheap.domains.dns.setHosts") + values.Set("SLD", ch.sld) values.Set("TLD", ch.tld) @@ -292,7 +364,7 @@ func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error { values.Add("TTL"+ind, h.TTL) } - resp, err := d.client.PostForm(d.baseURL, values) + resp, err := d.config.HTTPClient.PostForm(d.config.BaseURL, values) if err != nil { return err } @@ -307,25 +379,15 @@ func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error { return err } - type SetHostsResponse struct { - XMLName xml.Name `xml:"ApiResponse"` - Status string `xml:"Status,attr"` - Errors []apierror `xml:"Errors>Error"` - Result struct { - IsSuccess string `xml:",attr"` - } `xml:"CommandResponse>DomainDNSSetHostsResult"` - } - - var shr SetHostsResponse + var shr setHostsResponse if err := xml.Unmarshal(body, &shr); err != nil { return err } if len(shr.Errors) > 0 { - return fmt.Errorf("Namecheap error: %s [%d]", - shr.Errors[0].Description, shr.Errors[0].Number) + return fmt.Errorf("%s [%d]", shr.Errors[0].Description, shr.Errors[0].Number) } if shr.Result.IsSuccess != "true" { - return fmt.Errorf("Namecheap setHosts failed") + return fmt.Errorf("setHosts failed") } return nil @@ -339,7 +401,7 @@ func (d *DNSProvider) addChallengeRecord(ch *challenge, hosts *[]host) { Type: "TXT", Address: ch.keyValue, MXPref: "10", - TTL: "120", + TTL: strconv.Itoa(d.config.TTL), } // If there's already a TXT record with the same name, replace it. @@ -367,57 +429,3 @@ func (d *DNSProvider) removeChallengeRecord(ch *challenge, hosts *[]host) bool { return false } - -// Present installs a TXT record for the DNS challenge. -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - tlds, err := d.getTLDs() - if err != nil { - return err - } - - ch, err := newChallenge(domain, keyAuth, tlds) - if err != nil { - return err - } - - hosts, err := d.getHosts(ch) - if err != nil { - return err - } - - d.addChallengeRecord(ch, &hosts) - - if debug { - for _, h := range hosts { - log.Printf( - "%-5.5s %-30.30s %-6s %-70.70s\n", - h.Type, h.Name, h.TTL, h.Address) - } - } - - return d.setHosts(ch, hosts) -} - -// CleanUp removes a TXT record used for a previous DNS challenge. -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - tlds, err := d.getTLDs() - if err != nil { - return err - } - - ch, err := newChallenge(domain, keyAuth, tlds) - if err != nil { - return err - } - - hosts, err := d.getHosts(ch) - if err != nil { - return err - } - - if removed := d.removeChallengeRecord(ch, &hosts); !removed { - return nil - } - - return d.setHosts(ch, hosts) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go b/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go index 15272c5f4..075f01835 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go @@ -3,66 +3,115 @@ package namedotcom import ( + "errors" "fmt" + "net/http" "os" "strings" + "time" "github.com/namedotcom/go/namecom" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + Username string + APIToken string + Server string + TTL int + PropagationTimeout time.Duration + PollingInterval time.Duration + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("NAMECOM_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("NAMECOM_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("NAMECOM_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("NAMECOM_HTTP_TIMEOUT", 10*time.Second), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { client *namecom.NameCom + config *Config } // NewDNSProvider returns a DNSProvider instance configured for namedotcom. -// Credentials must be passed in the environment variables: NAMECOM_USERNAME and NAMECOM_API_TOKEN +// Credentials must be passed in the environment variables: +// NAMECOM_USERNAME and NAMECOM_API_TOKEN func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("NAMECOM_USERNAME", "NAMECOM_API_TOKEN") if err != nil { - return nil, fmt.Errorf("Name.com: %v", err) + return nil, fmt.Errorf("namedotcom: %v", err) } - server := os.Getenv("NAMECOM_SERVER") - return NewDNSProviderCredentials(values["NAMECOM_USERNAME"], values["NAMECOM_API_TOKEN"], server) + config := NewDefaultConfig() + config.Username = values["NAMECOM_USERNAME"] + config.APIToken = values["NAMECOM_API_TOKEN"] + config.Server = os.Getenv("NAMECOM_SERVER") + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for namedotcom. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for namedotcom. +// Deprecated func NewDNSProviderCredentials(username, apiToken, server string) (*DNSProvider, error) { - if username == "" { - return nil, fmt.Errorf("Name.com Username is required") - } - if apiToken == "" { - return nil, fmt.Errorf("Name.com API token is required") + config := NewDefaultConfig() + config.Username = username + config.APIToken = apiToken + config.Server = server + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for namedotcom. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("namedotcom: the configuration of the DNS provider is nil") } - client := namecom.New(username, apiToken) - - if server != "" { - client.Server = server + if config.Username == "" { + return nil, fmt.Errorf("namedotcom: username is required") } - return &DNSProvider{client: client}, nil + if config.APIToken == "" { + return nil, fmt.Errorf("namedotcom: API token is required") + } + + client := namecom.New(config.Username, config.APIToken) + client.Client = config.HTTPClient + + if config.Server != "" { + client.Server = config.Server + } + + return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) request := &namecom.Record{ DomainName: domain, Host: d.extractRecordName(fqdn, domain), Type: "TXT", - TTL: uint32(ttl), + TTL: uint32(d.config.TTL), Answer: value, } _, err := d.client.CreateRecord(request) if err != nil { - return fmt.Errorf("Name.com API call failed: %v", err) + return fmt.Errorf("namedotcom: API call failed: %v", err) } return nil @@ -74,7 +123,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { records, err := d.getRecords(domain) if err != nil { - return err + return fmt.Errorf("namedotcom: %v", err) } for _, rec := range records { @@ -85,7 +134,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { } _, err := d.client.DeleteRecord(request) if err != nil { - return err + return fmt.Errorf("namedotcom: %v", err) } } } @@ -93,20 +142,21 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { - var ( - err error - records []*namecom.Record - response *namecom.ListRecordsResponse - ) +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} +func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { request := &namecom.ListRecordsRequest{ DomainName: domain, Page: 1, } + var records []*namecom.Record for request.Page > 0 { - response, err = d.client.ListRecords(request) + response, err := d.client.ListRecords(request) if err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go index e498d694c..f30bd7f12 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/client.go @@ -6,12 +6,13 @@ import ( "fmt" "io/ioutil" "net/http" + "time" "github.com/xenolf/lego/acme" ) -// netcupBaseURL for reaching the jSON-based API-Endpoint of netcup -const netcupBaseURL = "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON" +// defaultBaseURL for reaching the jSON-based API-Endpoint of netcup +const defaultBaseURL = "https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON" // success response status const success = "success" @@ -80,6 +81,7 @@ type DNSRecord struct { Destination string `json:"destination"` DeleteRecord bool `json:"deleterecord,omitempty"` State string `json:"state,omitempty"` + TTL int `json:"ttl,omitempty"` } // ResponseMsg as specified in netcup WSDL @@ -119,21 +121,20 @@ type Client struct { customerNumber string apiKey string apiPassword string - client *http.Client + HTTPClient *http.Client + BaseURL string } // NewClient creates a netcup DNS client -func NewClient(httpClient *http.Client, customerNumber string, apiKey string, apiPassword string) *Client { - client := http.DefaultClient - if httpClient != nil { - client = httpClient - } - +func NewClient(customerNumber string, apiKey string, apiPassword string) *Client { return &Client{ customerNumber: customerNumber, apiKey: apiKey, apiPassword: apiPassword, - client: client, + BaseURL: defaultBaseURL, + HTTPClient: &http.Client{ + Timeout: 10 * time.Second, + }, } } @@ -153,17 +154,17 @@ func (c *Client) Login() (string, error) { response, err := c.sendRequest(payload) if err != nil { - return "", fmt.Errorf("netcup: error sending request to DNS-API, %v", err) + return "", fmt.Errorf("error sending request to DNS-API, %v", err) } var r ResponseMsg err = json.Unmarshal(response, &r) if err != nil { - return "", fmt.Errorf("netcup: error decoding response of DNS-API, %v", err) + return "", fmt.Errorf("error decoding response of DNS-API, %v", err) } if r.Status != success { - return "", fmt.Errorf("netcup: error logging into DNS-API, %v", r.LongMessage) + return "", fmt.Errorf("error logging into DNS-API, %v", r.LongMessage) } return r.ResponseData.APISessionID, nil } @@ -183,18 +184,18 @@ func (c *Client) Logout(sessionID string) error { response, err := c.sendRequest(payload) if err != nil { - return fmt.Errorf("netcup: error logging out of DNS-API: %v", err) + return fmt.Errorf("error logging out of DNS-API: %v", err) } var r LogoutResponseMsg err = json.Unmarshal(response, &r) if err != nil { - return fmt.Errorf("netcup: error logging out of DNS-API: %v", err) + return fmt.Errorf("error logging out of DNS-API: %v", err) } if r.Status != success { - return fmt.Errorf("netcup: error logging out of DNS-API: %v", r.ShortMessage) + return fmt.Errorf("error logging out of DNS-API: %v", r.ShortMessage) } return nil } @@ -216,18 +217,18 @@ func (c *Client) UpdateDNSRecord(sessionID, domainName string, record DNSRecord) response, err := c.sendRequest(payload) if err != nil { - return fmt.Errorf("netcup: %v", err) + return err } var r ResponseMsg err = json.Unmarshal(response, &r) if err != nil { - return fmt.Errorf("netcup: %v", err) + return err } if r.Status != success { - return fmt.Errorf("netcup: %s: %+v", r.ShortMessage, r) + return fmt.Errorf("%s: %+v", r.ShortMessage, r) } return nil } @@ -249,18 +250,18 @@ func (c *Client) GetDNSRecords(hostname, apiSessionID string) ([]DNSRecord, erro response, err := c.sendRequest(payload) if err != nil { - return nil, fmt.Errorf("netcup: %v", err) + return nil, err } var r ResponseMsg err = json.Unmarshal(response, &r) if err != nil { - return nil, fmt.Errorf("netcup: %v", err) + return nil, err } if r.Status != success { - return nil, fmt.Errorf("netcup: %s", r.ShortMessage) + return nil, fmt.Errorf("%s", r.ShortMessage) } return r.ResponseData.DNSRecords, nil @@ -271,30 +272,30 @@ func (c *Client) GetDNSRecords(hostname, apiSessionID string) ([]DNSRecord, erro func (c *Client) sendRequest(payload interface{}) ([]byte, error) { body, err := json.Marshal(payload) if err != nil { - return nil, fmt.Errorf("netcup: %v", err) + return nil, err } - req, err := http.NewRequest(http.MethodPost, netcupBaseURL, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, c.BaseURL, bytes.NewReader(body)) if err != nil { - return nil, fmt.Errorf("netcup: %v", err) + return nil, err } req.Close = true req.Header.Set("content-type", "application/json") req.Header.Set("User-Agent", acme.UserAgent) - resp, err := c.client.Do(req) + resp, err := c.HTTPClient.Do(req) if err != nil { - return nil, fmt.Errorf("netcup: %v", err) + return nil, err } if resp.StatusCode > 299 { - return nil, fmt.Errorf("netcup: API request failed with HTTP Status code %d", resp.StatusCode) + return nil, fmt.Errorf("API request failed with HTTP Status code %d", resp.StatusCode) } body, err = ioutil.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("netcup: read of response body failed, %v", err) + return nil, fmt.Errorf("read of response body failed, %v", err) } defer resp.Body.Close() @@ -310,11 +311,11 @@ func GetDNSRecordIdx(records []DNSRecord, record DNSRecord) (int, error) { return index, nil } } - return -1, fmt.Errorf("netcup: no DNS Record found") + return -1, fmt.Errorf("no DNS Record found") } // CreateTxtRecord uses the supplied values to return a DNSRecord of type TXT for the dns-01 challenge -func CreateTxtRecord(hostname, value string) DNSRecord { +func CreateTxtRecord(hostname, value string, ttl int) DNSRecord { return DNSRecord{ ID: 0, Hostname: hostname, @@ -323,5 +324,6 @@ func CreateTxtRecord(hostname, value string) DNSRecord { Destination: value, DeleteRecord: false, State: "", + TTL: ttl, } } diff --git a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go index e7cc4c6b7..983b71e57 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go +++ b/vendor/github.com/xenolf/lego/providers/dns/netcup/netcup.go @@ -2,6 +2,7 @@ package netcup import ( + "errors" "fmt" "net/http" "strings" @@ -11,37 +12,78 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + Key string + Password string + Customer string + TTL int + PropagationTimeout time.Duration + PollingInterval time.Duration + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("NETCUP_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("NETCUP_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("NETCUP_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("NETCUP_HTTP_TIMEOUT", 10*time.Second), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { client *Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for netcup. -// Credentials must be passed in the environment variables: NETCUP_CUSTOMER_NUMBER, -// NETCUP_API_KEY, NETCUP_API_PASSWORD +// Credentials must be passed in the environment variables: +// NETCUP_CUSTOMER_NUMBER, NETCUP_API_KEY, NETCUP_API_PASSWORD func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("NETCUP_CUSTOMER_NUMBER", "NETCUP_API_KEY", "NETCUP_API_PASSWORD") if err != nil { return nil, fmt.Errorf("netcup: %v", err) } - return NewDNSProviderCredentials(values["NETCUP_CUSTOMER_NUMBER"], values["NETCUP_API_KEY"], values["NETCUP_API_PASSWORD"]) + config := NewDefaultConfig() + config.Customer = values["NETCUP_CUSTOMER_NUMBER"] + config.Key = values["NETCUP_API_KEY"] + config.Password = values["NETCUP_API_PASSWORD"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for netcup. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for netcup. +// Deprecated func NewDNSProviderCredentials(customer, key, password string) (*DNSProvider, error) { - if customer == "" || key == "" || password == "" { + config := NewDefaultConfig() + config.Customer = customer + config.Key = key + config.Password = password + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for netcup. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("netcup: the configuration of the DNS provider is nil") + } + + if config.Customer == "" || config.Key == "" || config.Password == "" { return nil, fmt.Errorf("netcup: netcup credentials missing") } - httpClient := &http.Client{ - Timeout: 10 * time.Second, - } + client := NewClient(config.Customer, config.Key, config.Password) + client.HTTPClient = config.HTTPClient - return &DNSProvider{ - client: NewClient(httpClient, customer, key, password), - }, nil + return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record to fulfill the dns-01 challenge @@ -55,21 +97,25 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error { sessionID, err := d.client.Login() if err != nil { - return err + return fmt.Errorf("netcup: %v", err) } hostname := strings.Replace(fqdn, "."+zone, "", 1) - record := CreateTxtRecord(hostname, value) + record := CreateTxtRecord(hostname, value, d.config.TTL) err = d.client.UpdateDNSRecord(sessionID, acme.UnFqdn(zone), record) if err != nil { if errLogout := d.client.Logout(sessionID); errLogout != nil { - return fmt.Errorf("failed to add TXT-Record: %v; %v", err, errLogout) + return fmt.Errorf("netcup: failed to add TXT-Record: %v; %v", err, errLogout) } - return fmt.Errorf("failed to add TXT-Record: %v", err) + return fmt.Errorf("netcup: failed to add TXT-Record: %v", err) } - return d.client.Logout(sessionID) + err = d.client.Logout(sessionID) + if err != nil { + return fmt.Errorf("netcup: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters @@ -78,12 +124,12 @@ func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error { zone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("failed to find DNSZone, %v", err) + return fmt.Errorf("netcup: failed to find DNSZone, %v", err) } sessionID, err := d.client.Login() if err != nil { - return err + return fmt.Errorf("netcup: %v", err) } hostname := strings.Replace(fqdn, "."+zone, "", 1) @@ -92,14 +138,14 @@ func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error { records, err := d.client.GetDNSRecords(zone, sessionID) if err != nil { - return err + return fmt.Errorf("netcup: %v", err) } - record := CreateTxtRecord(hostname, value) + record := CreateTxtRecord(hostname, value, 0) idx, err := GetDNSRecordIdx(records, record) if err != nil { - return err + return fmt.Errorf("netcup: %v", err) } records[idx].DeleteRecord = true @@ -107,10 +153,20 @@ func (d *DNSProvider) CleanUp(domainname, token, keyAuth string) error { err = d.client.UpdateDNSRecord(sessionID, zone, records[idx]) if err != nil { if errLogout := d.client.Logout(sessionID); errLogout != nil { - return fmt.Errorf("%v; %v", err, errLogout) + return fmt.Errorf("netcup: %v; %v", err, errLogout) } - return err + return fmt.Errorf("netcup: %v", err) } - return d.client.Logout(sessionID) + err = d.client.Logout(sessionID) + if err != nil { + return fmt.Errorf("netcup: %v", err) + } + return nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } diff --git a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go index 86b6fa659..dc10cbc20 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go +++ b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go @@ -15,9 +15,9 @@ import ( ) const ( - defaultEndpoint = "https://dns.api.cloud.nifty.com" - apiVersion = "2012-12-12N2013-12-16" - xmlNs = "https://route53.amazonaws.com/doc/2012-12-12/" + defaultBaseURL = "https://dns.api.cloud.nifty.com" + apiVersion = "2012-12-12N2013-12-16" + xmlNs = "https://route53.amazonaws.com/doc/2012-12-12/" ) // ChangeResourceRecordSetsRequest is a complex type that contains change information for the resource record set. @@ -88,31 +88,27 @@ type ChangeInfo struct { SubmittedAt string `xml:"SubmittedAt"` } -func newClient(httpClient *http.Client, accessKey string, secretKey string, endpoint string) *Client { - client := http.DefaultClient - if httpClient != nil { - client = httpClient - } - +// NewClient Creates a new client of NIFCLOUD DNS +func NewClient(accessKey string, secretKey string) *Client { return &Client{ - accessKey: accessKey, - secretKey: secretKey, - endpoint: endpoint, - client: client, + accessKey: accessKey, + secretKey: secretKey, + BaseURL: defaultBaseURL, + HTTPClient: &http.Client{}, } } // Client client of NIFCLOUD DNS type Client struct { - accessKey string - secretKey string - endpoint string - client *http.Client + accessKey string + secretKey string + BaseURL string + HTTPClient *http.Client } // ChangeResourceRecordSets Call ChangeResourceRecordSets API and return response. func (c *Client) ChangeResourceRecordSets(hostedZoneID string, input ChangeResourceRecordSetsRequest) (*ChangeResourceRecordSetsResponse, error) { - requestURL := fmt.Sprintf("%s/%s/hostedzone/%s/rrset", c.endpoint, apiVersion, hostedZoneID) + requestURL := fmt.Sprintf("%s/%s/hostedzone/%s/rrset", c.BaseURL, apiVersion, hostedZoneID) body := &bytes.Buffer{} body.Write([]byte(xml.Header)) @@ -133,7 +129,7 @@ func (c *Client) ChangeResourceRecordSets(hostedZoneID string, input ChangeResou return nil, fmt.Errorf("an error occurred during the creation of the signature: %v", err) } - res, err := c.client.Do(req) + res, err := c.HTTPClient.Do(req) if err != nil { return nil, err } @@ -164,7 +160,7 @@ func (c *Client) ChangeResourceRecordSets(hostedZoneID string, input ChangeResou // GetChange Call GetChange API and return response. func (c *Client) GetChange(statusID string) (*GetChangeResponse, error) { - requestURL := fmt.Sprintf("%s/%s/change/%s", c.endpoint, apiVersion, statusID) + requestURL := fmt.Sprintf("%s/%s/change/%s", c.BaseURL, apiVersion, statusID) req, err := http.NewRequest(http.MethodGet, requestURL, nil) if err != nil { @@ -176,7 +172,7 @@ func (c *Client) GetChange(statusID string) (*GetChangeResponse, error) { return nil, fmt.Errorf("an error occurred during the creation of the signature: %v", err) } - res, err := c.client.Do(req) + res, err := c.HTTPClient.Do(req) if err != nil { return nil, err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go index 5a7fa6b8c..7e828aa96 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go @@ -3,6 +3,7 @@ package nifcloud import ( + "errors" "fmt" "net/http" "os" @@ -12,49 +13,110 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + AccessKey string + SecretKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("NIFCLOUD_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("NIFCLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("NIFCLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("NIFCLOUD_HTTP_TIMEOUT", 30*time.Second), + }, + } +} + // DNSProvider implements the acme.ChallengeProvider interface type DNSProvider struct { client *Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for the NIFCLOUD DNS service. -// Credentials must be passed in the environment variables: NIFCLOUD_ACCESS_KEY_ID and NIFCLOUD_SECRET_ACCESS_KEY. +// Credentials must be passed in the environment variables: +// NIFCLOUD_ACCESS_KEY_ID and NIFCLOUD_SECRET_ACCESS_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("NIFCLOUD_ACCESS_KEY_ID", "NIFCLOUD_SECRET_ACCESS_KEY") if err != nil { - return nil, fmt.Errorf("NIFCLOUD: %v", err) + return nil, fmt.Errorf("nifcloud: %v", err) } - endpoint := os.Getenv("NIFCLOUD_DNS_ENDPOINT") - if endpoint == "" { - endpoint = defaultEndpoint - } + config := NewDefaultConfig() + config.BaseURL = os.Getenv("NIFCLOUD_DNS_ENDPOINT") + config.AccessKey = values["NIFCLOUD_ACCESS_KEY_ID"] + config.SecretKey = values["NIFCLOUD_SECRET_ACCESS_KEY"] - httpClient := &http.Client{Timeout: 30 * time.Second} - - return NewDNSProviderCredentials(httpClient, endpoint, values["NIFCLOUD_ACCESS_KEY_ID"], values["NIFCLOUD_SECRET_ACCESS_KEY"]) + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for NIFCLOUD. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for NIFCLOUD. +// Deprecated func NewDNSProviderCredentials(httpClient *http.Client, endpoint, accessKey, secretKey string) (*DNSProvider, error) { - client := newClient(httpClient, accessKey, secretKey, endpoint) + config := NewDefaultConfig() + config.HTTPClient = httpClient + config.BaseURL = endpoint + config.AccessKey = accessKey + config.SecretKey = secretKey - return &DNSProvider{ - client: client, - }, nil + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for NIFCLOUD. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("nifcloud: the configuration of the DNS provider is nil") + } + + client := NewClient(config.AccessKey, config.SecretKey) + + if config.HTTPClient != nil { + client.HTTPClient = config.HTTPClient + } + + if len(config.BaseURL) > 0 { + client.BaseURL = config.BaseURL + } + + return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - return d.changeRecord("CREATE", fqdn, value, domain, ttl) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + err := d.changeRecord("CREATE", fqdn, value, domain, d.config.TTL) + if err != nil { + return fmt.Errorf("nifcloud: %v", err) + } + return err } // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - return d.changeRecord("DELETE", fqdn, value, domain, ttl) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + err := d.changeRecord("DELETE", fqdn, value, domain, d.config.TTL) + if err != nil { + return fmt.Errorf("nifcloud: %v", err) + } + return err +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } func (d *DNSProvider) changeRecord(action, fqdn, value, domain string, ttl int) error { diff --git a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go index 148747bde..40dff6c60 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go @@ -3,6 +3,7 @@ package ns1 import ( + "errors" "fmt" "net/http" "strings" @@ -14,9 +15,31 @@ import ( "gopkg.in/ns1/ns1-go.v2/rest/model/dns" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("NS1_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("NS1_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("NS1_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("NS1_HTTP_TIMEOUT", 10*time.Second), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { client *rest.Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for NS1. @@ -24,38 +47,53 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("NS1_API_KEY") if err != nil { - return nil, fmt.Errorf("NS1: %v", err) + return nil, fmt.Errorf("ns1: %v", err) } - return NewDNSProviderCredentials(values["NS1_API_KEY"]) + config := NewDefaultConfig() + config.APIKey = values["NS1_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for NS1. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for NS1. +// Deprecated func NewDNSProviderCredentials(key string) (*DNSProvider, error) { - if key == "" { - return nil, fmt.Errorf("NS1 credentials missing") + config := NewDefaultConfig() + config.APIKey = key + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for NS1. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("ns1: the configuration of the DNS provider is nil") } - httpClient := &http.Client{Timeout: time.Second * 10} - client := rest.NewClient(httpClient, rest.SetAPIKey(key)) + if config.APIKey == "" { + return nil, fmt.Errorf("ns1: credentials missing") + } - return &DNSProvider{client}, nil + client := rest.NewClient(config.HTTPClient, rest.SetAPIKey(config.APIKey)) + + return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("ns1: %v", err) } - record := d.newTxtRecord(zone, fqdn, value, ttl) + record := d.newTxtRecord(zone, fqdn, value, d.config.TTL) _, err = d.client.Records.Create(record) if err != nil && err != rest.ErrRecordExists { - return err + return fmt.Errorf("ns1: %v", err) } return nil @@ -67,23 +105,29 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { zone, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("ns1: %v", err) } name := acme.UnFqdn(fqdn) _, err = d.client.Records.Delete(zone.Zone, name, "TXT") - return err + return fmt.Errorf("ns1: %v", err) +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { authZone, err := getAuthZone(domain) if err != nil { - return nil, err + return nil, fmt.Errorf("ns1: %v", err) } zone, _, err := d.client.Zones.Get(authZone) if err != nil { - return nil, err + return nil, fmt.Errorf("ns1: %v", err) } return zone, nil diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/client.go b/vendor/github.com/xenolf/lego/providers/dns/otc/client.go new file mode 100644 index 000000000..7f4fd34be --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/otc/client.go @@ -0,0 +1,68 @@ +package otc + +type recordset struct { + Name string `json:"name"` + Description string `json:"description"` + Type string `json:"type"` + TTL int `json:"ttl"` + Records []string `json:"records"` +} + +type nameResponse struct { + Name string `json:"name"` +} + +type userResponse struct { + Name string `json:"name"` + Password string `json:"password"` + Domain nameResponse `json:"domain"` +} + +type passwordResponse struct { + User userResponse `json:"user"` +} + +type identityResponse struct { + Methods []string `json:"methods"` + Password passwordResponse `json:"password"` +} + +type scopeResponse struct { + Project nameResponse `json:"project"` +} + +type authResponse struct { + Identity identityResponse `json:"identity"` + Scope scopeResponse `json:"scope"` +} + +type loginResponse struct { + Auth authResponse `json:"auth"` +} + +type endpointResponse struct { + Token struct { + Catalog []struct { + Type string `json:"type"` + Endpoints []struct { + URL string `json:"url"` + } `json:"endpoints"` + } `json:"catalog"` + } `json:"token"` +} + +type zoneItem struct { + ID string `json:"id"` +} + +type zonesResponse struct { + Zones []zoneItem `json:"zones"` +} + +type recordSet struct { + ID string `json:"id"` +} + +type recordSetsResponse struct { + RecordSets []recordSet `json:"recordsets"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go index 30535f7ec..b26934d15 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go +++ b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go @@ -5,27 +5,70 @@ package otc import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" + "net" "net/http" - "os" "time" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) +const defaultIdentityEndpoint = "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens" + +// minTTL 300 is otc minimum value for ttl +const minTTL = 300 + +// Config is used to configure the creation of the DNSProvider +type Config struct { + IdentityEndpoint string + DomainName string + ProjectName string + UserName string + Password string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + IdentityEndpoint: env.GetOrDefaultString("OTC_IDENTITY_ENDPOINT", defaultIdentityEndpoint), + PropagationTimeout: env.GetOrDefaultSecond("OTC_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("OTC_POLLING_INTERVAL", acme.DefaultPollingInterval), + TTL: env.GetOrDefaultInt("OTC_TTL", minTTL), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("OTC_HTTP_TIMEOUT", 10*time.Second), + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + + // Workaround for keep alive bug in otc api + DisableKeepAlives: true, + }, + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses // OTC's Managed DNS API to manage TXT records for a domain. type DNSProvider struct { - identityEndpoint string - otcBaseURL string - domainName string - projectName string - userName string - password string - token string + config *Config + baseURL string + token string } // NewDNSProvider returns a DNSProvider instance configured for OTC DNS. @@ -34,41 +77,129 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("OTC_DOMAIN_NAME", "OTC_USER_NAME", "OTC_PASSWORD", "OTC_PROJECT_NAME") if err != nil { - return nil, fmt.Errorf("OTC: %v", err) + return nil, fmt.Errorf("otc: %v", err) } - return NewDNSProviderCredentials( - values["OTC_DOMAIN_NAME"], - values["OTC_USER_NAME"], - values["OTC_PASSWORD"], - values["OTC_PROJECT_NAME"], - os.Getenv("OTC_IDENTITY_ENDPOINT"), - ) + config := NewDefaultConfig() + config.DomainName = values["OTC_DOMAIN_NAME"] + config.UserName = values["OTC_USER_NAME"] + config.Password = values["OTC_PASSWORD"] + config.ProjectName = values["OTC_PROJECT_NAME"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for OTC DNS. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for OTC DNS. +// Deprecated func NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint string) (*DNSProvider, error) { - if domainName == "" || userName == "" || password == "" || projectName == "" { - return nil, fmt.Errorf("OTC credentials missing") - } + config := NewDefaultConfig() + config.IdentityEndpoint = identityEndpoint + config.DomainName = domainName + config.UserName = userName + config.Password = password + config.ProjectName = projectName - if identityEndpoint == "" { - identityEndpoint = "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens" - } - - return &DNSProvider{ - identityEndpoint: identityEndpoint, - domainName: domainName, - userName: userName, - password: password, - projectName: projectName, - }, nil + return NewDNSProviderConfig(config) } -// SendRequest send request -func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) (io.Reader, error) { - url := fmt.Sprintf("%s/%s", d.otcBaseURL, resource) +// NewDNSProviderConfig return a DNSProvider instance configured for OTC DNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("otc: the configuration of the DNS provider is nil") + } + + if config.DomainName == "" || config.UserName == "" || config.Password == "" || config.ProjectName == "" { + return nil, fmt.Errorf("otc: credentials missing") + } + + if config.IdentityEndpoint == "" { + config.IdentityEndpoint = defaultIdentityEndpoint + } + + return &DNSProvider{config: config}, nil +} + +// Present creates a TXT record using the specified parameters +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + if d.config.TTL < minTTL { + d.config.TTL = minTTL + } + + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("otc: %v", err) + } + + err = d.login() + if err != nil { + return fmt.Errorf("otc: %v", err) + } + + zoneID, err := d.getZoneID(authZone) + if err != nil { + return fmt.Errorf("otc: unable to get zone: %s", err) + } + + resource := fmt.Sprintf("zones/%s/recordsets", zoneID) + + r1 := &recordset{ + Name: fqdn, + Description: "Added TXT record for ACME dns-01 challenge using lego client", + Type: "TXT", + TTL: d.config.TTL, + Records: []string{fmt.Sprintf("\"%s\"", value)}, + } + + _, err = d.sendRequest(http.MethodPost, resource, r1) + if err != nil { + return fmt.Errorf("otc: %v", err) + } + return nil +} + +// CleanUp removes the TXT record matching the specified parameters +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + + authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) + if err != nil { + return fmt.Errorf("otc: %v", err) + } + + err = d.login() + if err != nil { + return fmt.Errorf("otc: %v", err) + } + + zoneID, err := d.getZoneID(authZone) + if err != nil { + return fmt.Errorf("otc: %v", err) + } + + recordID, err := d.getRecordSetID(zoneID, fqdn) + if err != nil { + return fmt.Errorf("otc: unable go get record %s for zone %s: %s", fqdn, domain, err) + } + + err = d.deleteRecordSet(zoneID, recordID) + if err != nil { + return fmt.Errorf("otc: %v", err) + } + return nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +// sendRequest send request +func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) (io.Reader, error) { + url := fmt.Sprintf("%s/%s", d.baseURL, resource) body, err := json.Marshal(payload) if err != nil { @@ -84,15 +215,7 @@ func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) req.Header.Set("X-Auth-Token", d.token) } - // Workaround for keep alive bug in otc api - tr := http.DefaultTransport.(*http.Transport) - tr.DisableKeepAlives = true - - client := &http.Client{ - Timeout: 10 * time.Second, - Transport: tr, - } - resp, err := client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { return nil, err } @@ -111,42 +234,11 @@ func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) } func (d *DNSProvider) loginRequest() error { - type nameResponse struct { - Name string `json:"name"` - } - - type userResponse struct { - Name string `json:"name"` - Password string `json:"password"` - Domain nameResponse `json:"domain"` - } - - type passwordResponse struct { - User userResponse `json:"user"` - } - type identityResponse struct { - Methods []string `json:"methods"` - Password passwordResponse `json:"password"` - } - - type scopeResponse struct { - Project nameResponse `json:"project"` - } - - type authResponse struct { - Identity identityResponse `json:"identity"` - Scope scopeResponse `json:"scope"` - } - - type loginResponse struct { - Auth authResponse `json:"auth"` - } - userResp := userResponse{ - Name: d.userName, - Password: d.password, + Name: d.config.UserName, + Password: d.config.Password, Domain: nameResponse{ - Name: d.domainName, + Name: d.config.DomainName, }, } @@ -160,7 +252,7 @@ func (d *DNSProvider) loginRequest() error { }, Scope: scopeResponse{ Project: nameResponse{ - Name: d.projectName, + Name: d.config.ProjectName, }, }, }, @@ -170,13 +262,14 @@ func (d *DNSProvider) loginRequest() error { if err != nil { return err } - req, err := http.NewRequest(http.MethodPost, d.identityEndpoint, bytes.NewReader(body)) + + req, err := http.NewRequest(http.MethodPost, d.config.IdentityEndpoint, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") - client := &http.Client{Timeout: 10 * time.Second} + client := &http.Client{Timeout: d.config.HTTPClient.Timeout} resp, err := client.Do(req) if err != nil { return err @@ -193,16 +286,6 @@ func (d *DNSProvider) loginRequest() error { return fmt.Errorf("unable to get auth token") } - type endpointResponse struct { - Token struct { - Catalog []struct { - Type string `json:"type"` - Endpoints []struct { - URL string `json:"url"` - } `json:"endpoints"` - } `json:"catalog"` - } `json:"token"` - } var endpointResp endpointResponse err = json.NewDecoder(resp.Body).Decode(&endpointResp) @@ -213,13 +296,13 @@ func (d *DNSProvider) loginRequest() error { for _, v := range endpointResp.Token.Catalog { if v.Type == "dns" { for _, endpoint := range v.Endpoints { - d.otcBaseURL = fmt.Sprintf("%s/v2", endpoint.URL) + d.baseURL = fmt.Sprintf("%s/v2", endpoint.URL) continue } } } - if d.otcBaseURL == "" { + if d.baseURL == "" { return fmt.Errorf("unable to get dns endpoint") } @@ -233,16 +316,8 @@ func (d *DNSProvider) login() error { } func (d *DNSProvider) getZoneID(zone string) (string, error) { - type zoneItem struct { - ID string `json:"id"` - } - - type zonesResponse struct { - Zones []zoneItem `json:"zones"` - } - resource := fmt.Sprintf("zones?name=%s", zone) - resp, err := d.SendRequest(http.MethodGet, resource, nil) + resp, err := d.sendRequest(http.MethodGet, resource, nil) if err != nil { return "", err } @@ -269,16 +344,8 @@ func (d *DNSProvider) getZoneID(zone string) (string, error) { } func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) { - type recordSet struct { - ID string `json:"id"` - } - - type recordSetsResponse struct { - RecordSets []recordSet `json:"recordsets"` - } - resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn) - resp, err := d.SendRequest(http.MethodGet, resource, nil) + resp, err := d.sendRequest(http.MethodGet, resource, nil) if err != nil { return "", err } @@ -307,77 +374,6 @@ func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error { resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID) - _, err := d.SendRequest(http.MethodDelete, resource, nil) + _, err := d.sendRequest(http.MethodDelete, resource, nil) return err } - -// Present creates a TXT record using the specified parameters -func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - - if ttl < 300 { - ttl = 300 // 300 is otc minimum value for ttl - } - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - zoneID, err := d.getZoneID(authZone) - if err != nil { - return fmt.Errorf("unable to get zone: %s", err) - } - - resource := fmt.Sprintf("zones/%s/recordsets", zoneID) - - type recordset struct { - Name string `json:"name"` - Description string `json:"description"` - Type string `json:"type"` - TTL int `json:"ttl"` - Records []string `json:"records"` - } - - r1 := &recordset{ - Name: fqdn, - Description: "Added TXT record for ACME dns-01 challenge using lego client", - Type: "TXT", - TTL: ttl, - Records: []string{fmt.Sprintf("\"%s\"", value)}, - } - _, err = d.SendRequest(http.MethodPost, resource, r1) - return err -} - -// CleanUp removes the TXT record matching the specified parameters -func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - - authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) - if err != nil { - return err - } - - err = d.login() - if err != nil { - return err - } - - zoneID, err := d.getZoneID(authZone) - if err != nil { - return err - } - - recordID, err := d.getRecordSetID(zoneID, fqdn) - if err != nil { - return fmt.Errorf("unable go get record %s for zone %s: %s", fqdn, domain, err) - } - - return d.deleteRecordSet(zoneID, recordID) -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go index 0a35f896c..c633ea7d1 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go @@ -3,9 +3,12 @@ package ovh import ( + "errors" "fmt" + "net/http" "strings" "sync" + "time" "github.com/ovh/go-ovh/ovh" "github.com/xenolf/lego/acme" @@ -15,9 +18,34 @@ import ( // OVH API reference: https://eu.api.ovh.com/ // Create a Token: https://eu.api.ovh.com/createToken/ +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIEndpoint string + ApplicationKey string + ApplicationSecret string + ConsumerKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("OVH_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("OVH_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("OVH_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("OVH_HTTP_TIMEOUT", ovh.DefaultTimeout), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface // that uses OVH's REST API to manage TXT records for a domain. type DNSProvider struct { + config *Config client *ovh.Client recordIDs map[string]int recordIDsMu sync.Mutex @@ -32,69 +60,88 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("OVH_ENDPOINT", "OVH_APPLICATION_KEY", "OVH_APPLICATION_SECRET", "OVH_CONSUMER_KEY") if err != nil { - return nil, fmt.Errorf("OVH: %v", err) + return nil, fmt.Errorf("ovh: %v", err) } - return NewDNSProviderCredentials( - values["OVH_ENDPOINT"], - values["OVH_APPLICATION_KEY"], - values["OVH_APPLICATION_SECRET"], - values["OVH_CONSUMER_KEY"], - ) + config := NewDefaultConfig() + config.APIEndpoint = values["OVH_ENDPOINT"] + config.ApplicationKey = values["OVH_APPLICATION_KEY"] + config.ApplicationSecret = values["OVH_APPLICATION_SECRET"] + config.ConsumerKey = values["OVH_CONSUMER_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for OVH. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for OVH. +// Deprecated func NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, consumerKey string) (*DNSProvider, error) { - if apiEndpoint == "" || applicationKey == "" || applicationSecret == "" || consumerKey == "" { - return nil, fmt.Errorf("OVH credentials missing") + config := NewDefaultConfig() + config.APIEndpoint = apiEndpoint + config.ApplicationKey = applicationKey + config.ApplicationSecret = applicationSecret + config.ConsumerKey = consumerKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for OVH. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("ovh: the configuration of the DNS provider is nil") } - ovhClient, err := ovh.NewClient( - apiEndpoint, - applicationKey, - applicationSecret, - consumerKey, + if config.APIEndpoint == "" || config.ApplicationKey == "" || config.ApplicationSecret == "" || config.ConsumerKey == "" { + return nil, fmt.Errorf("ovh: credentials missing") + } + + client, err := ovh.NewClient( + config.APIEndpoint, + config.ApplicationKey, + config.ApplicationSecret, + config.ConsumerKey, ) - if err != nil { - return nil, err + return nil, fmt.Errorf("ovh: %v", err) } + client.Client = config.HTTPClient + return &DNSProvider{ - client: ovhClient, + config: config, + client: client, recordIDs: make(map[string]int), }, nil } // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) // Parse domain name authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + return fmt.Errorf("ovh: could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) subDomain := d.extractRecordName(fqdn, authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone) - reqData := txtRecordRequest{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: ttl} + reqData := txtRecordRequest{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: d.config.TTL} var respData txtRecordResponse // Create TXT record err = d.client.Post(reqURL, reqData, &respData) if err != nil { - return fmt.Errorf("error when call OVH api to add record: %v", err) + return fmt.Errorf("ovh: error when call api to add record: %v", err) } // Apply the change reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone) err = d.client.Post(reqURL, nil, nil) if err != nil { - return fmt.Errorf("error when call OVH api to refresh zone: %v", err) + return fmt.Errorf("ovh: error when call api to refresh zone: %v", err) } d.recordIDsMu.Lock() @@ -113,12 +160,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { - return fmt.Errorf("unknown record ID for '%s'", fqdn) + return fmt.Errorf("ovh: unknown record ID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { - return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err) + return fmt.Errorf("ovh: could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) @@ -127,7 +174,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { err = d.client.Delete(reqURL, nil) if err != nil { - return fmt.Errorf("error when call OVH api to delete challenge record: %v", err) + return fmt.Errorf("ovh: error when call OVH api to delete challenge record: %v", err) } // Delete record ID from map @@ -138,6 +185,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { diff --git a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go index 3b9915ac2..44c4e4331 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go @@ -5,6 +5,7 @@ package pdns import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -18,12 +19,32 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + Host *url.URL + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("PDNS_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("PDNS_PROPAGATION_TIMEOUT", 120*time.Second), + PollingInterval: env.GetOrDefaultSecond("PDNS_POLLING_INTERVAL", 2*time.Second), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("PDNS_HTTP_TIMEOUT", 30*time.Second), + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface type DNSProvider struct { - apiKey string - host *url.URL apiVersion int - client *http.Client + config *Config } // NewDNSProvider returns a DNSProvider instance configured for pdns. @@ -32,37 +53,51 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("PDNS_API_KEY", "PDNS_API_URL") if err != nil { - return nil, fmt.Errorf("PDNS: %v", err) + return nil, fmt.Errorf("pdns: %v", err) } hostURL, err := url.Parse(values["PDNS_API_URL"]) if err != nil { - return nil, fmt.Errorf("PDNS: %v", err) + return nil, fmt.Errorf("pdns: %v", err) } - return NewDNSProviderCredentials(hostURL, values["PDNS_API_KEY"]) + config := NewDefaultConfig() + config.Host = hostURL + config.APIKey = values["PDNS_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for pdns. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for pdns. +// Deprecated func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error) { - if key == "" { - return nil, fmt.Errorf("PDNS API key missing") + config := NewDefaultConfig() + config.Host = host + config.APIKey = key + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for pdns. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("pdns: the configuration of the DNS provider is nil") } - if host == nil || host.Host == "" { - return nil, fmt.Errorf("PDNS API URL missing") + if config.APIKey == "" { + return nil, fmt.Errorf("pdns: API key missing") } - d := &DNSProvider{ - host: host, - apiKey: key, - client: &http.Client{Timeout: 30 * time.Second}, + if config.Host == nil || config.Host.Host == "" { + return nil, fmt.Errorf("pdns: API URL missing") } + d := &DNSProvider{config: config} + apiVersion, err := d.getAPIVersion() if err != nil { - log.Warnf("PDNS: failed to get API version %v", err) + log.Warnf("pdns: failed to get API version %v", err) } d.apiVersion = apiVersion @@ -72,7 +107,7 @@ func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error) // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return 120 * time.Second, 2 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record to fulfil the dns-01 challenge @@ -80,7 +115,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := d.getHostedZone(fqdn) if err != nil { - return err + return fmt.Errorf("pdns: %v", err) } name := fqdn @@ -97,7 +132,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // pre-v1 API Type: "TXT", Name: name, - TTL: 120, + TTL: d.config.TTL, } rrsets := rrSets{ @@ -107,7 +142,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { ChangeType: "REPLACE", Type: "TXT", Kind: "Master", - TTL: 120, + TTL: d.config.TTL, Records: []pdnsRecord{rec}, }, }, @@ -115,11 +150,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { body, err := json.Marshal(rrsets) if err != nil { - return err + return fmt.Errorf("pdns: %v", err) } _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) - return err + if err != nil { + return fmt.Errorf("pdns: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters @@ -128,12 +166,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { zone, err := d.getHostedZone(fqdn) if err != nil { - return err + return fmt.Errorf("pdns: %v", err) } set, err := d.findTxtRecord(fqdn) if err != nil { - return err + return fmt.Errorf("pdns: %v", err) } rrsets := rrSets{ @@ -147,11 +185,14 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { } body, err := json.Marshal(rrsets) if err != nil { - return err + return fmt.Errorf("pdns: %v", err) } _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) - return err + if err != nil { + return fmt.Errorf("pdns: %v", err) + } + return nil } func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { @@ -161,8 +202,8 @@ func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { return nil, err } - url := "/servers/localhost/zones" - result, err := d.makeRequest(http.MethodGet, url, nil) + u := "/servers/localhost/zones" + result, err := d.makeRequest(http.MethodGet, u, nil) if err != nil { return nil, err } @@ -173,14 +214,14 @@ func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { return nil, err } - url = "" + u = "" for _, zone := range zones { if acme.UnFqdn(zone.Name) == acme.UnFqdn(authZone) { - url = zone.URL + u = zone.URL } } - result, err = d.makeRequest(http.MethodGet, url, nil) + result, err = d.makeRequest(http.MethodGet, u, nil) if err != nil { return nil, err } @@ -259,8 +300,8 @@ func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM } var path = "" - if d.host.Path != "/" { - path = d.host.Path + if d.config.Host.Path != "/" { + path = d.config.Host.Path } if !strings.HasPrefix(uri, "/") { @@ -271,15 +312,15 @@ func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM uri = "/api/v" + strconv.Itoa(d.apiVersion) + uri } - url := d.host.Scheme + "://" + d.host.Host + path + uri - req, err := http.NewRequest(method, url, body) + u := d.config.Host.Scheme + "://" + d.config.Host.Host + path + uri + req, err := http.NewRequest(method, u, body) if err != nil { return nil, err } - req.Header.Set("X-API-Key", d.apiKey) + req.Header.Set("X-API-Key", d.config.APIKey) - resp, err := d.client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("error talking to PDNS API -> %v", err) } @@ -287,7 +328,7 @@ func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM defer resp.Body.Close() if resp.StatusCode != http.StatusUnprocessableEntity && (resp.StatusCode < 200 || resp.StatusCode >= 300) { - return nil, fmt.Errorf("unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url) + return nil, fmt.Errorf("unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, u) } var msg json.RawMessage diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go new file mode 100644 index 000000000..ce2032762 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/rackspace/client.go @@ -0,0 +1,47 @@ +package rackspace + +// APIKeyCredentials API credential +type APIKeyCredentials struct { + Username string `json:"username"` + APIKey string `json:"apiKey"` +} + +// Auth auth credentials +type Auth struct { + APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"` +} + +// AuthData Auth data +type AuthData struct { + Auth `json:"auth"` +} + +// Identity Identity +type Identity struct { + Access struct { + ServiceCatalog []struct { + Endpoints []struct { + PublicURL string `json:"publicURL"` + TenantID string `json:"tenantId"` + } `json:"endpoints"` + Name string `json:"name"` + } `json:"serviceCatalog"` + Token struct { + ID string `json:"id"` + } `json:"token"` + } `json:"access"` +} + +// Records is the list of records sent/received from the DNS API +type Records struct { + Record []Record `json:"records"` +} + +// Record represents a Rackspace DNS record +type Record struct { + Name string `json:"name"` + Type string `json:"type"` + Data string `json:"data"` + TTL int `json:"ttl,omitempty"` + ID string `json:"id,omitempty"` +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go index 358af69d0..8e56f20a9 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go @@ -5,6 +5,7 @@ package rackspace import ( "bytes" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -14,42 +15,85 @@ import ( "github.com/xenolf/lego/platform/config/env" ) -// rackspaceAPIURL represents the Identity API endpoint to call -var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens" +// defaultBaseURL represents the Identity API endpoint to call +const defaultBaseURL = "https://identity.api.rackspacecloud.com/v2.0/tokens" + +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + APIUser string + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + BaseURL: defaultBaseURL, + TTL: env.GetOrDefaultInt("RACKSPACE_TTL", 300), + PropagationTimeout: env.GetOrDefaultSecond("RACKSPACE_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("RACKSPACE_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("RACKSPACE_HTTP_TIMEOUT", 30*time.Second), + }, + } +} // DNSProvider is an implementation of the acme.ChallengeProvider interface // used to store the reusable token and DNS API endpoint type DNSProvider struct { + config *Config token string cloudDNSEndpoint string - client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Rackspace. -// Credentials must be passed in the environment variables: RACKSPACE_USER -// and RACKSPACE_API_KEY. +// Credentials must be passed in the environment variables: +// RACKSPACE_USER and RACKSPACE_API_KEY. func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("RACKSPACE_USER", "RACKSPACE_API_KEY") if err != nil { - return nil, fmt.Errorf("Rackspace: %v", err) + return nil, fmt.Errorf("rackspace: %v", err) } - return NewDNSProviderCredentials(values["RACKSPACE_USER"], values["RACKSPACE_API_KEY"]) + config := NewDefaultConfig() + config.APIUser = values["RACKSPACE_USER"] + config.APIKey = values["RACKSPACE_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for Rackspace. It authenticates against -// the API, also grabbing the DNS Endpoint. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Rackspace. +// It authenticates against the API, also grabbing the DNS Endpoint. +// Deprecated func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { - if user == "" || key == "" { - return nil, fmt.Errorf("Rackspace credentials missing") + config := NewDefaultConfig() + config.APIUser = user + config.APIKey = key + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Rackspace. +// It authenticates against the API, also grabbing the DNS Endpoint. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("rackspace: the configuration of the DNS provider is nil") + } + + if config.APIUser == "" || config.APIKey == "" { + return nil, fmt.Errorf("rackspace: credentials missing") } authData := AuthData{ Auth: Auth{ APIKeyCredentials: APIKeyCredentials{ - Username: user, - APIKey: key, + Username: config.APIUser, + APIKey: config.APIKey, }, }, } @@ -59,46 +103,47 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, err } - req, err := http.NewRequest(http.MethodPost, rackspaceAPIURL, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, config.BaseURL, bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") - client := &http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + // client := &http.Client{Timeout: 30 * time.Second} + resp, err := config.HTTPClient.Do(req) if err != nil { - return nil, fmt.Errorf("error querying Rackspace Identity API: %v", err) + return nil, fmt.Errorf("rackspace: error querying Identity API: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode) + return nil, fmt.Errorf("rackspace: authentication failed: response code: %d", resp.StatusCode) } - var rackspaceIdentity Identity - err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity) + var identity Identity + err = json.NewDecoder(resp.Body).Decode(&identity) if err != nil { - return nil, err + return nil, fmt.Errorf("rackspace: %v", err) } // Iterate through the Service Catalog to get the DNS Endpoint var dnsEndpoint string - for _, service := range rackspaceIdentity.Access.ServiceCatalog { + for _, service := range identity.Access.ServiceCatalog { if service.Name == "cloudDNS" { dnsEndpoint = service.Endpoints[0].PublicURL break } } if dnsEndpoint == "" { - return nil, fmt.Errorf("failed to populate DNS endpoint, check Rackspace API for changes") + return nil, fmt.Errorf("rackspace: failed to populate DNS endpoint, check Rackspace API for changes") } return &DNSProvider{ - token: rackspaceIdentity.Access.Token.ID, + config: config, + token: identity.Access.Token.ID, cloudDNSEndpoint: dnsEndpoint, - client: client, }, nil + } // Present creates a TXT record to fulfil the dns-01 challenge @@ -106,7 +151,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := d.getHostedZoneID(fqdn) if err != nil { - return err + return fmt.Errorf("rackspace: %v", err) } rec := Records{ @@ -114,17 +159,20 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Name: acme.UnFqdn(fqdn), Type: "TXT", Data: value, - TTL: 300, + TTL: d.config.TTL, }}, } body, err := json.Marshal(rec) if err != nil { - return err + return fmt.Errorf("rackspace: %v", err) } _, err = d.makeRequest(http.MethodPost, fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) - return err + if err != nil { + return fmt.Errorf("rackspace: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters @@ -132,16 +180,25 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := d.getHostedZoneID(fqdn) if err != nil { - return err + return fmt.Errorf("rackspace: %v", err) } record, err := d.findTxtRecord(fqdn, zoneID) if err != nil { - return err + return fmt.Errorf("rackspace: %v", err) } _, err = d.makeRequest(http.MethodDelete, fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) - return err + if err != nil { + return fmt.Errorf("rackspace: %v", err) + } + return nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // getHostedZoneID performs a lookup to get the DNS zone which needs @@ -216,8 +273,7 @@ func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM req.Header.Set("X-Auth-Token", d.token) req.Header.Set("Content-Type", "application/json") - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.config.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("error querying DNS API: %v", err) } @@ -236,49 +292,3 @@ func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM return r, nil } - -// APIKeyCredentials API credential -type APIKeyCredentials struct { - Username string `json:"username"` - APIKey string `json:"apiKey"` -} - -// Auth auth credentials -type Auth struct { - APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"` -} - -// AuthData Auth data -type AuthData struct { - Auth `json:"auth"` -} - -// Identity Identity -type Identity struct { - Access struct { - ServiceCatalog []struct { - Endpoints []struct { - PublicURL string `json:"publicURL"` - TenantID string `json:"tenantId"` - } `json:"endpoints"` - Name string `json:"name"` - } `json:"serviceCatalog"` - Token struct { - ID string `json:"id"` - } `json:"token"` - } `json:"access"` -} - -// Records is the list of records sent/received from the DNS API -type Records struct { - Record []Record `json:"records"` -} - -// Record represents a Rackspace DNS record -type Record struct { - Name string `json:"name"` - Type string `json:"type"` - Data string `json:"data"` - TTL int `json:"ttl,omitempty"` - ID string `json:"id,omitempty"` -} diff --git a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go index ee0b7c14e..792ee8b0c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go @@ -3,6 +3,7 @@ package rfc2136 import ( + "errors" "fmt" "net" "os" @@ -11,16 +12,37 @@ import ( "github.com/miekg/dns" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) +const defaultTimeout = 60 * time.Second + +// Config is used to configure the creation of the DNSProvider +type Config struct { + Nameserver string + TSIGAlgorithm string + TSIGKey string + TSIGSecret string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TSIGAlgorithm: env.GetOrDefaultString("RFC2136_TSIG_ALGORITHM", dns.HmacMD5), + TTL: env.GetOrDefaultInt("RFC2136_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("RFC2136_PROPAGATION_TIMEOUT", + env.GetOrDefaultSecond("RFC2136_TIMEOUT", 60*time.Second)), + PollingInterval: env.GetOrDefaultSecond("RFC2136_POLLING_INTERVAL", 2*time.Second), + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface that // uses dynamic DNS updates (RFC 2136) to create TXT records on a nameserver. type DNSProvider struct { - nameserver string - tsigAlgorithm string - tsigKey string - tsigSecret string - timeout time.Duration + config *Config } // NewDNSProvider returns a DNSProvider instance configured for rfc2136 @@ -33,81 +55,110 @@ type DNSProvider struct { // RFC2136_TIMEOUT: DNS propagation timeout in time.ParseDuration format. (60s) // To disable TSIG authentication, leave the RFC2136_TSIG* variables unset. func NewDNSProvider() (*DNSProvider, error) { - nameserver := os.Getenv("RFC2136_NAMESERVER") - tsigAlgorithm := os.Getenv("RFC2136_TSIG_ALGORITHM") - tsigKey := os.Getenv("RFC2136_TSIG_KEY") - tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") - timeout := os.Getenv("RFC2136_TIMEOUT") + values, err := env.Get("RFC2136_NAMESERVER") + if err != nil { + return nil, fmt.Errorf("rfc2136: %v", err) + } - return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout) + config := NewDefaultConfig() + config.Nameserver = values["RFC2136_NAMESERVER"] + config.TSIGKey = os.Getenv("RFC2136_TSIG_KEY") + config.TSIGSecret = os.Getenv("RFC2136_TSIG_SECRET") + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for rfc2136 dynamic update. To disable TSIG -// authentication, leave the TSIG parameters as empty strings. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for rfc2136 dynamic update. +// To disable TSIG authentication, leave the TSIG parameters as empty strings. // nameserver must be a network address in the form "host" or "host:port". -func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout string) (*DNSProvider, error) { - if nameserver == "" { - return nil, fmt.Errorf("RFC2136 nameserver missing") - } +// Deprecated +func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, rawTimeout string) (*DNSProvider, error) { + config := NewDefaultConfig() + config.Nameserver = nameserver + config.TSIGAlgorithm = tsigAlgorithm + config.TSIGKey = tsigKey + config.TSIGSecret = tsigSecret - // Append the default DNS port if none is specified. - if _, _, err := net.SplitHostPort(nameserver); err != nil { - if strings.Contains(err.Error(), "missing port") { - nameserver = net.JoinHostPort(nameserver, "53") - } else { - return nil, err - } - } - - d := &DNSProvider{nameserver: nameserver} - - if tsigAlgorithm == "" { - tsigAlgorithm = dns.HmacMD5 - } - d.tsigAlgorithm = tsigAlgorithm - - if len(tsigKey) > 0 && len(tsigSecret) > 0 { - d.tsigKey = tsigKey - d.tsigSecret = tsigSecret - } - - if timeout == "" { - d.timeout = 60 * time.Second - } else { - t, err := time.ParseDuration(timeout) + timeout := defaultTimeout + if rawTimeout != "" { + t, err := time.ParseDuration(rawTimeout) if err != nil { return nil, err } else if t < 0 { - return nil, fmt.Errorf("invalid/negative RFC2136_TIMEOUT: %v", timeout) + return nil, fmt.Errorf("rfc2136: invalid/negative RFC2136_TIMEOUT: %v", rawTimeout) } else { - d.timeout = t + timeout = t + } + } + config.PropagationTimeout = timeout + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for rfc2136. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("rfc2136: the configuration of the DNS provider is nil") + } + + if config.Nameserver == "" { + return nil, fmt.Errorf("rfc2136: nameserver missing") + } + + if config.TSIGAlgorithm == "" { + config.TSIGAlgorithm = dns.HmacMD5 + } + + // Append the default DNS port if none is specified. + if _, _, err := net.SplitHostPort(config.Nameserver); err != nil { + if strings.Contains(err.Error(), "missing port") { + config.Nameserver = net.JoinHostPort(config.Nameserver, "53") + } else { + return nil, fmt.Errorf("rfc2136: %v", err) } } - return d, nil + if len(config.TSIGKey) == 0 && len(config.TSIGSecret) > 0 || + len(config.TSIGKey) > 0 && len(config.TSIGSecret) == 0 { + config.TSIGKey = "" + config.TSIGSecret = "" + } + + return &DNSProvider{config: config}, nil } -// Timeout Returns the timeout configured with RFC2136_TIMEOUT, or 60s. +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { - return d.timeout, 2 * time.Second + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - return d.changeRecord("INSERT", fqdn, value, ttl) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + err := d.changeRecord("INSERT", fqdn, value, d.config.TTL) + if err != nil { + return fmt.Errorf("rfc2136: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - return d.changeRecord("REMOVE", fqdn, value, ttl) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + err := d.changeRecord("REMOVE", fqdn, value, d.config.TTL) + if err != nil { + return fmt.Errorf("rfc2136: %v", err) + } + return nil } func (d *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { // Find the zone for the given fqdn - zone, err := acme.FindZoneByFqdn(fqdn, []string{d.nameserver}) + zone, err := acme.FindZoneByFqdn(fqdn, []string{d.config.Nameserver}) if err != nil { return err } @@ -135,14 +186,15 @@ func (d *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { // Setup client c := new(dns.Client) c.SingleInflight = true + // TSIG authentication / msg signing - if len(d.tsigKey) > 0 && len(d.tsigSecret) > 0 { - m.SetTsig(dns.Fqdn(d.tsigKey), d.tsigAlgorithm, 300, time.Now().Unix()) - c.TsigSecret = map[string]string{dns.Fqdn(d.tsigKey): d.tsigSecret} + if len(d.config.TSIGKey) > 0 && len(d.config.TSIGSecret) > 0 { + m.SetTsig(dns.Fqdn(d.config.TSIGKey), d.config.TSIGAlgorithm, 300, time.Now().Unix()) + c.TsigSecret = map[string]string{dns.Fqdn(d.config.TSIGKey): d.config.TSIGSecret} } // Send the query - reply, _, err := c.Exchange(m, d.nameserver) + reply, _, err := c.Exchange(m, d.config.Nameserver) if err != nil { return fmt.Errorf("DNS update failed: %v", err) } diff --git a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go index 71fd4e859..a1d4a8a3e 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go +++ b/vendor/github.com/xenolf/lego/providers/dns/route53/route53.go @@ -30,13 +30,11 @@ type Config struct { // NewDefaultConfig returns a default configuration for the DNSProvider func NewDefaultConfig() *Config { - propagationMins := env.GetOrDefaultInt("AWS_PROPAGATION_TIMEOUT", 2) - intervalSecs := env.GetOrDefaultInt("AWS_POLLING_INTERVAL", 4) return &Config{ MaxRetries: env.GetOrDefaultInt("AWS_MAX_RETRIES", 5), TTL: env.GetOrDefaultInt("AWS_TTL", 10), - PropagationTimeout: time.Second * time.Duration(propagationMins), - PollingInterval: time.Second * time.Duration(intervalSecs), + PropagationTimeout: env.GetOrDefaultSecond("AWS_PROPAGATION_TIMEOUT", 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond("AWS_POLLING_INTERVAL", 4*time.Second), HostedZoneID: os.Getenv("AWS_HOSTED_ZONE_ID"), } } @@ -91,20 +89,20 @@ func NewDNSProvider() (*DNSProvider, error) { // DNSProvider instance func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { - return nil, errors.New("the configuration of the Route53 DNS provider is nil") + return nil, errors.New("route53: the configuration of the Route53 DNS provider is nil") } r := customRetryer{} r.NumMaxRetries = config.MaxRetries sessionCfg := request.WithRetryer(aws.NewConfig(), r) - session, err := session.NewSessionWithOptions(session.Options{Config: *sessionCfg}) + sess, err := session.NewSessionWithOptions(session.Options{Config: *sessionCfg}) if err != nil { return nil, err } - client := route53.New(session) + cl := route53.New(sess) return &DNSProvider{ - client: client, + client: cl, config: config, }, nil } @@ -118,15 +116,23 @@ func (r *DNSProvider) Timeout() (timeout, interval time.Duration) { // Present creates a TXT record using the specified parameters func (r *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - value = `"` + value + `"` - return r.changeRecord("UPSERT", fqdn, value, r.config.TTL) + + err := r.changeRecord("UPSERT", fqdn, `"`+value+`"`, r.config.TTL) + if err != nil { + return fmt.Errorf("route53: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - value = `"` + value + `"` - return r.changeRecord("DELETE", fqdn, value, r.config.TTL) + + err := r.changeRecord("DELETE", fqdn, `"`+value+`"`, r.config.TTL) + if err != nil { + return fmt.Errorf("route53: %v", err) + } + return nil } func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { @@ -151,7 +157,7 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { resp, err := r.client.ChangeResourceRecordSets(reqParams) if err != nil { - return fmt.Errorf("failed to change Route 53 record set: %v", err) + return fmt.Errorf("failed to change record set: %v", err) } statusID := resp.ChangeInfo.Id @@ -162,7 +168,7 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { } resp, err := r.client.GetChange(reqParams) if err != nil { - return false, fmt.Errorf("failed to query Route 53 change status: %v", err) + return false, fmt.Errorf("failed to query change status: %v", err) } if aws.StringValue(resp.ChangeInfo.Status) == route53.ChangeStatusInsync { return true, nil @@ -200,7 +206,7 @@ func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) { } if len(hostedZoneID) == 0 { - return "", fmt.Errorf("zone %s not found in Route 53 for domain %s", authZone, fqdn) + return "", fmt.Errorf("zone %s not found for domain %s", authZone, fqdn) } if strings.HasPrefix(hostedZoneID, "/hostedzone/") { diff --git a/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go b/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go index 17ca694a6..b746fd66a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strings" + "time" "github.com/sacloud/libsacloud/api" "github.com/sacloud/libsacloud/sacloud" @@ -14,8 +15,27 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + Token string + Secret string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("SAKURACLOUD_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("SAKURACLOUD_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("SAKURACLOUD_POLLING_INTERVAL", acme.DefaultPollingInterval), + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { + config *Config client *api.Client } @@ -24,23 +44,42 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("SAKURACLOUD_ACCESS_TOKEN", "SAKURACLOUD_ACCESS_TOKEN_SECRET") if err != nil { - return nil, fmt.Errorf("SakuraCloud: %v", err) + return nil, fmt.Errorf("sakuracloud: %v", err) } - return NewDNSProviderCredentials(values["SAKURACLOUD_ACCESS_TOKEN"], values["SAKURACLOUD_ACCESS_TOKEN_SECRET"]) + config := NewDefaultConfig() + config.Token = values["SAKURACLOUD_ACCESS_TOKEN"] + config.Secret = values["SAKURACLOUD_ACCESS_TOKEN_SECRET"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for sakuracloud. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for sakuracloud. +// Deprecated func NewDNSProviderCredentials(token, secret string) (*DNSProvider, error) { - if token == "" { - return nil, errors.New("SakuraCloud AccessToken is missing") - } - if secret == "" { - return nil, errors.New("SakuraCloud AccessSecret is missing") + config := NewDefaultConfig() + config.Token = token + config.Secret = secret + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for GleSYS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("sakuracloud: the configuration of the DNS provider is nil") } - client := api.NewClient(token, secret, "tk1a") + if config.Token == "" { + return nil, errors.New("sakuracloud: AccessToken is missing") + } + + if config.Secret == "" { + return nil, errors.New("sakuracloud: AccessSecret is missing") + } + + client := api.NewClient(config.Token, config.Secret, "tk1a") client.UserAgent = acme.UserAgent return &DNSProvider{client: client}, nil @@ -48,19 +87,19 @@ func NewDNSProviderCredentials(token, secret string) (*DNSProvider, error) { // Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("sakuracloud: %v", err) } name := d.extractRecordName(fqdn, zone.Name) - zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, ttl)) + zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, d.config.TTL)) _, err = d.client.GetDNSAPI().Update(zone.ID, zone) if err != nil { - return fmt.Errorf("SakuraCloud API call failed: %v", err) + return fmt.Errorf("sakuracloud: API call failed: %v", err) } return nil @@ -72,12 +111,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { zone, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("sakuracloud: %v", err) } records, err := d.findTxtRecords(fqdn, zone) if err != nil { - return err + return fmt.Errorf("sakuracloud: %v", err) } for _, record := range records { @@ -92,12 +131,18 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { _, err = d.client.GetDNSAPI().Update(zone.ID, zone) if err != nil { - return fmt.Errorf("SakuraCloud API call failed: %v", err) + return fmt.Errorf("sakuracloud: API call failed: %v", err) } return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { @@ -111,7 +156,7 @@ func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { if notFound, ok := err.(api.Error); ok && notFound.ResponseCode() == http.StatusNotFound { return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS: %v", zoneName, err) } - return nil, fmt.Errorf("SakuraCloud API call failed: %v", err) + return nil, fmt.Errorf("API call failed: %v", err) } for _, zone := range res.CommonServiceDNSItems { @@ -120,7 +165,7 @@ func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { } } - return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS", zoneName) + return nil, fmt.Errorf("zone %s not found", zoneName) } func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) ([]sacloud.DNSRecordSet, error) { diff --git a/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go b/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go index 4d5371b85..034d5ed4d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go @@ -3,6 +3,7 @@ package vegadns import ( + "errors" "fmt" "os" "strings" @@ -13,8 +14,28 @@ import ( "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + BaseURL string + APIKey string + APISecret string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("VEGADNS_TTL", 10), + PropagationTimeout: env.GetOrDefaultSecond("VEGADNS_PROPAGATION_TIMEOUT", 12*time.Minute), + PollingInterval: env.GetOrDefaultSecond("VEGADNS_POLLING_INTERVAL", 1*time.Minute), + } +} + // DNSProvider describes a provider for VegaDNS type DNSProvider struct { + config *Config client vegaClient.VegaDNSClient } @@ -24,62 +45,83 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("VEGADNS_URL") if err != nil { - return nil, fmt.Errorf("VegaDNS: %v", err) + return nil, fmt.Errorf("vegadns: %v", err) } - key := os.Getenv("SECRET_VEGADNS_KEY") - secret := os.Getenv("SECRET_VEGADNS_SECRET") + config := NewDefaultConfig() + config.BaseURL = values["VEGADNS_URL"] + config.APIKey = os.Getenv("SECRET_VEGADNS_KEY") + config.APISecret = os.Getenv("SECRET_VEGADNS_SECRET") - return NewDNSProviderCredentials(values["VEGADNS_URL"], key, secret) + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a -// DNSProvider instance configured for VegaDNS. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for VegaDNS. +// Deprecated func NewDNSProviderCredentials(vegaDNSURL string, key string, secret string) (*DNSProvider, error) { - vega := vegaClient.NewVegaDNSClient(vegaDNSURL) - vega.APIKey = key - vega.APISecret = secret + config := NewDefaultConfig() + config.BaseURL = vegaDNSURL + config.APIKey = key + config.APISecret = secret - return &DNSProvider{ - client: vega, - }, nil + return NewDNSProviderConfig(config) } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. -func (r *DNSProvider) Timeout() (timeout, interval time.Duration) { - timeout = 12 * time.Minute - interval = 1 * time.Minute - return +// NewDNSProviderConfig return a DNSProvider instance configured for VegaDNS. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("vegadns: the configuration of the DNS provider is nil") + } + + vega := vegaClient.NewVegaDNSClient(config.BaseURL) + vega.APIKey = config.APIKey + vega.APISecret = config.APISecret + + return &DNSProvider{client: vega, config: config}, nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval } // Present creates a TXT record to fulfil the dns-01 challenge -func (r *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - _, domainID, err := r.client.GetAuthZone(fqdn) + _, domainID, err := d.client.GetAuthZone(fqdn) if err != nil { - return fmt.Errorf("can't find Authoritative Zone for %s in Present: %v", fqdn, err) + return fmt.Errorf("vegadns: can't find Authoritative Zone for %s in Present: %v", fqdn, err) } - return r.client.CreateTXT(domainID, fqdn, value, 10) + err = d.client.CreateTXT(domainID, fqdn, value, d.config.TTL) + if err != nil { + return fmt.Errorf("vegadns: %v", err) + } + return nil } // CleanUp removes the TXT record matching the specified parameters -func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - _, domainID, err := r.client.GetAuthZone(fqdn) + _, domainID, err := d.client.GetAuthZone(fqdn) if err != nil { - return fmt.Errorf("can't find Authoritative Zone for %s in CleanUp: %v", fqdn, err) + return fmt.Errorf("vegadns: can't find Authoritative Zone for %s in CleanUp: %v", fqdn, err) } txt := strings.TrimSuffix(fqdn, ".") - recordID, err := r.client.GetRecordID(domainID, txt, "TXT") + recordID, err := d.client.GetRecordID(domainID, txt, "TXT") if err != nil { - return fmt.Errorf("couldn't get Record ID in CleanUp: %s", err) + return fmt.Errorf("vegadns: couldn't get Record ID in CleanUp: %s", err) } - return r.client.DeleteRecord(recordID) + err = d.client.DeleteRecord(recordID) + if err != nil { + return fmt.Errorf("vegadns: %v", err) + } + return nil } diff --git a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go index 27cfa524a..e6a2a336e 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go @@ -4,16 +4,46 @@ package vultr import ( + "crypto/tls" + "errors" "fmt" + "net/http" "strings" + "time" vultr "github.com/JamesClonk/vultr/lib" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/platform/config/env" ) +// Config is used to configure the creation of the DNSProvider +type Config struct { + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPClient *http.Client +} + +// NewDefaultConfig returns a default configuration for the DNSProvider +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt("VULTR_TTL", 120), + PropagationTimeout: env.GetOrDefaultSecond("VULTR_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond("VULTR_POLLING_INTERVAL", acme.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond("VULTR_HTTP_TIMEOUT", 0), + // from Vultr Client + Transport: &http.Transport{ + TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper), + }, + }, + } +} + // DNSProvider is an implementation of the acme.ChallengeProvider interface. type DNSProvider struct { + config *Config client *vultr.Client } @@ -22,36 +52,58 @@ type DNSProvider struct { func NewDNSProvider() (*DNSProvider, error) { values, err := env.Get("VULTR_API_KEY") if err != nil { - return nil, fmt.Errorf("Vultr: %v", err) + return nil, fmt.Errorf("vultr: %v", err) } - return NewDNSProviderCredentials(values["VULTR_API_KEY"]) + config := NewDefaultConfig() + config.APIKey = values["VULTR_API_KEY"] + + return NewDNSProviderConfig(config) } -// NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider -// instance configured for Vultr. +// NewDNSProviderCredentials uses the supplied credentials +// to return a DNSProvider instance configured for Vultr. +// Deprecated func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { - if apiKey == "" { - return nil, fmt.Errorf("Vultr credentials missing") + config := NewDefaultConfig() + config.APIKey = apiKey + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Vultr. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("vultr: the configuration of the DNS provider is nil") } - return &DNSProvider{client: vultr.NewClient(apiKey, nil)}, nil + if config.APIKey == "" { + return nil, fmt.Errorf("vultr: credentials missing") + } + + options := &vultr.Options{ + HTTPClient: config.HTTPClient, + UserAgent: acme.UserAgent, + } + client := vultr.NewClient(config.APIKey, options) + + return &DNSProvider{client: client, config: config}, nil } // Present creates a TXT record to fulfil the DNS-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneDomain, err := d.getHostedZone(domain) if err != nil { - return err + return fmt.Errorf("vultr: %v", err) } name := d.extractRecordName(fqdn, zoneDomain) - err = d.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) + err = d.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, d.config.TTL) if err != nil { - return fmt.Errorf("Vultr API call failed: %v", err) + return fmt.Errorf("vultr: API call failed: %v", err) } return nil @@ -63,22 +115,34 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { zoneDomain, records, err := d.findTxtRecords(domain, fqdn) if err != nil { - return err + return fmt.Errorf("vultr: %v", err) } + var allErr []string for _, rec := range records { err := d.client.DeleteDNSRecord(zoneDomain, rec.RecordID) if err != nil { - return err + allErr = append(allErr, err.Error()) } } + + if len(allErr) > 0 { + return errors.New(strings.Join(allErr, ": ")) + } + return nil } +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + func (d *DNSProvider) getHostedZone(domain string) (string, error) { domains, err := d.client.GetDNSDomains() if err != nil { - return "", fmt.Errorf("Vultr API call failed: %v", err) + return "", fmt.Errorf("API call failed: %v", err) } var hostedDomain vultr.DNSDomain @@ -90,7 +154,7 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) { } } if hostedDomain.Domain == "" { - return "", fmt.Errorf("No matching Vultr domain found for domain %s", domain) + return "", fmt.Errorf("no matching Vultr domain found for domain %s", domain) } return hostedDomain.Domain, nil @@ -105,7 +169,7 @@ func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRe var records []vultr.DNSRecord result, err := d.client.GetDNSRecords(zoneDomain) if err != nil { - return "", records, fmt.Errorf("Vultr API call has failed: %v", err) + return "", records, fmt.Errorf("API call has failed: %v", err) } recordName := d.extractRecordName(fqdn, zoneDomain) From f3aefe282cdef53c340efb6dd3ad67b031169638 Mon Sep 17 00:00:00 2001 From: NicoMen Date: Mon, 17 Sep 2018 16:26:03 +0200 Subject: [PATCH 06/10] Avoid panic during stop --- server/server.go | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/server/server.go b/server/server.go index 4415223b7..d19462278 100644 --- a/server/server.go +++ b/server/server.go @@ -137,29 +137,35 @@ type serverEntryPoint struct { func (s serverEntryPoint) Shutdown(ctx context.Context) { var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - if err := s.httpServer.Shutdown(ctx); err != nil { - if ctx.Err() == context.DeadlineExceeded { - log.Debugf("Wait server shutdown is over due to: %s", err) - err = s.httpServer.Close() - if err != nil { - log.Error(err) + if s.httpServer != nil { + wg.Add(1) + go func() { + defer wg.Done() + if err := s.httpServer.Shutdown(ctx); err != nil { + if ctx.Err() == context.DeadlineExceeded { + log.Debugf("Wait server shutdown is over due to: %s", err) + err = s.httpServer.Close() + if err != nil { + log.Error(err) + } } } - } - }() - wg.Add(1) - go func() { - defer wg.Done() - if err := s.hijackConnectionTracker.Shutdown(ctx); err != nil { - if ctx.Err() == context.DeadlineExceeded { - log.Debugf("Wait hijack connection is over due to: %s", err) - s.hijackConnectionTracker.Close() + }() + } + + if s.hijackConnectionTracker != nil { + wg.Add(1) + go func() { + defer wg.Done() + if err := s.hijackConnectionTracker.Shutdown(ctx); err != nil { + if ctx.Err() == context.DeadlineExceeded { + log.Debugf("Wait hijack connection is over due to: %s", err) + s.hijackConnectionTracker.Close() + } } - } - }() + }() + } + wg.Wait() } From 0ae8cd9a9d39d391256dff6e0588d35ba597f4ab Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Mon, 17 Sep 2018 20:40:04 +0200 Subject: [PATCH 07/10] Fix error pages --- server/server_configuration.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/server_configuration.go b/server/server_configuration.go index a9e5b0e59..eec23f8f3 100644 --- a/server/server_configuration.go +++ b/server/server_configuration.go @@ -184,6 +184,11 @@ func (s *Server) loadFrontendConfig( return nil, err } + // Handler used by error pages + if backendsHandlers[entryPointName+providerName+frontend.Backend] == nil { + backendsHandlers[entryPointName+providerName+frontend.Backend] = lb + } + if healthCheckConfig != nil { backendsHealthCheck[entryPointName+providerName+frontendHash] = healthCheckConfig } From ab2c98d931b167c305f4dc19ff3fb16057693060 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Tue, 18 Sep 2018 01:22:03 -0500 Subject: [PATCH 08/10] Ensure only certificates from ACME enabled entrypoint are used --- cmd/traefik/traefik.go | 14 +++-- integration/acme_test.go | 20 ++++++- .../acme/acme_tls_multiple_entrypoints.toml | 59 +++++++++++++++++++ integration/https_test.go | 28 ++++----- 4 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 integration/fixtures/acme/acme_tls_multiple_entrypoints.toml diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 8cf540dac..412027507 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -200,22 +200,24 @@ func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile s internalRouter := router.NewInternalRouterAggregator(*globalConfiguration, entryPointName) if acmeprovider != nil { - if acmeprovider.HTTPChallenge != nil && acmeprovider.HTTPChallenge.EntryPoint == entryPointName { + if acmeprovider.HTTPChallenge != nil && entryPointName == acmeprovider.HTTPChallenge.EntryPoint { internalRouter.AddRouter(acmeprovider) } // TLS ALPN 01 - if acmeprovider.HTTPChallenge == nil && acmeprovider.DNSChallenge == nil && acmeprovider.TLSChallenge != nil { + if acmeprovider.TLSChallenge != nil && acmeprovider.HTTPChallenge == nil && acmeprovider.DNSChallenge == nil { entryPoint.TLSALPNGetter = acmeprovider.GetTLSALPNCertificate } - if acmeprovider.EntryPoint == entryPointName && acmeprovider.OnDemand { + if acmeprovider.OnDemand && entryPointName == acmeprovider.EntryPoint { entryPoint.OnDemandListener = acmeprovider.ListenRequest } - entryPoint.CertificateStore = traefiktls.NewCertificateStore() - acmeprovider.SetCertificateStore(entryPoint.CertificateStore) - + if entryPointName == acmeprovider.EntryPoint { + entryPoint.CertificateStore = traefiktls.NewCertificateStore() + acmeprovider.SetCertificateStore(entryPoint.CertificateStore) + log.Debugf("Setting Acme Certificate store from Entrypoint: %s", entryPointName) + } } entryPoint.InternalRouter = internalRouter diff --git a/integration/acme_test.go b/integration/acme_test.go index 906f48cf2..ebb5342c6 100644 --- a/integration/acme_test.go +++ b/integration/acme_test.go @@ -287,6 +287,22 @@ func (s *AcmeSuite) TestHTTP01OnDemandStaticCertificatesWithWildcard(c *check.C) s.retrieveAcmeCertificate(c, testCase) } +func (s *AcmeSuite) TestHTTP01OnDemandStaticCertificatesWithWildcardMultipleEntrypoints(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme_tls_multiple_entrypoints.toml", + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnDemand: true, + }, + }, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + func (s *AcmeSuite) TestHTTP01OnDemandDynamicCertificatesWithWildcard(c *check.C) { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml", @@ -379,11 +395,11 @@ func (s *AcmeSuite) TestTLSALPN01DomainsWithProvidedWildcardDomainAtStart(c *che Acme: acme.Configuration{ TLSChallenge: &acme.TLSChallenge{}, Domains: types.Domains{types.Domain{ - Main: "traefik.acme.wtf", + Main: acmeDomain, }}, }, }, - expectedCommonName: "traefik.acme.wtf", + expectedCommonName: wildcardDomain, expectedAlgorithm: x509.RSA, } diff --git a/integration/fixtures/acme/acme_tls_multiple_entrypoints.toml b/integration/fixtures/acme/acme_tls_multiple_entrypoints.toml new file mode 100644 index 000000000..4288b0225 --- /dev/null +++ b/integration/fixtures/acme/acme_tls_multiple_entrypoints.toml @@ -0,0 +1,59 @@ +logLevel = "DEBUG" + +defaultEntryPoints = ["http", "https"] + +[entryPoints] + [entryPoints.http] + address = "{{ .PortHTTP }}" + [entryPoints.https] + address = "{{ .PortHTTPS }}" + [entryPoints.https.tls] + [entryPoints.traefik] + address = ":9000" + [entryPoints.traefik.tls] + [[entryPoints.traefik.tls.certificates]] + certFile = "fixtures/acme/ssl/wildcard.crt" + keyFile = "fixtures/acme/ssl/wildcard.key" + +[acme] + email = "test@traefik.io" + storage = "/tmp/acme.json" + entryPoint = "https" + acmeLogging = true + onDemand = {{ .Acme.OnDemand }} + onHostRule = {{ .Acme.OnHostRule }} + keyType = "{{ .Acme.KeyType }}" + caServer = "{{ .Acme.CAServer }}" + + {{if .Acme.HTTPChallenge }} + [acme.httpChallenge] + entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}" + {{end}} + + {{if .Acme.TLSChallenge }} + [acme.tlsChallenge] + {{end}} + + {{range .Acme.Domains}} + [[acme.domains]] + main = "{{ .Main }}" + sans = [{{range .SANs }} + "{{.}}", + {{end}}] + {{end}} + +[api] + +[file] + +[backends] + [backends.backend] + [backends.backend.servers.server1] + url = "http://127.0.0.1:9010" + weight = 1 + +[frontends] + [frontends.frontend] + backend = "backend" + [frontends.frontend.routes.test] + rule = "Host:traefik.acme.wtf" diff --git a/integration/https_test.go b/integration/https_test.go index cf6dee11e..1eade1045 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -704,31 +704,31 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion(c // modifyCertificateConfFileContent replaces the content of a HTTPS configuration file. func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName, entryPoint string) { - f, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive) + file, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive) c.Assert(err, checker.IsNil) defer func() { - f.Close() + file.Close() }() - f.Truncate(0) + err = file.Truncate(0) + c.Assert(err, checker.IsNil) + // If certificate file is not provided, just truncate the configuration file if len(certFileName) > 0 { tlsConf := types.Configuration{ - TLS: []*traefiktls.Configuration{ - { - Certificate: &traefiktls.Certificate{ - CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"), - KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"), - }, - EntryPoints: []string{entryPoint}, + TLS: []*traefiktls.Configuration{{ + Certificate: &traefiktls.Certificate{ + CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"), + KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"), }, - }, + EntryPoints: []string{entryPoint}, + }}, } + var confBuffer bytes.Buffer - e := toml.NewEncoder(&confBuffer) - err := e.Encode(tlsConf) + err := toml.NewEncoder(&confBuffer).Encode(tlsConf) c.Assert(err, checker.IsNil) - _, err = f.Write(confBuffer.Bytes()) + _, err = file.Write(confBuffer.Bytes()) c.Assert(err, checker.IsNil) } } From c2938ff1385514819d40fd03884999eaf58cd6f2 Mon Sep 17 00:00:00 2001 From: stffabi Date: Tue, 18 Sep 2018 14:22:03 +0200 Subject: [PATCH 09/10] Remove hop-by-hop headers from forward auth response --- middlewares/auth/forward.go | 1 + middlewares/auth/forward_test.go | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/middlewares/auth/forward.go b/middlewares/auth/forward.go index 12e885fda..86c365e8c 100644 --- a/middlewares/auth/forward.go +++ b/middlewares/auth/forward.go @@ -73,6 +73,7 @@ func Forward(config *types.Forward, w http.ResponseWriter, r *http.Request, next log.Debugf("Remote error %s. StatusCode: %d", config.Address, forwardResponse.StatusCode) utils.CopyHeaders(w.Header(), forwardResponse.Header) + utils.RemoveHeaders(w.Header(), forward.HopHeaders...) // Grab the location header, if any. redirectURL, err := forwardResponse.Location() diff --git a/middlewares/auth/forward_test.go b/middlewares/auth/forward_test.go index 72ce246b7..bf0ada6fb 100644 --- a/middlewares/auth/forward_test.go +++ b/middlewares/auth/forward_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/negroni" + "github.com/vulcand/oxy/forward" ) func TestForwardAuthFail(t *testing.T) { @@ -122,6 +123,59 @@ func TestForwardAuthRedirect(t *testing.T) { assert.NotEmpty(t, string(body), "there should be something in the body") } +func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) { + authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + for _, header := range forward.HopHeaders { + if header == forward.TransferEncoding { + headers.Add(header, "identity") + } else { + headers.Add(header, "test") + } + } + + http.Redirect(w, r, "http://example.com/redirect-test", http.StatusFound) + })) + defer authTs.Close() + + authMiddleware, err := NewAuthenticator(&types.Auth{ + Forward: &types.Forward{ + Address: authTs.URL, + }, + }, &tracing.Tracing{}) + assert.NoError(t, err, "there should be no error") + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "traefik") + }) + n := negroni.New(authMiddleware) + n.UseHandler(handler) + ts := httptest.NewServer(n) + defer ts.Close() + + client := &http.Client{ + CheckRedirect: func(r *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) + res, err := client.Do(req) + assert.NoError(t, err, "there should be no error") + assert.Equal(t, http.StatusFound, res.StatusCode, "they should be equal") + + for _, header := range forward.HopHeaders { + assert.Equal(t, "", res.Header.Get(header), "hop-by-hop header '%s' mustn't be set", header) + } + + location, err := res.Location() + assert.NoError(t, err, "there should be no error") + assert.Equal(t, "http://example.com/redirect-test", location.String(), "they should be equal") + + body, err := ioutil.ReadAll(res.Body) + assert.NoError(t, err, "there should be no error") + assert.NotEmpty(t, string(body), "there should be something in the body") +} + func TestForwardAuthFailResponseHeaders(t *testing.T) { authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie := &http.Cookie{Name: "example", Value: "testing", Path: "/"} From f63873cc73f367f87bb3768481e30e6b7099f7dc Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Tue, 18 Sep 2018 15:36:03 +0200 Subject: [PATCH 10/10] Prepare release 1.7.0-rc5 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e384caf7..a73ecd214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## [v1.7.0-rc5](https://github.com/containous/traefik/tree/v1.7.0-rc5) (2018-09-18) +[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc4...v1.7.0-rc5) + +**Bug fixes:** +- **[acme]** Ensure only certificates from ACME enabled entrypoint are used ([#3880](https://github.com/containous/traefik/pull/3880) by [dtomcej](https://github.com/dtomcej)) +- **[acme]** Fix LEGO update ([#3895](https://github.com/containous/traefik/pull/3895) by [ldez](https://github.com/ldez)) +- **[acme]** Bump LEGO version ([#3888](https://github.com/containous/traefik/pull/3888) by [ldez](https://github.com/ldez)) +- **[authentication,middleware]** Remove hop-by-hop headers from forward auth response ([#3900](https://github.com/containous/traefik/pull/3900) by [stffabi](https://github.com/stffabi)) +- **[kv]** Add missing quotes around backendName in kv template ([#3885](https://github.com/containous/traefik/pull/3885) by [NatMarchand](https://github.com/NatMarchand)) +- **[logs]** Add interface to Træfik logger ([#3889](https://github.com/containous/traefik/pull/3889) by [nmengin](https://github.com/nmengin)) +- **[middleware,tracing]** Fix tracing duplicated headers ([#3878](https://github.com/containous/traefik/pull/3878) by [mmatur](https://github.com/mmatur)) +- **[middleware]** Fix error pages ([#3894](https://github.com/containous/traefik/pull/3894) by [Juliens](https://github.com/Juliens)) +- **[server]** Avoid panic during stop ([#3898](https://github.com/containous/traefik/pull/3898) by [nmengin](https://github.com/nmengin)) + ## [v1.7.0-rc4](https://github.com/containous/traefik/tree/v1.7.0-rc4) (2018-09-07) [All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc3...v1.7.0-rc4)