From 139f280f35975e9b3b8777575652bc49bdd390be Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 3 Jul 2018 12:44:04 +0200 Subject: [PATCH] ACME TLS ALPN --- Gopkg.lock | 26 +- acme/account.go | 2 +- acme/acme.go | 31 +- acme/challenge_tls_provider.go | 127 ++ cmd/traefik/traefik.go | 5 + configuration/configuration.go | 13 +- docs/configuration/acme.md | 111 +- integration/acme_test.go | 458 ++++--- integration/fake_dns_server.go | 114 ++ integration/fixtures/acme/acme-base.toml | 22 +- .../fixtures/acme/acme_http01_web_path.toml | 18 +- integration/fixtures/acme/acme_tls.toml | 18 +- .../fixtures/acme/acme_tls_dynamic.toml | 19 +- .../fixtures/acme/ssl/pebble.minica.pem | 19 + integration/resources/compose/boulder.yml | 49 - integration/resources/compose/peddle.yml | 10 + provider/acme/account.go | 5 +- .../acme/{challenge.go => challenge_http.go} | 122 +- provider/acme/challenge_tls.go | 52 + provider/acme/local_store.go | 49 +- provider/acme/provider.go | 748 ++++++----- provider/acme/store.go | 6 + server/server.go | 20 +- server/server_configuration.go | 9 +- tls/generate/generate.go | 24 +- .../github.com/OpenDNS/vegadns2client/LICENSE | 13 + .../OpenDNS/vegadns2client/client.go | 71 + .../OpenDNS/vegadns2client/domains.go | 80 ++ .../github.com/OpenDNS/vegadns2client/main.go | 19 + .../OpenDNS/vegadns2client/records.go | 113 ++ .../OpenDNS/vegadns2client/tokens.go | 74 ++ .../github.com/sacloud/libsacloud/LICENSE.txt | 207 +++ .../sacloud/libsacloud/api/archive.go | 344 +++++ .../sacloud/libsacloud/api/archive_gen.go | 251 ++++ .../sacloud/libsacloud/api/auth_status.go | 42 + .../sacloud/libsacloud/api/auto_backup.go | 116 ++ .../sacloud/libsacloud/api/auto_backup_gen.go | 239 ++++ .../sacloud/libsacloud/api/base_api.go | 277 ++++ .../github.com/sacloud/libsacloud/api/bill.go | 185 +++ .../sacloud/libsacloud/api/bridge.go | 18 + .../sacloud/libsacloud/api/bridge_gen.go | 243 ++++ .../sacloud/libsacloud/api/cdrom.go | 80 ++ .../sacloud/libsacloud/api/cdrom_gen.go | 250 ++++ .../sacloud/libsacloud/api/client.go | 608 +++++++++ .../sacloud/libsacloud/api/database.go | 429 ++++++ .../sacloud/libsacloud/api/database_gen.go | 238 ++++ .../github.com/sacloud/libsacloud/api/disk.go | 283 ++++ .../sacloud/libsacloud/api/disk_gen.go | 228 ++++ .../github.com/sacloud/libsacloud/api/dns.go | 206 +++ .../sacloud/libsacloud/api/dns_gen.go | 238 ++++ .../github.com/sacloud/libsacloud/api/doc.go | 176 +++ .../sacloud/libsacloud/api/error.go | 79 ++ .../github.com/sacloud/libsacloud/api/gslb.go | 202 +++ .../sacloud/libsacloud/api/gslb_gen.go | 238 ++++ .../github.com/sacloud/libsacloud/api/icon.go | 31 + .../sacloud/libsacloud/api/icon_gen.go | 247 ++++ .../sacloud/libsacloud/api/interface.go | 83 ++ .../sacloud/libsacloud/api/interface_gen.go | 243 ++++ .../sacloud/libsacloud/api/internet.go | 143 ++ .../sacloud/libsacloud/api/internet_gen.go | 243 ++++ .../sacloud/libsacloud/api/ipaddress.go | 56 + .../sacloud/libsacloud/api/ipaddress_gen.go | 231 ++++ .../sacloud/libsacloud/api/ipv6addr.go | 96 ++ .../sacloud/libsacloud/api/ipv6addr_gen.go | 232 ++++ .../sacloud/libsacloud/api/ipv6net.go | 18 + .../sacloud/libsacloud/api/ipv6net_gen.go | 229 ++++ .../sacloud/libsacloud/api/license.go | 18 + .../sacloud/libsacloud/api/license_gen.go | 243 ++++ .../sacloud/libsacloud/api/load_balancer.go | 232 ++++ .../libsacloud/api/load_balancer_gen.go | 238 ++++ .../sacloud/libsacloud/api/mobile_gateway.go | 414 ++++++ .../libsacloud/api/mobile_gateway_gen.go | 238 ++++ .../sacloud/libsacloud/api/newsfeed.go | 50 + .../github.com/sacloud/libsacloud/api/nfs.go | 234 ++++ .../sacloud/libsacloud/api/nfs_gen.go | 238 ++++ .../github.com/sacloud/libsacloud/api/note.go | 18 + .../sacloud/libsacloud/api/note_gen.go | 247 ++++ .../sacloud/libsacloud/api/packet_filter.go | 18 + .../libsacloud/api/packet_filter_gen.go | 243 ++++ .../sacloud/libsacloud/api/polling.go | 156 +++ .../sacloud/libsacloud/api/private_host.go | 18 + .../libsacloud/api/private_host_gen.go | 243 ++++ .../sacloud/libsacloud/api/product_disk.go | 18 + .../libsacloud/api/product_disk_gen.go | 239 ++++ .../libsacloud/api/product_internet.go | 18 + .../libsacloud/api/product_internet_gen.go | 239 ++++ .../sacloud/libsacloud/api/product_license.go | 18 + .../libsacloud/api/product_license_gen.go | 235 ++++ .../libsacloud/api/product_private_host.go | 19 + .../api/product_private_host_gen.go | 239 ++++ .../sacloud/libsacloud/api/product_server.go | 71 + .../libsacloud/api/product_server_gen.go | 239 ++++ .../sacloud/libsacloud/api/public_price.go | 18 + .../libsacloud/api/public_price_gen.go | 236 ++++ .../sacloud/libsacloud/api/region.go | 18 + .../sacloud/libsacloud/api/region_gen.go | 239 ++++ .../sacloud/libsacloud/api/server.go | 287 ++++ .../sacloud/libsacloud/api/server_gen.go | 243 ++++ .../github.com/sacloud/libsacloud/api/sim.go | 219 ++++ .../sacloud/libsacloud/api/sim_gen.go | 238 ++++ .../sacloud/libsacloud/api/simple_monitor.go | 133 ++ .../libsacloud/api/simple_monitor_gen.go | 238 ++++ .../sacloud/libsacloud/api/ssh_key.go | 64 + .../sacloud/libsacloud/api/ssh_key_gen.go | 243 ++++ .../sacloud/libsacloud/api/subnet.go | 18 + .../sacloud/libsacloud/api/subnet_gen.go | 229 ++++ .../sacloud/libsacloud/api/switch.go | 55 + .../sacloud/libsacloud/api/switch_gen.go | 243 ++++ .../sacloud/libsacloud/api/vpc_router.go | 437 +++++++ .../sacloud/libsacloud/api/vpc_router_gen.go | 238 ++++ .../sacloud/libsacloud/api/webaccel.go | 114 ++ .../sacloud/libsacloud/api/webaccel_search.go | 85 ++ .../github.com/sacloud/libsacloud/api/zone.go | 19 + .../sacloud/libsacloud/api/zone_gen.go | 239 ++++ .../sacloud/libsacloud/libsacloud.go | 5 + .../sacloud/libsacloud/sacloud/account.go | 9 + .../sacloud/libsacloud/sacloud/appliance.go | 57 + .../sacloud/libsacloud/sacloud/archive.go | 27 + .../sacloud/libsacloud/sacloud/auth_status.go | 79 ++ .../sacloud/libsacloud/sacloud/auto_backup.go | 88 ++ .../sacloud/libsacloud/sacloud/bill.go | 32 + .../sacloud/libsacloud/sacloud/bridge.go | 30 + .../sacloud/libsacloud/sacloud/cdrom.go | 17 + .../libsacloud/sacloud/common_types.go | 326 +++++ .../sacloud/libsacloud/sacloud/database.go | 435 +++++++ .../libsacloud/sacloud/database_status.go | 128 ++ .../libsacloud/sacloud/delete_cache_result.go | 8 + .../sacloud/libsacloud/sacloud/disk.go | 202 +++ .../sacloud/libsacloud/sacloud/dns.go | 189 +++ .../sacloud/libsacloud/sacloud/doc.go | 3 + .../sacloud/libsacloud/sacloud/feed.go | 36 + .../sacloud/libsacloud/sacloud/ftp_server.go | 15 + .../sacloud/libsacloud/sacloud/gslb.go | 216 +++ .../sacloud/libsacloud/sacloud/host.go | 6 + .../sacloud/libsacloud/sacloud/icon.go | 33 + .../sacloud/libsacloud/sacloud/instance.go | 35 + .../sacloud/libsacloud/sacloud/interface.go | 43 + .../sacloud/libsacloud/sacloud/internet.go | 50 + .../sacloud/libsacloud/sacloud/ipaddress.go | 10 + .../sacloud/libsacloud/sacloud/ipv6addr.go | 35 + .../sacloud/libsacloud/sacloud/ipv6net.go | 18 + .../sacloud/libsacloud/sacloud/license.go | 27 + .../libsacloud/sacloud/loadbalancer.go | 181 +++ .../sacloud/libsacloud/sacloud/member.go | 9 + .../libsacloud/sacloud/mobile_gateway.go | 209 +++ .../sacloud/libsacloud/sacloud/monitor.go | 305 +++++ .../sacloud/libsacloud/sacloud/nfs.go | 110 ++ .../sacloud/libsacloud/sacloud/note.go | 16 + .../sacloud/ostype/archive_ostype.go | 127 ++ .../sacloud/ostype/archiveostypes_string.go | 16 + .../libsacloud/sacloud/packet_filter.go | 248 ++++ .../libsacloud/sacloud/private_host.go | 18 + .../libsacloud/sacloud/product_disk.go | 20 + .../libsacloud/sacloud/product_internet.go | 12 + .../libsacloud/sacloud/product_license.go | 11 + .../libsacloud/sacloud/product_privatehost.go | 13 + .../libsacloud/sacloud/product_server.go | 12 + .../libsacloud/sacloud/prop_assigned_cpu.go | 11 + .../sacloud/prop_assigned_memory.go | 19 + .../libsacloud/sacloud/prop_availability.go | 26 + .../libsacloud/sacloud/prop_bundle_info.go | 27 + .../sacloud/libsacloud/sacloud/prop_class.go | 11 + .../sacloud/prop_connected_switches.go | 45 + .../libsacloud/sacloud/prop_copy_source.go | 56 + .../sacloud/libsacloud/sacloud/prop_cpu.go | 11 + .../libsacloud/sacloud/prop_description.go | 16 + .../sacloud/prop_disk_connection.go | 33 + .../libsacloud/sacloud/prop_disk_size.go | 65 + .../sacloud/libsacloud/sacloud/prop_disks.go | 22 + .../libsacloud/sacloud/prop_distant_from.go | 26 + .../sacloud/libsacloud/sacloud/prop_host.go | 19 + .../libsacloud/sacloud/prop_host_name.go | 11 + .../sacloud/libsacloud/sacloud/prop_icon.go | 47 + .../libsacloud/sacloud/prop_instance.go | 59 + .../sacloud/prop_interface_driver.go | 26 + .../libsacloud/sacloud/prop_interfaces.go | 19 + .../libsacloud/sacloud/prop_job_status.go | 11 + .../sacloud/libsacloud/sacloud/prop_memory.go | 19 + .../sacloud/libsacloud/sacloud/prop_name.go | 16 + .../libsacloud/sacloud/prop_note_class.go | 39 + .../sacloud/prop_original_archive_id.go | 22 + .../libsacloud/sacloud/prop_plan_id.go | 22 + .../libsacloud/sacloud/prop_private_host.go | 21 + .../sacloud/prop_private_host_plan.go | 51 + .../sacloud/libsacloud/sacloud/prop_region.go | 44 + .../sacloud/libsacloud/sacloud/prop_scope.go | 42 + .../sacloud/libsacloud/sacloud/prop_server.go | 21 + .../libsacloud/sacloud/prop_server_plan.go | 51 + .../libsacloud/sacloud/prop_service_class.go | 11 + .../libsacloud/sacloud/prop_storage.go | 11 + .../libsacloud/sacloud/prop_storage_class.go | 11 + .../sacloud/libsacloud/sacloud/prop_switch.go | 21 + .../sacloud/libsacloud/sacloud/prop_tags.go | 57 + .../libsacloud/sacloud/prop_timestamp.go | 35 + .../sacloud/libsacloud/sacloud/prop_zone.go | 118 ++ .../libsacloud/sacloud/public_price.go | 18 + .../sacloud/libsacloud/sacloud/region.go | 18 + .../sacloud/libsacloud/sacloud/server.go | 145 +++ .../sacloud/libsacloud/sacloud/sim.go | 111 ++ .../libsacloud/sacloud/simple_monitor.go | 254 ++++ .../sacloud/sitetosite_connection_detail.go | 38 + .../sacloud/libsacloud/sacloud/ssh_key.go | 38 + .../sacloud/libsacloud/sacloud/storage.go | 18 + .../sacloud/libsacloud/sacloud/subnet.go | 18 + .../sacloud/libsacloud/sacloud/switch.go | 84 ++ .../sacloud/libsacloud/sacloud/vpc_router.go | 238 ++++ .../libsacloud/sacloud/vpc_router_setting.go | 1158 +++++++++++++++++ .../libsacloud/sacloud/vpc_router_status.go | 8 + .../sacloud/libsacloud/sacloud/webaccel.go | 201 +++ .../sacloud/libsacloud/sacloud/zone.go | 46 + .../github.com/xenolf/lego/acme/challenges.go | 2 + vendor/github.com/xenolf/lego/acme/client.go | 145 ++- vendor/github.com/xenolf/lego/acme/crypto.go | 11 +- .../xenolf/lego/acme/dns_challenge.go | 10 +- .../xenolf/lego/acme/dns_challenge_manual.go | 10 +- vendor/github.com/xenolf/lego/acme/http.go | 82 +- .../xenolf/lego/acme/http_challenge.go | 4 +- .../xenolf/lego/acme/http_challenge_server.go | 6 +- .../xenolf/lego/acme/tls_alpn_challenge.go | 104 ++ .../lego/acme/tls_alpn_challenge_server.go | 86 ++ vendor/github.com/xenolf/lego/log/logger.go | 32 +- .../xenolf/lego/platform/config/env/env.go | 27 + .../lego/providers/dns/auroradns/auroradns.go | 54 +- .../xenolf/lego/providers/dns/azure/azure.go | 65 +- .../lego/providers/dns/bluecat/bluecat.go | 70 +- .../providers/dns/cloudflare/cloudflare.go | 95 +- .../lego/providers/dns/cloudxns/cloudxns.go | 51 +- .../dns/digitalocean/digitalocean.go | 35 +- .../lego/providers/dns/dns_providers.go | 78 +- .../lego/providers/dns/dnsimple/dnsimple.go | 44 +- .../providers/dns/dnsmadeeasy/dnsmadeeasy.go | 41 +- .../lego/providers/dns/dnspod/dnspod.go | 46 +- .../lego/providers/dns/duckdns/duckdns.go | 15 +- .../xenolf/lego/providers/dns/dyn/dyn.go | 35 +- .../xenolf/lego/providers/dns/exec/exec.go | 8 +- .../lego/providers/dns/exoscale/exoscale.go | 35 +- .../lego/providers/dns/fastdns/fastdns.go | 45 +- .../xenolf/lego/providers/dns/gandi/gandi.go | 38 +- .../lego/providers/dns/gandiv5/gandiv5.go | 26 +- .../lego/providers/dns/gcloud/googlecloud.go | 47 +- .../lego/providers/dns/glesys/glesys.go | 38 +- .../lego/providers/dns/godaddy/godaddy.go | 60 +- .../lego/providers/dns/lightsail/lightsail.go | 34 +- .../lego/providers/dns/linode/linode.go | 23 +- .../lego/providers/dns/namecheap/namecheap.go | 35 +- .../providers/dns/namedotcom/namedotcom.go | 31 +- .../lego/providers/dns/nifcloud/client.go | 235 ++++ .../lego/providers/dns/nifcloud/nifcloud.go | 103 ++ .../xenolf/lego/providers/dns/ns1/ns1.go | 31 +- .../xenolf/lego/providers/dns/otc/mock.go | 10 +- .../xenolf/lego/providers/dns/otc/otc.go | 29 +- .../xenolf/lego/providers/dns/ovh/ovh.go | 27 +- .../xenolf/lego/providers/dns/pdns/pdns.go | 100 +- .../lego/providers/dns/rackspace/rackspace.go | 109 +- .../lego/providers/dns/rfc2136/rfc2136.go | 8 +- .../providers/dns/sakuracloud/sakuracloud.go | 144 ++ .../lego/providers/dns/vegadns/vegadns.go | 85 ++ .../xenolf/lego/providers/dns/vultr/vultr.go | 52 +- 258 files changed, 25528 insertions(+), 1516 deletions(-) create mode 100644 acme/challenge_tls_provider.go create mode 100644 integration/fake_dns_server.go create mode 100644 integration/fixtures/acme/ssl/pebble.minica.pem delete mode 100644 integration/resources/compose/boulder.yml create mode 100644 integration/resources/compose/peddle.yml rename provider/acme/{challenge.go => challenge_http.go} (51%) create mode 100644 provider/acme/challenge_tls.go create mode 100644 vendor/github.com/OpenDNS/vegadns2client/LICENSE create mode 100644 vendor/github.com/OpenDNS/vegadns2client/client.go create mode 100644 vendor/github.com/OpenDNS/vegadns2client/domains.go create mode 100644 vendor/github.com/OpenDNS/vegadns2client/main.go create mode 100644 vendor/github.com/OpenDNS/vegadns2client/records.go create mode 100644 vendor/github.com/OpenDNS/vegadns2client/tokens.go create mode 100644 vendor/github.com/sacloud/libsacloud/LICENSE.txt create mode 100644 vendor/github.com/sacloud/libsacloud/api/archive.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/archive_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/auth_status.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/auto_backup.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/auto_backup_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/base_api.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/bill.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/bridge.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/bridge_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/cdrom.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/cdrom_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/client.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/database.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/database_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/disk.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/disk_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/dns.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/dns_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/doc.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/error.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/gslb.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/gslb_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/icon.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/icon_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/interface.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/interface_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/internet.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/internet_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ipaddress.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ipaddress_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ipv6addr.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ipv6addr_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ipv6net.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ipv6net_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/license.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/license_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/load_balancer.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/load_balancer_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/mobile_gateway_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/newsfeed.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/nfs.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/nfs_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/note.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/note_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/packet_filter.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/packet_filter_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/polling.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/private_host.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/private_host_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_disk.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_disk_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_internet.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_internet_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_license.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_license_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_private_host.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_private_host_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_server.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/product_server_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/public_price.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/public_price_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/region.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/region_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/server.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/server_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/sim.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/sim_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/simple_monitor.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/simple_monitor_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ssh_key.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/ssh_key_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/subnet.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/subnet_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/switch.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/switch_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/vpc_router.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/vpc_router_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/webaccel.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/webaccel_search.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/zone.go create mode 100644 vendor/github.com/sacloud/libsacloud/api/zone_gen.go create mode 100644 vendor/github.com/sacloud/libsacloud/libsacloud.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/account.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/appliance.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/archive.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/auth_status.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/auto_backup.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/bill.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/bridge.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/cdrom.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/common_types.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/database.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/database_status.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/delete_cache_result.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/disk.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/dns.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/doc.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/feed.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ftp_server.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/gslb.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/host.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/icon.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/instance.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/interface.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/internet.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ipaddress.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ipv6addr.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ipv6net.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/license.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/member.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/monitor.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/nfs.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/note.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/packet_filter.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/private_host.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/product_disk.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/product_internet.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/product_license.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/product_privatehost.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/product_server.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_cpu.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_memory.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_availability.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_bundle_info.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_class.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_connected_switches.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_copy_source.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_cpu.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_description.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_connection.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_size.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_disks.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_distant_from.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_host.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_host_name.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_icon.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_instance.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_interface_driver.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_interfaces.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_job_status.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_memory.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_name.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_note_class.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_original_archive_id.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_plan_id.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host_plan.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_region.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_scope.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_server.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_server_plan.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_service_class.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_storage.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_storage_class.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_switch.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_tags.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_timestamp.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/prop_zone.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/public_price.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/region.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/server.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/sim.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/sitetosite_connection_detail.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/ssh_key.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/storage.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/subnet.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/switch.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_setting.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/webaccel.go create mode 100644 vendor/github.com/sacloud/libsacloud/sacloud/zone.go create mode 100644 vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go create mode 100644 vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go create mode 100644 vendor/github.com/xenolf/lego/platform/config/env/env.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go create mode 100644 vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go diff --git a/Gopkg.lock b/Gopkg.lock index 7644eb2d5..4bd182ff1 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -98,6 +98,12 @@ revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c" source = "github.com/ijc25/Gotty" +[[projects]] + branch = "master" + name = "github.com/OpenDNS/vegadns2client" + packages = ["."] + revision = "a3fa4a771d87bda2514a90a157e1fed1b6897d2e" + [[projects]] name = "github.com/PuerkitoBio/purell" packages = ["."] @@ -758,6 +764,7 @@ version = "v1.3.7" [[projects]] + branch = "master" name = "github.com/jjcollinge/servicefabric" packages = ["."] revision = "8eebe170fa1ba25d3dfb928b3f86a7313b13b9fe" @@ -1087,6 +1094,17 @@ packages = ["."] revision = "256dc444b735e061061cf46c809487313d5b0065" +[[projects]] + branch = "master" + name = "github.com/sacloud/libsacloud" + packages = [ + ".", + "api", + "sacloud", + "sacloud/ostype" + ] + revision = "306ea89b6ef19334614f7b0fc5aa19595022bb8c" + [[projects]] name = "github.com/samuel/go-zookeeper" packages = ["zk"] @@ -1257,6 +1275,7 @@ packages = [ "acme", "log", + "platform/config/env", "providers/dns", "providers/dns/auroradns", "providers/dns/azure", @@ -1281,6 +1300,7 @@ "providers/dns/linode", "providers/dns/namecheap", "providers/dns/namedotcom", + "providers/dns/nifcloud", "providers/dns/ns1", "providers/dns/otc", "providers/dns/ovh", @@ -1288,9 +1308,11 @@ "providers/dns/rackspace", "providers/dns/rfc2136", "providers/dns/route53", + "providers/dns/sakuracloud", + "providers/dns/vegadns", "providers/dns/vultr" ] - revision = "7fedfd1388f016c7ca7ed92a7f2024d06a7e20d8" + revision = "e0d512138c43e3f056a41cd7a5beff662ec130d3" [[projects]] branch = "master" @@ -1711,6 +1733,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "593c88b41d6384d68bd610a8c80c39017e77584f4e3454b2ca5c26ee904bf1da" + inputs-digest = "53d03fd81596d9560d751bc1102615990a91b0779cff079784a9a1c6641bd503" solver-name = "gps-cdcl" solver-version = 1 diff --git a/acme/account.go b/acme/account.go index 571caf9dd..86db58a1b 100644 --- a/acme/account.go +++ b/acme/account.go @@ -187,7 +187,7 @@ func (dc *DomainsCertificates) removeDuplicates() { } func (dc *DomainsCertificates) removeEmpty() { - certs := []*DomainsCertificate{} + var certs []*DomainsCertificate for _, cert := range dc.Certs { if cert.Certificate != nil && len(cert.Certificate.Certificate) > 0 && len(cert.Certificate.PrivateKey) > 0 { certs = append(certs, cert) diff --git a/acme/acme.go b/acme/acme.go index 201e68242..4c2361155 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -51,6 +51,7 @@ type ACME struct { KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'. Default to 'RSA4096'"` DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"` HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"` + TLSChallenge *acmeprovider.TLSChallenge `description:"Activate TLS-ALPN-01 Challenge"` DNSProvider string `description:"(Deprecated) Activate DNS-01 Challenge"` // Deprecated DelayDontCheckDNS flaeg.Duration `description:"(Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // Deprecated ACMELogging bool `description:"Enable debug logging of ACME actions."` @@ -59,6 +60,7 @@ type ACME struct { defaultCertificate *tls.Certificate store cluster.Store challengeHTTPProvider *challengeHTTPProvider + challengeTLSProvider *challengeTLSProvider checkOnDemandDomain func(domain string) bool jobs *channels.InfiniteChannel TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"` @@ -69,7 +71,7 @@ func (a *ACME) init() error { acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) if a.ACMELogging { - legolog.Logger = fmtlog.New(log.WriterLevel(logrus.DebugLevel), "legolog: ", 0) + legolog.Logger = fmtlog.New(log.WriterLevel(logrus.InfoLevel), "legolog: ", 0) } else { legolog.Logger = fmtlog.New(ioutil.Discard, "", 0) } @@ -122,11 +124,12 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl } if len(a.Storage) == 0 { - return errors.New("Empty Store, please provide a key for certs storage") + return errors.New("empty Store, please provide a key for certs storage") } a.checkOnDemandDomain = checkOnDemandDomain a.dynamicCerts = certs + a.challengeTLSProvider = &challengeTLSProvider{store: a.store} tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate) tlsConfig.GetCertificate = a.getCertificate @@ -246,16 +249,23 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat return providedCertificate, nil } + if challengeCert, ok := a.challengeTLSProvider.getCertificate(domain); ok { + log.Debugf("ACME got challenge %s", domain) + return challengeCert, nil + } + if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok { log.Debugf("ACME got domain cert %s", domain) return domainCert.tlsCert, nil } + if a.OnDemand { if a.checkOnDemandDomain != nil && !a.checkOnDemandDomain(domain) { return nil, nil } return a.loadCertificateOnDemand(clientHello) } + log.Debugf("No certificate found or generated for %s", domain) return nil, nil } @@ -417,6 +427,7 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { return nil, err } + // DNS challenge if a.DNSChallenge != nil && len(a.DNSChallenge.Provider) > 0 { log.Debugf("Using DNS Challenge provider: %s", a.DNSChallenge.Provider) @@ -431,21 +442,30 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { return nil, err } - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01}) + client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSALPN01}) err = client.SetChallengeProvider(acme.DNS01, provider) return client, err } + // HTTP challenge if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 { log.Debug("Using HTTP Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.DNS01}) + client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSALPN01}) a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store} err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider) return client, err } - return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge") + // TLS Challenge + if a.TLSChallenge != nil { + log.Debug("Using TLS Challenge provider.") + client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) + err = client.SetChallengeProvider(acme.TLSALPN01, a.challengeTLSProvider) + return client, err + } + + return nil, errors.New("ACME challenge not specified, please select TLS or HTTP or DNS Challenge") } func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -631,7 +651,6 @@ func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) { certificate, err := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple) if err != nil { - log.Error(err) return nil, fmt.Errorf("cannot obtain certificates: %+v", err) } diff --git a/acme/challenge_tls_provider.go b/acme/challenge_tls_provider.go new file mode 100644 index 000000000..a519bc8c6 --- /dev/null +++ b/acme/challenge_tls_provider.go @@ -0,0 +1,127 @@ +package acme + +import ( + "crypto/tls" + "fmt" + "strings" + "sync" + "time" + + "github.com/cenk/backoff" + "github.com/containous/traefik/cluster" + "github.com/containous/traefik/log" + "github.com/containous/traefik/safe" + "github.com/xenolf/lego/acme" +) + +var _ acme.ChallengeProviderTimeout = (*challengeTLSProvider)(nil) + +type challengeTLSProvider struct { + store cluster.Store + lock sync.RWMutex +} + +func (c *challengeTLSProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) { + log.Debugf("Looking for an existing ACME challenge for %s...", domain) + + if !strings.HasSuffix(domain, ".acme.invalid") { + return nil, false + } + + c.lock.RLock() + defer c.lock.RUnlock() + + account := c.store.Get().(*Account) + if account.ChallengeCerts == nil { + return nil, false + } + + account.Init() + + var result *tls.Certificate + operation := func() error { + for _, cert := range account.ChallengeCerts { + for _, dns := range cert.certificate.Leaf.DNSNames { + if domain == dns { + result = cert.certificate + return nil + } + } + } + return fmt.Errorf("cannot find challenge cert for domain %s", domain) + } + + notify := func(err error, time time.Duration) { + log.Errorf("Error getting cert: %v, retrying in %s", err, time) + } + ebo := backoff.NewExponentialBackOff() + ebo.MaxElapsedTime = 60 * time.Second + + err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify) + if err != nil { + log.Errorf("Error getting cert: %v", err) + return nil, false + + } + return result, true +} + +func (c *challengeTLSProvider) Present(domain, token, keyAuth string) error { + log.Debugf("Challenge Present %s", domain) + + cert, err := tlsALPN01ChallengeCert(domain, keyAuth) + if err != nil { + return err + } + + c.lock.Lock() + defer c.lock.Unlock() + + transaction, object, err := c.store.Begin() + if err != nil { + return err + } + + account := object.(*Account) + if account.ChallengeCerts == nil { + account.ChallengeCerts = map[string]*ChallengeCert{} + } + account.ChallengeCerts[domain] = cert + + return transaction.Commit(account) +} + +func (c *challengeTLSProvider) CleanUp(domain, token, keyAuth string) error { + log.Debugf("Challenge CleanUp %s", domain) + + c.lock.Lock() + defer c.lock.Unlock() + + transaction, object, err := c.store.Begin() + if err != nil { + return err + } + + account := object.(*Account) + delete(account.ChallengeCerts, domain) + + return transaction.Commit(account) +} + +func (c *challengeTLSProvider) Timeout() (timeout, interval time.Duration) { + return 60 * time.Second, 5 * time.Second +} + +func tlsALPN01ChallengeCert(domain, keyAuth string) (*ChallengeCert, error) { + tempCertPEM, rsaPrivPEM, err := acme.TLSALPNChallengeBlocks(domain, keyAuth) + if err != nil { + return nil, err + } + + certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM) + if err != nil { + return nil, err + } + + return &ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, nil +} diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index f09402748..8e0dad24b 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -200,6 +200,11 @@ func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile s internalRouter.AddRouter(acmeprovider) } + // TLS ALPN 01 + if acmeprovider.HTTPChallenge == nil && acmeprovider.DNSChallenge == nil && acmeprovider.TLSChallenge != nil { + entryPoint.TLSALPNGetter = acmeprovider.GetTLSALPNCertificate + } + if acmeprovider.EntryPoint == entryPointName && acmeprovider.OnDemand { entryPoint.OnDemandListener = acmeprovider.ListenRequest } diff --git a/configuration/configuration.go b/configuration/configuration.go index ec9452978..3573a32ad 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -360,6 +360,16 @@ func (gc *GlobalConfiguration) initACMEProvider() { gc.ACME.HTTPChallenge = nil } + if gc.ACME.DNSChallenge != nil && gc.ACME.TLSChallenge != nil { + log.Warn("Unable to use DNS challenge and TLS challenge at the same time. Fallback to DNS challenge.") + gc.ACME.TLSChallenge = nil + } + + if gc.ACME.HTTPChallenge != nil && gc.ACME.TLSChallenge != nil { + log.Warn("Unable to use HTTP challenge and TLS challenge at the same time. Fallback to TLS challenge.") + gc.ACME.HTTPChallenge = nil + } + // TODO: to remove in the future if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 { log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead") @@ -392,6 +402,7 @@ func (gc *GlobalConfiguration) InitACMEProvider() *acmeprovider.Provider { Storage: gc.ACME.Storage, HTTPChallenge: gc.ACME.HTTPChallenge, DNSChallenge: gc.ACME.DNSChallenge, + TLSChallenge: gc.ACME.TLSChallenge, Domains: gc.ACME.Domains, ACMELogging: gc.ACME.ACMELogging, CAServer: gc.ACME.CAServer, @@ -399,7 +410,7 @@ func (gc *GlobalConfiguration) InitACMEProvider() *acmeprovider.Provider { } store := acmeprovider.NewLocalStore(provider.Storage) - provider.Store = &store + provider.Store = store acme.ConvertToNewFormat(provider.Storage) gc.ACME = nil return provider diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md index 44e7cf465..fd4501bae 100644 --- a/docs/configuration/acme.md +++ b/docs/configuration/acme.md @@ -162,7 +162,63 @@ caServer = "https://acme-staging-v02.api.letsencrypt.org/directory" # ... ``` -### `dnsChallenge` +### ACME Challenge + +#### TLS Challenge + +Use the `TLS-ALPN-01` challenge to generate and renew ACME certificates by provisioning a TLS certificate. + +```toml +[acme] +# ... +entryPoint = "https" +[acme.tlsChallenge] +``` + +#### `httpChallenge` + +Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning a HTTP resource under a well-known URI. + +Redirection is fully compatible with the `HTTP-01` challenge. + +```toml +[acme] +# ... +entryPoint = "https" +[acme.httpChallenge] + entryPoint = "http" +``` + +!!! note + If the `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through port 80. + This is a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72). + +##### `entryPoint` + +Specify the entryPoint to use during the challenges. + +```toml +defaultEntryPoints = ["http", "https"] + +[entryPoints] + [entryPoints.http] + address = ":80" + [entryPoints.https] + address = ":443" + [entryPoints.https.tls] +# ... + +[acme] + # ... + entryPoint = "https" + [acme.httpChallenge] + entryPoint = "http" +``` + +!!! note + `acme.httpChallenge.entryPoint` has to be reachable through port 80. It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72). + +#### `dnsChallenge` Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record. @@ -175,7 +231,7 @@ Use the `DNS-01` challenge to generate and renew ACME certificates by provisioni # ... ``` -#### `delayBeforeCheck` +##### `delayBeforeCheck` By default, the `provider` will verify the TXT DNS challenge record before letting ACME verify. If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds. @@ -185,7 +241,7 @@ Useful if internal networks block external DNS queries. !!! note A `provider` is mandatory. -#### `provider` +##### `provider` Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each. Do not hesitate to complete it. @@ -203,7 +259,7 @@ Here is a list of supported `provider`s, that can automate the DNS verification, | [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | Not tested yet | | [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet | | External Program | `exec` | `EXEC_PATH` | Not tested yet | -| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES | +| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES | | [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet | | [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet | | [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES | @@ -215,6 +271,7 @@ Here is a list of supported `provider`s, that can automate the DNS verification, | 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` | Not tested yet | | [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | 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/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet | | [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES | @@ -222,8 +279,11 @@ Here is a list of supported `provider`s, that can automate the DNS verification, | [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet | | [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet | | [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_HOSTED_ZONE_ID` or a configured user/instance IAM profile. | YES | +| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet | +| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet | | [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet | + ### `domains` You can provide SANs (alternative domains) to each main domain. @@ -272,49 +332,6 @@ Eventhough this behaviour is [DNS RFC](https://community.letsencrypt.org/t/wildc The Træfik ACME client library [LEGO](https://github.com/xenolf/lego) supports some but not all DNS providers to work around this issue. The [`provider` table](/configuration/acme/#provider) indicates if they allow generating certificates for a wildcard domain and its root domain. -### `httpChallenge` - -Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning a HTTP resource under a well-known URI. - -Redirection is fully compatible with the `HTTP-01` challenge. - -```toml -[acme] -# ... -entryPoint = "https" -[acme.httpChallenge] - entryPoint = "http" -``` - -!!! note - If the `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through port 80. - This is a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72). - -#### `entryPoint` - -Specify the entryPoint to use during the challenges. - -```toml -defaultEntryPoints = ["http", "https"] - -[entryPoints] - [entryPoints.http] - address = ":80" - [entryPoints.https] - address = ":443" - [entryPoints.https.tls] -# ... - -[acme] - # ... - entryPoint = "https" - [acme.httpChallenge] - entryPoint = "http" -``` - -!!! note - `acme.httpChallenge.entryPoint` has to be reachable through port 80. It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72). - ### `onDemand` (Deprecated) !!! danger "DEPRECATED" diff --git a/integration/acme_test.go b/integration/acme_test.go index ba3948d20..913ee0f5c 100644 --- a/integration/acme_test.go +++ b/integration/acme_test.go @@ -4,8 +4,10 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "io/ioutil" "net/http" "os" + "path/filepath" "time" "github.com/containous/traefik/integration/try" @@ -13,268 +15,360 @@ import ( "github.com/containous/traefik/testhelpers" "github.com/containous/traefik/types" "github.com/go-check/check" + "github.com/miekg/dns" checker "github.com/vdemeester/shakers" ) // ACME test suites (using libcompose) type AcmeSuite struct { BaseSuite - boulderIP string + pebbleIP string + fakeDNSServer *dns.Server } -// Acme tests configuration -type AcmeTestCase struct { - configuration acme.Configuration +type acmeTestCase struct { + template templateModel traefikConfFilePath string - expectedDomain string + expectedCommonName string expectedAlgorithm x509.PublicKeyAlgorithm } +type templateModel struct { + PortHTTP string + PortHTTPS string + Acme acme.Configuration +} + const ( // Domain to check acmeDomain = "traefik.acme.wtf" // Wildcard domain to check wildcardDomain = "*.acme.wtf" - - // Traefik default certificate - traefikDefaultDomain = "TRAEFIK DEFAULT CERT" ) func (s *AcmeSuite) getAcmeURL() string { - return fmt.Sprintf("http://%s:4001/directory", s.boulderIP) + return fmt.Sprintf("https://%s:14000/dir", s.pebbleIP) +} + +func setupPebbleRootCA() (*http.Transport, error) { + path, err := filepath.Abs("fixtures/acme/ssl/pebble.minica.pem") + if err != nil { + return nil, err + } + + os.Setenv("LEGO_CA_CERTIFICATES", path) + os.Setenv("LEGO_CA_SERVER_NAME", "pebble") + + customCAs, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(customCAs); !ok { + return nil, fmt.Errorf("error creating x509 cert pool from %q: %v", path, err) + } + + return &http.Transport{ + TLSClientConfig: &tls.Config{ + ServerName: "pebble", + RootCAs: certPool, + }, + }, nil } func (s *AcmeSuite) SetUpSuite(c *check.C) { - s.createComposeProject(c, "boulder") + s.createComposeProject(c, "peddle") s.composeProject.Start(c) - s.boulderIP = s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress + s.fakeDNSServer = startFakeDNSServer() - // wait for boulder - err := try.GetRequest(s.getAcmeURL(), 120*time.Second, try.StatusCodeIs(http.StatusOK)) + s.pebbleIP = s.composeProject.Container(c, "pebble").NetworkSettings.IPAddress + + pebbleTransport, err := setupPebbleRootCA() + if err != nil { + c.Fatal(err) + } + + // wait for peddle + req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil) + + client := &http.Client{ + Transport: pebbleTransport, + } + + err = try.Do(5*time.Second, func() error { + resp, errGet := client.Do(req) + if errGet != nil { + return errGet + } + return try.StatusCodeIs(http.StatusOK)(resp) + }) c.Assert(err, checker.IsNil) } func (s *AcmeSuite) TearDownSuite(c *check.C) { + err := s.fakeDNSServer.Shutdown() + if err != nil { + c.Log(err) + } + // shutdown and delete compose project if s.composeProject != nil { s.composeProject.Stop(c) } } -// Test ACME provider with certificate at start -func (s *AcmeSuite) TestACMEProviderAtStart(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01DomainsAtStart(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, - Domains: types.Domains{types.Domain{ - Main: "traefik.acme.wtf", - }}, + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + Domains: types.Domains{types.Domain{ + Main: "traefik.acme.wtf", + }}, + }, }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test ACME provider with certificate at start -func (s *AcmeSuite) TestACMEProviderAtStartInSAN(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01DomainsInSANAtStart(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - Domains: types.Domains{types.Domain{ - Main: "acme.wtf", - SANs: []string{"traefik.acme.wtf"}, - }}, + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + Domains: types.Domains{types.Domain{ + Main: "acme.wtf", + SANs: []string{"traefik.acme.wtf"}, + }}, + }, }, - expectedDomain: "acme.wtf", - expectedAlgorithm: x509.RSA, + expectedCommonName: "acme.wtf", + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test ACME provider with certificate at start -func (s *AcmeSuite) TestACMEProviderOnHost(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - CAServer: s.getAcmeURL(), - OnHostRule: true, + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + }, }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test ACME provider with certificate at start ECDSA algo -func (s *AcmeSuite) TestACMEProviderOnHostECDSA(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, - KeyType: "EC384", + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + KeyType: "EC384", + }, }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.ECDSA, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.ECDSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test ACME provider with certificate at start invalid algo default RSA -func (s *AcmeSuite) TestACMEProviderOnHostInvalidAlgo(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, - KeyType: "INVALID", + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + KeyType: "INVALID", + }, }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test ACME provider with certificate at start and no ACME challenge -func (s *AcmeSuite) TestACMEProviderOnHostWithNoACMEChallenge(c *check.C) { - testCase := AcmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - OnHostRule: true, - }, - expectedDomain: traefikDefaultDomain, - expectedAlgorithm: x509.RSA, - } - - s.retrieveAcmeCertificate(c, testCase) -} - -// Test OnDemand option with none provided certificate and challenge HTTP-01 -func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateHTTP01(c *check.C) { - testCase := AcmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnDemand: true, - }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.RSA, - } - - s.retrieveAcmeCertificate(c, testCase) -} - -// Test OnHostRule option with none provided certificate and challenge HTTP-01 -func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01(c *check.C) { - testCase := AcmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, - }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.RSA, - } - - s.retrieveAcmeCertificate(c, testCase) -} - -// Test OnHostRule option with none provided certificate and challenge HTTP-01 and web path -func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateHTTP01WithPath(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01OnHostRuleWithPath(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_http01_web_path.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + }, }, - expectedDomain: acmeDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test OnDemand option with a wildcard provided certificate -func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithWildcard(c *check.C) { +func (s *AcmeSuite) TestHTTP01OnHostRuleStaticCertificatesWithWildcard(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme_tls.toml", + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + }, + }, + expectedCommonName: wildcardDomain, + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + +func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml", + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + }, + }, + expectedCommonName: wildcardDomain, + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + +func (s *AcmeSuite) TestHTTP01OnDemand(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme-base.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) TestHTTP01OnDemandStaticCertificatesWithWildcard(c *check.C) { // FIXME flaky c.Skip("Flaky behavior will be fixed in the next PR") - testCase := AcmeTestCase{ + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_tls.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnDemand: true, + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnDemand: true, + }, }, - expectedDomain: wildcardDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: wildcardDomain, + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test onHostRule option with a wildcard provided certificate -func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithWildcard(c *check.C) { - testCase := AcmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme_tls.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, - }, - expectedDomain: wildcardDomain, - expectedAlgorithm: x509.RSA, - } - - s.retrieveAcmeCertificate(c, testCase) -} - -// Test OnDemand option with a wildcard provided certificate -func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateWithDynamicWildcard(c *check.C) { - testCase := AcmeTestCase{ +func (s *AcmeSuite) TestHTTP01OnDemandDynamicCertificatesWithWildcard(c *check.C) { + testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnDemand: true, + template: templateModel{ + Acme: acme.Configuration{ + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnDemand: true, + }, }, - expectedDomain: wildcardDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: wildcardDomain, + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) } -// Test onHostRule option with a wildcard provided certificate -func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *check.C) { - testCase := AcmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml", - configuration: acme.Configuration{ - CAServer: s.getAcmeURL(), - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, +func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme-base.toml", + template: templateModel{ + Acme: acme.Configuration{ + TLSChallenge: &acme.TLSChallenge{}, + OnHostRule: true, + }, }, - expectedDomain: wildcardDomain, - expectedAlgorithm: x509.RSA, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + +func (s *AcmeSuite) TestTLSALPN01OnDemand(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme-base.toml", + template: templateModel{ + Acme: acme.Configuration{ + TLSChallenge: &acme.TLSChallenge{}, + OnDemand: true, + }, + }, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + +func (s *AcmeSuite) TestTLSALPN01DomainsAtStart(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme-base.toml", + template: templateModel{ + Acme: acme.Configuration{ + TLSChallenge: &acme.TLSChallenge{}, + Domains: types.Domains{types.Domain{ + Main: "traefik.acme.wtf", + }}, + }, + }, + expectedCommonName: acmeDomain, + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + +func (s *AcmeSuite) TestTLSALPN01DomainsInSANAtStart(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme-base.toml", + template: templateModel{ + Acme: acme.Configuration{ + TLSChallenge: &acme.TLSChallenge{}, + Domains: types.Domains{types.Domain{ + Main: "acme.wtf", + SANs: []string{"traefik.acme.wtf"}, + }}, + }, + }, + expectedCommonName: "acme.wtf", + expectedAlgorithm: x509.RSA, } s.retrieveAcmeCertificate(c, testCase) @@ -282,10 +376,12 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c * // Test Let's encrypt down func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) { - file := s.adaptFile(c, "fixtures/acme/acme-base.toml", acme.Configuration{ - CAServer: "http://wrongurl:4001/directory", - HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, - OnHostRule: true, + file := s.adaptFile(c, "fixtures/acme/acme-base.toml", templateModel{ + Acme: acme.Configuration{ + CAServer: "http://wrongurl:4001/directory", + HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, + OnHostRule: true, + }, }) defer os.Remove(file) @@ -301,8 +397,20 @@ func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) { } // Doing an HTTPS request and test the response certificate -func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) { - file := s.adaptFile(c, testCase.traefikConfFilePath, testCase.configuration) +func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase acmeTestCase) { + if len(testCase.template.PortHTTP) == 0 { + testCase.template.PortHTTP = ":5002" + } + + if len(testCase.template.PortHTTPS) == 0 { + testCase.template.PortHTTPS = ":5001" + } + + if len(testCase.template.Acme.CAServer) == 0 { + testCase.template.Acme.CAServer = s.getAcmeURL() + } + + file := s.adaptFile(c, testCase.traefikConfFilePath, testCase.template) defer os.Remove(file) cmd, display := s.traefikCmd(withConfigFile(file)) @@ -357,8 +465,8 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) { } cn := resp.TLS.PeerCertificates[0].Subject.CommonName - if cn != testCase.expectedDomain { - return fmt.Errorf("domain %s found instead of %s", cn, testCase.expectedDomain) + if cn != testCase.expectedCommonName { + return fmt.Errorf("domain %s found instead of %s", cn, testCase.expectedCommonName) } return nil @@ -367,6 +475,6 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) { c.Assert(err, checker.IsNil) c.Assert(resp.StatusCode, checker.Equals, http.StatusOK) // Check Domain into response certificate - c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, testCase.expectedDomain) + c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Equals, testCase.expectedCommonName) c.Assert(resp.TLS.PeerCertificates[0].PublicKeyAlgorithm, checker.Equals, testCase.expectedAlgorithm) } diff --git a/integration/fake_dns_server.go b/integration/fake_dns_server.go new file mode 100644 index 000000000..3319b703b --- /dev/null +++ b/integration/fake_dns_server.go @@ -0,0 +1,114 @@ +package integration + +import ( + "fmt" + "net" + "os" + + "github.com/containous/traefik/log" + "github.com/miekg/dns" +) + +type handler struct{} + +// ServeDNS a fake DNS server +// Simplified version of the Challenge Test Server from Boulder +// https://github.com/letsencrypt/boulder/blob/a6597b9f120207eff192c3e4107a7e49972a0250/test/challtestsrv/dnsone.go#L40 +func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { + m := new(dns.Msg) + m.SetReply(r) + m.Compress = false + + fakeDNS := os.Getenv("DOCKER_HOST_IP") + if fakeDNS == "" { + fakeDNS = "127.0.0.1" + } + for _, q := range r.Question { + log.Printf("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype]) + + switch q.Qtype { + case dns.TypeA: + record := new(dns.A) + record.Hdr = dns.RR_Header{ + Name: q.Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 0, + } + record.A = net.ParseIP(fakeDNS) + + m.Answer = append(m.Answer, record) + case dns.TypeCAA: + addCAARecord := true + + var value string + switch q.Name { + case "bad-caa-reserved.com.": + value = "sad-hacker-ca.invalid" + case "good-caa-reserved.com.": + value = "happy-hacker-ca.invalid" + case "accounturi.good-caa-reserved.com.": + uri := os.Getenv("ACCOUNT_URI") + value = fmt.Sprintf("happy-hacker-ca.invalid; accounturi=%s", uri) + case "recheck.good-caa-reserved.com.": + // Allow issuance when we're running in the past + // (under FAKECLOCK), otherwise deny issuance. + if os.Getenv("FAKECLOCK") != "" { + value = "happy-hacker-ca.invalid" + } else { + value = "sad-hacker-ca.invalid" + } + case "dns-01-only.good-caa-reserved.com.": + value = "happy-hacker-ca.invalid; validationmethods=dns-01" + case "http-01-only.good-caa-reserved.com.": + value = "happy-hacker-ca.invalid; validationmethods=http-01" + case "dns-01-or-http-01.good-caa-reserved.com.": + value = "happy-hacker-ca.invalid; validationmethods=dns-01,http-01" + default: + addCAARecord = false + } + if addCAARecord { + record := new(dns.CAA) + record.Hdr = dns.RR_Header{ + Name: q.Name, + Rrtype: dns.TypeCAA, + Class: dns.ClassINET, + Ttl: 0, + } + record.Tag = "issue" + record.Value = value + m.Answer = append(m.Answer, record) + } + } + } + + auth := new(dns.SOA) + auth.Hdr = dns.RR_Header{Name: "boulder.invalid.", Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0} + auth.Ns = "ns.boulder.invalid." + auth.Mbox = "master.boulder.invalid." + auth.Serial = 1 + auth.Refresh = 1 + auth.Retry = 1 + auth.Expire = 1 + auth.Minttl = 1 + m.Ns = append(m.Ns, auth) + + w.WriteMsg(m) +} + +func startFakeDNSServer() *dns.Server { + srv := &dns.Server{ + Addr: ":5053", + Net: "udp", + Handler: &handler{}, + } + + go func() { + log.Infof("Start a fake DNS server.") + if err := srv.ListenAndServe(); err != nil { + log.Fatalf("Failed to set udp listener %v", err) + } + }() + + return srv +} diff --git a/integration/fixtures/acme/acme-base.toml b/integration/fixtures/acme/acme-base.toml index efa36cd44..ec07ddf91 100644 --- a/integration/fixtures/acme/acme-base.toml +++ b/integration/fixtures/acme/acme-base.toml @@ -4,9 +4,9 @@ defaultEntryPoints = ["http", "https"] [entryPoints] [entryPoints.http] - address = ":5002" + address = "{{ .PortHTTP }}" [entryPoints.https] - address = ":5001" + address = "{{ .PortHTTPS }}" [entryPoints.https.tls] [acme] @@ -14,17 +14,21 @@ defaultEntryPoints = ["http", "https"] storage = "/tmp/acme.json" entryPoint = "https" acmeLogging = true - onDemand = {{ .OnDemand }} - onHostRule = {{ .OnHostRule }} - keyType = "{{ .KeyType }}" - caServer = "{{ .CAServer }}" + onDemand = {{ .Acme.OnDemand }} + onHostRule = {{ .Acme.OnHostRule }} + keyType = "{{ .Acme.KeyType }}" + caServer = "{{ .Acme.CAServer }}" - {{if .HTTPChallenge }} + {{if .Acme.HTTPChallenge }} [acme.httpChallenge] - entryPoint = "{{ .HTTPChallenge.EntryPoint }}" + entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}" {{end}} - {{range .Domains}} + {{if .Acme.TLSChallenge }} + [acme.tlsChallenge] + {{end}} + + {{range .Acme.Domains}} [[acme.domains]] main = "{{ .Main }}" sans = [{{range .SANs }} diff --git a/integration/fixtures/acme/acme_http01_web_path.toml b/integration/fixtures/acme/acme_http01_web_path.toml index af6fb05a5..4992ae7fc 100644 --- a/integration/fixtures/acme/acme_http01_web_path.toml +++ b/integration/fixtures/acme/acme_http01_web_path.toml @@ -4,9 +4,9 @@ defaultEntryPoints = ["http", "https"] [entryPoints] [entryPoints.http] - address = ":5002" + address = "{{ .PortHTTP }}" [entryPoints.https] - address = ":5001" + address = "{{ .PortHTTPS }}" [entryPoints.https.tls] [acme] @@ -14,17 +14,17 @@ defaultEntryPoints = ["http", "https"] storage = "/tmp/acme.json" entryPoint = "https" acmeLogging = true - onDemand = {{ .OnDemand }} - onHostRule = {{ .OnHostRule }} - keyType = "{{ .KeyType }}" - caServer = "{{ .CAServer }}" + onDemand = {{ .Acme.OnDemand }} + onHostRule = {{ .Acme.OnHostRule }} + keyType = "{{ .Acme.KeyType }}" + caServer = "{{ .Acme.CAServer }}" - {{if .HTTPChallenge }} + {{if .Acme.HTTPChallenge }} [acme.httpChallenge] - entryPoint = "{{ .HTTPChallenge.EntryPoint }}" + entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}" {{end}} - {{range .Domains}} + {{range .Acme.Domains}} [[acme.domains]] main = "{{ .Main }}" sans = [{{range .SANs }} diff --git a/integration/fixtures/acme/acme_tls.toml b/integration/fixtures/acme/acme_tls.toml index feec57eb6..29532fe94 100644 --- a/integration/fixtures/acme/acme_tls.toml +++ b/integration/fixtures/acme/acme_tls.toml @@ -4,9 +4,9 @@ defaultEntryPoints = ["http", "https"] [entryPoints] [entryPoints.http] - address = ":5002" + address = "{{ .PortHTTP }}" [entryPoints.https] - address = ":5001" + address = "{{ .PortHTTPS }}" [entryPoints.https.tls] [[entryPoints.https.tls.certificates]] certFile = "fixtures/acme/ssl/wildcard.crt" @@ -17,17 +17,17 @@ defaultEntryPoints = ["http", "https"] storage = "/tmp/acme.json" entryPoint = "https" acmeLogging = true - onDemand = {{ .OnDemand }} - onHostRule = {{ .OnHostRule }} - keyType = "{{ .KeyType }}" - caServer = "{{ .CAServer }}" + onDemand = {{ .Acme.OnDemand }} + onHostRule = {{ .Acme.OnHostRule }} + keyType = "{{ .Acme.KeyType }}" + caServer = "{{ .Acme.CAServer }}" - {{if .HTTPChallenge }} + {{if .Acme.HTTPChallenge }} [acme.httpChallenge] - entryPoint = "{{ .HTTPChallenge.EntryPoint }}" + entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}" {{end}} - {{range .Domains}} + {{range .Acme.Domains}} [[acme.domains]] main = "{{ .Main }}" sans = [{{range .SANs }} diff --git a/integration/fixtures/acme/acme_tls_dynamic.toml b/integration/fixtures/acme/acme_tls_dynamic.toml index ab5eb4e47..bc2ed9477 100644 --- a/integration/fixtures/acme/acme_tls_dynamic.toml +++ b/integration/fixtures/acme/acme_tls_dynamic.toml @@ -4,28 +4,27 @@ defaultEntryPoints = ["http", "https"] [entryPoints] [entryPoints.http] - address = ":5002" + address = "{{ .PortHTTP }}" [entryPoints.https] - address = ":5001" + address = "{{ .PortHTTPS }}" [entryPoints.https.tls] - [acme] email = "test@traefik.io" storage = "/tmp/acme.json" entryPoint = "https" acmeLogging = true - onDemand = {{ .OnDemand }} - onHostRule = {{ .OnHostRule }} - keyType = "{{ .KeyType }}" - caServer = "{{ .CAServer }}" + onDemand = {{ .Acme.OnDemand }} + onHostRule = {{ .Acme.OnHostRule }} + keyType = "{{ .Acme.KeyType }}" + caServer = "{{ .Acme.CAServer }}" - {{if .HTTPChallenge }} + {{if .Acme.HTTPChallenge }} [acme.httpChallenge] - entryPoint = "{{ .HTTPChallenge.EntryPoint }}" + entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}" {{end}} - {{range .Domains}} + {{range .Acme.Domains}} [[acme.domains]] main = "{{ .Main }}" sans = [{{range .SANs }} diff --git a/integration/fixtures/acme/ssl/pebble.minica.pem b/integration/fixtures/acme/ssl/pebble.minica.pem new file mode 100644 index 000000000..a69a4c419 --- /dev/null +++ b/integration/fixtures/acme/ssl/pebble.minica.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIIJOLbes8sTr4wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgMjRlMmRiMCAXDTE3MTIwNjE5NDIxMFoYDzIxMTcx +MjA2MTk0MjEwWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSAyNGUyZGIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5WgZNoVJandj43kkLyU50vzCZ +alozvdRo3OFiKoDtmqKPNWRNO2hC9AUNxTDJco51Yc42u/WV3fPbbhSznTiOOVtn +Ajm6iq4I5nZYltGGZetGDOQWr78y2gWY+SG078MuOO2hyDIiKtVc3xiXYA+8Hluu +9F8KbqSS1h55yxZ9b87eKR+B0zu2ahzBCIHKmKWgc6N13l7aDxxY3D6uq8gtJRU0 +toumyLbdzGcupVvjbjDP11nl07RESDWBLG1/g3ktJvqIa4BWgU2HMh4rND6y8OD3 +Hy3H8MY6CElL+MOCbFJjWqhtOxeFyZZV9q3kYnk9CAuQJKMEGuN4GU6tzhW1AgMB +AAGjRTBDMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB +BQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQsFAAOCAQEAF85v +d40HK1ouDAtWeO1PbnWfGEmC5Xa478s9ddOd9Clvp2McYzNlAFfM7kdcj6xeiNhF +WPIfaGAi/QdURSL/6C1KsVDqlFBlTs9zYfh2g0UXGvJtj1maeih7zxFLvet+fqll +xseM4P9EVJaQxwuK/F78YBt0tCNfivC6JNZMgxKF59h0FBpH70ytUSHXdz7FKwix +Mfn3qEb9BXSk0Q3prNV5sOV3vgjEtB4THfDxSz9z3+DepVnW3vbbqwEbkXdk3j82 +2muVldgOUgTwK8eT+XdofVdntzU/kzygSAtAQwLJfn51fS1GvEcYGBc1bDryIqmF +p9BI7gVKtWSZYegicA== +-----END CERTIFICATE----- diff --git a/integration/resources/compose/boulder.yml b/integration/resources/compose/boulder.yml deleted file mode 100644 index dba52860d..000000000 --- a/integration/resources/compose/boulder.yml +++ /dev/null @@ -1,49 +0,0 @@ -boulder: - image: containous/boulder:containous-acmev2 - environment: - FAKE_DNS: ${DOCKER_HOST_IP} - PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657 - extra_hosts: - - le.wtf:127.0.0.1 - - boulder:127.0.0.1 - ports: - - 4000:4000 # ACME - - 4001:4001 # ACMEv2 - - 4002:4002 # OCSP - - 4003:4003 # OCSP - - 4430:4430 # ACME via HTTPS - - 4431:4431 # ACMEv2 via HTTPS - - 4500:4500 # ct-test-srv - - 6000:6000 # gsb-test-srv - - 8000:8000 # debug ports - - 8001:8001 - - 8002:8002 - - 8003:8003 - - 8004:8004 - - 8005:8005 - - 8006:8006 - - 8008:8008 - - 8009:8009 - - 8010:8010 - - 8055:8055 # dns-test-srv updates - - 9380:9380 # mail-test-srv - - 9381:9381 # mail-test-srv - links: - - bhsm:boulder-hsm - - bmysql:boulder-mysql - -bhsm: - # To minimize the fetching of various layers this should match - # the FROM image and tag in boulder/Dockerfile - image: letsencrypt/boulder-tools:2018-03-07 - environment: - PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657 - command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so - expose: - - 5657 -bmysql: - image: mariadb:10.1 - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: "yes" - command: mysqld --bind-address=0.0.0.0 - log_driver: none diff --git a/integration/resources/compose/peddle.yml b/integration/resources/compose/peddle.yml new file mode 100644 index 000000000..f1b053f34 --- /dev/null +++ b/integration/resources/compose/peddle.yml @@ -0,0 +1,10 @@ +pebble: + image: ldez/pebble + command: --dnsserver ${DOCKER_HOST_IP}:5053 + ports: + - 14000:14000 + environment: + # https://github.com/letsencrypt/pebble#testing-at-full-speed + - PEBBLE_VA_NOSLEEP=1 + # https://github.com/letsencrypt/pebble#invalid-anti-replay-nonce-errors + - PEBBLE_WFE_NONCEREJECT=0 diff --git a/provider/acme/account.go b/provider/acme/account.go index 682a4ad8a..f63147e91 100644 --- a/provider/acme/account.go +++ b/provider/acme/account.go @@ -73,8 +73,11 @@ func GetKeyType(value string) acme.KeyType { return acme.RSA4096 case "RSA8192": return acme.RSA8192 + case "": + log.Infof("The key type is empty. Use default key type %v.", acme.RSA4096) + return acme.RSA4096 default: - log.Warnf("Unable to determine key type value %s. Use %s as default value", value, acme.RSA4096) + log.Infof("Unable to determine key type value %q. Use default key type %v.", value, acme.RSA4096) return acme.RSA4096 } } diff --git a/provider/acme/challenge.go b/provider/acme/challenge_http.go similarity index 51% rename from provider/acme/challenge.go rename to provider/acme/challenge_http.go index 0ed638131..e620dbc4e 100644 --- a/provider/acme/challenge.go +++ b/provider/acme/challenge_http.go @@ -2,43 +2,80 @@ package acme import ( "fmt" + "net" + "net/http" "time" "github.com/cenk/backoff" - "github.com/containous/flaeg" + "github.com/containous/mux" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" "github.com/xenolf/lego/acme" ) -func dnsOverrideDelay(delay flaeg.Duration) error { - if delay == 0 { - return nil +var _ acme.ChallengeProviderTimeout = (*challengeHTTP)(nil) + +type challengeHTTP struct { + Store Store +} + +// Present presents a challenge to obtain new ACME certificate +func (c *challengeHTTP) Present(domain, token, keyAuth string) error { + httpChallenges, err := c.Store.GetHTTPChallenges() + if err != nil { + return fmt.Errorf("unable to get HTTPChallenges : %s", err) } - if delay > 0 { - log.Debugf("Delaying %d rather than validating DNS propagation now.", delay) + if httpChallenges == nil { + httpChallenges = map[string]map[string][]byte{} + } - acme.PreCheckDNS = func(_, _ string) (bool, error) { - time.Sleep(time.Duration(delay)) - return true, nil + if _, ok := httpChallenges[token]; !ok { + httpChallenges[token] = map[string][]byte{} + } + + httpChallenges[token][domain] = []byte(keyAuth) + + return c.Store.SaveHTTPChallenges(httpChallenges) +} + +// CleanUp cleans the challenges when certificate is obtained +func (c *challengeHTTP) CleanUp(domain, token, keyAuth string) error { + httpChallenges, err := c.Store.GetHTTPChallenges() + if err != nil { + return fmt.Errorf("unable to get HTTPChallenges : %s", err) + } + + log.Debugf("Challenge CleanUp for domain %s", domain) + + if _, ok := httpChallenges[token]; ok { + if _, domainOk := httpChallenges[token][domain]; domainOk { + delete(httpChallenges[token], domain) } - } else { - return fmt.Errorf("delayBeforeCheck: %d cannot be less than 0", delay) + if len(httpChallenges[token]) == 0 { + delete(httpChallenges, token) + } + return c.Store.SaveHTTPChallenges(httpChallenges) } return nil } +// Timeout calculates the maximum of time allowed to resolved an ACME challenge +func (c *challengeHTTP) Timeout() (timeout, interval time.Duration) { + return 60 * time.Second, 5 * time.Second +} + func getTokenValue(token, domain string, store Store) []byte { log.Debugf("Looking for an existing ACME challenge for token %v...", token) var result []byte operation := func() error { - var ok bool httpChallenges, err := store.GetHTTPChallenges() if err != nil { return fmt.Errorf("HTTPChallenges not available : %s", err) } + + var ok bool if result, ok = httpChallenges[token][domain]; !ok { return fmt.Errorf("cannot find challenge for token %v", token) } @@ -56,44 +93,33 @@ func getTokenValue(token, domain string, store Store) []byte { log.Errorf("Error getting challenge for token: %v", err) return []byte{} } + return result } -func presentHTTPChallenge(domain, token, keyAuth string, store Store) error { - httpChallenges, err := store.GetHTTPChallenges() - if err != nil { - return fmt.Errorf("unable to get HTTPChallenges : %s", err) - } +// AddRoutes add routes on internal router +func (p *Provider) AddRoutes(router *mux.Router) { + router.Methods(http.MethodGet). + Path(acme.HTTP01ChallengePath("{token}")). + Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + if token, ok := vars["token"]; ok { + domain, _, err := net.SplitHostPort(req.Host) + if err != nil { + log.Debugf("Unable to split host and port: %v. Fallback to request host.", err) + domain = req.Host + } - if httpChallenges == nil { - httpChallenges = map[string]map[string][]byte{} - } - - if _, ok := httpChallenges[token]; !ok { - httpChallenges[token] = map[string][]byte{} - } - - httpChallenges[token][domain] = []byte(keyAuth) - - return store.SaveHTTPChallenges(httpChallenges) -} - -func cleanUpHTTPChallenge(domain, token string, store Store) error { - httpChallenges, err := store.GetHTTPChallenges() - if err != nil { - return fmt.Errorf("unable to get HTTPChallenges : %s", err) - } - - log.Debugf("Challenge CleanUp for domain %s", domain) - - if _, ok := httpChallenges[token]; ok { - if _, domainOk := httpChallenges[token][domain]; domainOk { - delete(httpChallenges[token], domain) - } - if len(httpChallenges[token]) == 0 { - delete(httpChallenges, token) - } - return store.SaveHTTPChallenges(httpChallenges) - } - return nil + tokenValue := getTokenValue(token, domain, p.Store) + if len(tokenValue) > 0 { + rw.WriteHeader(http.StatusOK) + _, err = rw.Write(tokenValue) + if err != nil { + log.Errorf("Unable to write token : %v", err) + } + return + } + } + rw.WriteHeader(http.StatusNotFound) + })) } diff --git a/provider/acme/challenge_tls.go b/provider/acme/challenge_tls.go new file mode 100644 index 000000000..39498cdb5 --- /dev/null +++ b/provider/acme/challenge_tls.go @@ -0,0 +1,52 @@ +package acme + +import ( + "crypto/tls" + + "github.com/containous/traefik/log" + "github.com/containous/traefik/types" + "github.com/xenolf/lego/acme" +) + +var _ acme.ChallengeProvider = (*challengeTLSALPN)(nil) + +type challengeTLSALPN struct { + Store Store +} + +func (c *challengeTLSALPN) Present(domain, token, keyAuth string) error { + log.Debugf("TLS Challenge Present temp certificate for %s", domain) + + certPEMBlock, keyPEMBlock, err := acme.TLSALPNChallengeBlocks(domain, keyAuth) + if err != nil { + return err + } + + cert := &Certificate{Certificate: certPEMBlock, Key: keyPEMBlock, Domain: types.Domain{Main: "TEMP-" + domain}} + return c.Store.AddTLSChallenge(domain, cert) +} + +func (c *challengeTLSALPN) CleanUp(domain, token, keyAuth string) error { + log.Debugf("TLS Challenge CleanUp temp certificate for %s", domain) + + return c.Store.RemoveTLSChallenge(domain) +} + +// GetTLSALPNCertificate Get the temp certificate for ACME TLS-ALPN-O1 challenge. +func (p *Provider) GetTLSALPNCertificate(domain string) (*tls.Certificate, error) { + cert, err := p.Store.GetTLSChallenge(domain) + if err != nil { + return nil, err + } + + if cert == nil { + return nil, nil + } + + certificate, err := tls.X509KeyPair(cert.Certificate, cert.Key) + if err != nil { + return nil, err + } + + return &certificate, nil +} diff --git a/provider/acme/local_store.go b/provider/acme/local_store.go index 66541bf3b..c27ae1c60 100644 --- a/provider/acme/local_store.go +++ b/provider/acme/local_store.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "regexp" + "sync" "github.com/containous/traefik/log" "github.com/containous/traefik/safe" @@ -17,18 +18,22 @@ type LocalStore struct { filename string storedData *StoredData SaveDataChan chan *StoredData `json:"-"` + lock sync.RWMutex } // NewLocalStore initializes a new LocalStore with a file name -func NewLocalStore(filename string) LocalStore { - store := LocalStore{filename: filename, SaveDataChan: make(chan *StoredData)} +func NewLocalStore(filename string) *LocalStore { + store := &LocalStore{filename: filename, SaveDataChan: make(chan *StoredData)} store.listenSaveAction() return store } func (s *LocalStore) get() (*StoredData, error) { if s.storedData == nil { - s.storedData = &StoredData{HTTPChallenges: make(map[string]map[string][]byte)} + s.storedData = &StoredData{ + HTTPChallenges: make(map[string]map[string][]byte), + TLSChallenges: make(map[string]*Certificate), + } hasData, err := CheckFile(s.filename) if err != nil { @@ -159,3 +164,41 @@ func (s *LocalStore) SaveHTTPChallenges(httpChallenges map[string]map[string][]b s.storedData.HTTPChallenges = httpChallenges return nil } + +// AddTLSChallenge Add a certificate to the ACME TLS-ALPN-01 certificates storage +func (s *LocalStore) AddTLSChallenge(domain string, cert *Certificate) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.storedData.TLSChallenges == nil { + s.storedData.TLSChallenges = make(map[string]*Certificate) + } + + s.storedData.TLSChallenges[domain] = cert + return nil +} + +// GetTLSChallenge Get a certificate from the ACME TLS-ALPN-01 certificates storage +func (s *LocalStore) GetTLSChallenge(domain string) (*Certificate, error) { + s.lock.Lock() + defer s.lock.Unlock() + + if s.storedData.TLSChallenges == nil { + s.storedData.TLSChallenges = make(map[string]*Certificate) + } + + return s.storedData.TLSChallenges[domain], nil +} + +// RemoveTLSChallenge Remove a certificate from the ACME TLS-ALPN-01 certificates storage +func (s *LocalStore) RemoveTLSChallenge(domain string) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.storedData.TLSChallenges == nil { + return nil + } + + delete(s.storedData.TLSChallenges, domain) + return nil +} diff --git a/provider/acme/provider.go b/provider/acme/provider.go index aea1066c7..0f4dadb99 100644 --- a/provider/acme/provider.go +++ b/provider/acme/provider.go @@ -6,8 +6,6 @@ import ( "fmt" "io/ioutil" fmtlog "log" - "net" - "net/http" "reflect" "strings" "sync" @@ -15,7 +13,6 @@ import ( "github.com/BurntSushi/ty/fun" "github.com/containous/flaeg" - "github.com/containous/mux" "github.com/containous/traefik/log" "github.com/containous/traefik/rules" "github.com/containous/traefik/safe" @@ -46,6 +43,7 @@ type Configuration struct { OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` // Deprecated DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge"` HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge"` + TLSChallenge *TLSChallenge `description:"Activate TLS-ALPN-01 Challenge"` Domains []types.Domain `description:"CN and SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='*.main.net'. No SANs for wildcards domain. Wildcard domains only accepted with DNSChallenge"` } @@ -82,55 +80,17 @@ type HTTPChallenge struct { EntryPoint string `description:"HTTP challenge EntryPoint"` } +// TLSChallenge contains TLS challenge Configuration +type TLSChallenge struct{} + // SetConfigListenerChan initializes the configFromListenerChan func (p *Provider) SetConfigListenerChan(configFromListenerChan chan types.Configuration) { p.configFromListenerChan = configFromListenerChan } -func (p *Provider) init() error { - acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) - if p.ACMELogging { - legolog.Logger = fmtlog.New(log.WriterLevel(logrus.DebugLevel), "legolog: ", 0) - } else { - legolog.Logger = fmtlog.New(ioutil.Discard, "", 0) - } - - var err error - if p.Store == nil { - err = errors.New("no store found for the ACME provider") - return err - } - - p.account, err = p.Store.GetAccount() - if err != nil { - return fmt.Errorf("unable to get ACME account : %v", err) - } - - // Reset Account if caServer changed, thus registration URI can be updated - if p.account != nil && p.account.Registration != nil && !strings.HasPrefix(p.account.Registration.URI, p.CAServer) { - p.account = nil - } - - p.certificates, err = p.Store.GetCertificates() - if err != nil { - return fmt.Errorf("unable to get ACME certificates : %v", err) - } - - p.watchCertificate() - p.watchNewDomains() - - return nil -} - -func (p *Provider) initAccount() (*Account, error) { - if p.account == nil || len(p.account.Email) == 0 { - var err error - p.account, err = NewAccount(p.Email, p.KeyType) - if err != nil { - return nil, err - } - } - return p.account, nil +// SetCertificateStore allow to initialize certificate store +func (p *Provider) SetCertificateStore(certificateStore *traefiktls.CertificateStore) { + p.certificateStore = certificateStore } // ListenConfiguration sets a new Configuration into the configFromListenerChan @@ -150,94 +110,78 @@ func (p *Provider) ListenRequest(domain string) (*tls.Certificate, error) { return &certificate, err } -func (p *Provider) watchNewDomains() { - p.pool.Go(func(stop chan bool) { +// Provide allows the file provider to provide configurations to traefik +// using the given Configuration channel. +func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { + p.pool = pool + err := p.init() + if err != nil { + return err + } + + p.configurationChan = configurationChan + p.refreshCertificates() + + p.deleteUnnecessaryDomains() + for i := 0; i < len(p.Domains); i++ { + domain := p.Domains[i] + safe.Go(func() { + if _, err := p.resolveCertificate(domain, true); err != nil { + log.Errorf("Unable to obtain ACME certificate for domains %q : %v", strings.Join(domain.ToStrArray(), ","), err) + } + }) + } + + p.renewCertificates() + + ticker := time.NewTicker(24 * time.Hour) + pool.Go(func(stop chan bool) { for { select { - case config := <-p.configFromListenerChan: - for _, frontend := range config.Frontends { - for _, route := range frontend.Routes { - domainRules := rules.Rules{} - domains, err := domainRules.ParseDomains(route.Rule) - if err != nil { - log.Errorf("Error parsing domains in provider ACME: %v", err) - continue - } - - if len(domains) == 0 { - log.Debugf("No domain parsed in rule %q", route.Rule) - continue - } - - log.Debugf("Try to challenge certificate for domain %v founded in Host rule", domains) - - var domain types.Domain - if len(domains) > 0 { - domain = types.Domain{Main: domains[0]} - if len(domains) > 1 { - domain.SANs = domains[1:] - } - - safe.Go(func() { - if _, err := p.resolveCertificate(domain, false); err != nil { - log.Errorf("Unable to obtain ACME certificate for domains %q detected thanks to rule %q : %v", strings.Join(domains, ","), route.Rule, err) - } - }) - } - } - } + case <-ticker.C: + p.renewCertificates() case <-stop: + ticker.Stop() return } } }) + + return nil } -// SetCertificateStore allow to initialize certificate store -func (p *Provider) SetCertificateStore(certificateStore *traefiktls.CertificateStore) { - p.certificateStore = certificateStore -} - -func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurationFile bool) (*acme.CertificateResource, error) { - domains, err := p.getValidDomains(domain, domainFromConfigurationFile) - if err != nil { - return nil, err - } - - // Check provided certificates - uncheckedDomains := p.getUncheckedDomains(domains, !domainFromConfigurationFile) - if len(uncheckedDomains) == 0 { - return nil, nil - } - - log.Debugf("Loading ACME certificates %+v...", uncheckedDomains) - - client, err := p.getClient() - if err != nil { - return nil, fmt.Errorf("cannot get ACME client %v", err) - } - - bundle := true - - certificate, err := client.ObtainCertificate(uncheckedDomains, bundle, nil, OSCPMustStaple) - if err != nil { - return nil, fmt.Errorf("cannot obtain certificates: %+v", err) - } - - if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 { - return nil, fmt.Errorf("domains %v generate certificate with no value: %v", uncheckedDomains, certificate) - } - - log.Debugf("Certificates obtained for domains %+v", uncheckedDomains) - - if len(uncheckedDomains) > 1 { - domain = types.Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]} +func (p *Provider) init() error { + acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) + if p.ACMELogging { + legolog.Logger = fmtlog.New(log.WriterLevel(logrus.InfoLevel), "legolog: ", 0) } else { - domain = types.Domain{Main: uncheckedDomains[0]} + legolog.Logger = fmtlog.New(ioutil.Discard, "", 0) } - p.addCertificateForDomain(domain, certificate.Certificate, certificate.PrivateKey) - return certificate, nil + if p.Store == nil { + return errors.New("no store found for the ACME provider") + } + + var err error + p.account, err = p.Store.GetAccount() + if err != nil { + return fmt.Errorf("unable to get ACME account : %v", err) + } + + // Reset Account if caServer changed, thus registration URI can be updated + if p.account != nil && p.account.Registration != nil && !strings.HasPrefix(p.account.Registration.URI, p.CAServer) { + p.account = nil + } + + p.certificates, err = p.Store.GetCertificates() + if err != nil { + return fmt.Errorf("unable to get ACME certificates : %v", err) + } + + p.watchCertificate() + p.watchNewDomains() + + return nil } func (p *Provider) getClient() (*acme.Client, error) { @@ -299,7 +243,7 @@ func (p *Provider) getClient() (*acme.Client, error) { return nil, err } - client.ExcludeChallenges([]acme.Challenge{acme.HTTP01}) + client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSALPN01}) err = client.SetChallengeProvider(acme.DNS01, provider) if err != nil { @@ -308,67 +252,140 @@ func (p *Provider) getClient() (*acme.Client, error) { } else if p.HTTPChallenge != nil && len(p.HTTPChallenge.EntryPoint) > 0 { log.Debug("Using HTTP Challenge provider.") - client.ExcludeChallenges([]acme.Challenge{acme.DNS01}) + client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSALPN01}) - err = client.SetChallengeProvider(acme.HTTP01, p) + err = client.SetChallengeProvider(acme.HTTP01, &challengeHTTP{Store: p.Store}) + if err != nil { + return nil, err + } + } else if p.TLSChallenge != nil { + log.Debug("Using TLS Challenge provider.") + + client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) + + err = client.SetChallengeProvider(acme.TLSALPN01, &challengeTLSALPN{Store: p.Store}) if err != nil { return nil, err } } else { - return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge") + return nil, errors.New("ACME challenge not specified, please select TLS or HTTP or DNS Challenge") } p.client = client return p.client, nil } -// Present presents a challenge to obtain new ACME certificate -func (p *Provider) Present(domain, token, keyAuth string) error { - return presentHTTPChallenge(domain, token, keyAuth, p.Store) +func (p *Provider) initAccount() (*Account, error) { + if p.account == nil || len(p.account.Email) == 0 { + var err error + p.account, err = NewAccount(p.Email, p.KeyType) + if err != nil { + return nil, err + } + } + return p.account, nil } -// CleanUp cleans the challenges when certificate is obtained -func (p *Provider) CleanUp(domain, token, keyAuth string) error { - return cleanUpHTTPChallenge(domain, token, p.Store) -} - -// Provide allows the file provider to provide configurations to traefik -// using the given Configuration channel. -func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error { - p.pool = pool - err := p.init() - if err != nil { - return err - } - - p.configurationChan = configurationChan - p.refreshCertificates() - - p.deleteUnnecessaryDomains() - for i := 0; i < len(p.Domains); i++ { - domain := p.Domains[i] - safe.Go(func() { - if _, err := p.resolveCertificate(domain, true); err != nil { - log.Errorf("Unable to obtain ACME certificate for domains %q : %v", strings.Join(domain.ToStrArray(), ","), err) - } - }) - } - - p.renewCertificates() - - ticker := time.NewTicker(24 * time.Hour) - pool.Go(func(stop chan bool) { +func (p *Provider) watchNewDomains() { + p.pool.Go(func(stop chan bool) { for { select { - case <-ticker.C: - p.renewCertificates() + case config := <-p.configFromListenerChan: + for _, frontend := range config.Frontends { + for _, route := range frontend.Routes { + domainRules := rules.Rules{} + domains, err := domainRules.ParseDomains(route.Rule) + if err != nil { + log.Errorf("Error parsing domains in provider ACME: %v", err) + continue + } + + if len(domains) == 0 { + log.Debugf("No domain parsed in rule %q", route.Rule) + continue + } + + log.Debugf("Try to challenge certificate for domain %v founded in Host rule", domains) + + var domain types.Domain + if len(domains) > 0 { + domain = types.Domain{Main: domains[0]} + if len(domains) > 1 { + domain.SANs = domains[1:] + } + + safe.Go(func() { + if _, err := p.resolveCertificate(domain, false); err != nil { + log.Errorf("Unable to obtain ACME certificate for domains %q detected thanks to rule %q : %v", strings.Join(domains, ","), route.Rule, err) + } + }) + } + } + } case <-stop: - ticker.Stop() return } } }) +} +func (p *Provider) resolveCertificate(domain types.Domain, domainFromConfigurationFile bool) (*acme.CertificateResource, error) { + domains, err := p.getValidDomains(domain, domainFromConfigurationFile) + if err != nil { + return nil, err + } + + // Check provided certificates + uncheckedDomains := p.getUncheckedDomains(domains, !domainFromConfigurationFile) + if len(uncheckedDomains) == 0 { + return nil, nil + } + + log.Debugf("Loading ACME certificates %+v...", uncheckedDomains) + + client, err := p.getClient() + if err != nil { + return nil, fmt.Errorf("cannot get ACME client %v", err) + } + + bundle := true + + certificate, err := client.ObtainCertificate(uncheckedDomains, bundle, nil, OSCPMustStaple) + if err != nil { + return nil, fmt.Errorf("cannot obtain certificates: %+v", err) + } + + if len(certificate.Certificate) == 0 || len(certificate.PrivateKey) == 0 { + return nil, fmt.Errorf("domains %v generate certificate with no value: %v", uncheckedDomains, certificate) + } + + log.Debugf("Certificates obtained for domains %+v", uncheckedDomains) + + if len(uncheckedDomains) > 1 { + domain = types.Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]} + } else { + domain = types.Domain{Main: uncheckedDomains[0]} + } + p.addCertificateForDomain(domain, certificate.Certificate, certificate.PrivateKey) + + return certificate, nil +} + +func dnsOverrideDelay(delay flaeg.Duration) error { + if delay == 0 { + return nil + } + + if delay > 0 { + log.Debugf("Delaying %d rather than validating DNS propagation now.", delay) + + acme.PreCheckDNS = func(_, _ string) (bool, error) { + time.Sleep(time.Duration(delay)) + return true, nil + } + } else { + return fmt.Errorf("delayBeforeCheck: %d cannot be less than 0", delay) + } return nil } @@ -376,219 +393,6 @@ func (p *Provider) addCertificateForDomain(domain types.Domain, certificate []by p.certsChan <- &Certificate{Certificate: certificate, Key: key, Domain: domain} } -func (p *Provider) watchCertificate() { - p.certsChan = make(chan *Certificate) - p.pool.Go(func(stop chan bool) { - for { - select { - case cert := <-p.certsChan: - certUpdated := false - for _, domainsCertificate := range p.certificates { - if reflect.DeepEqual(cert.Domain, domainsCertificate.Domain) { - domainsCertificate.Certificate = cert.Certificate - domainsCertificate.Key = cert.Key - certUpdated = true - break - } - } - if !certUpdated { - p.certificates = append(p.certificates, cert) - } - p.saveCertificates() - - case <-stop: - return - } - } - }) -} - -func (p *Provider) saveCertificates() { - err := p.Store.SaveCertificates(p.certificates) - if err != nil { - log.Error(err) - } - p.refreshCertificates() -} - -func (p *Provider) refreshCertificates() { - config := types.ConfigMessage{ - ProviderName: "ACME", - Configuration: &types.Configuration{ - Backends: map[string]*types.Backend{}, - Frontends: map[string]*types.Frontend{}, - TLS: []*traefiktls.Configuration{}, - }, - } - - for _, cert := range p.certificates { - certificate := &traefiktls.Certificate{CertFile: traefiktls.FileOrContent(cert.Certificate), KeyFile: traefiktls.FileOrContent(cert.Key)} - config.Configuration.TLS = append(config.Configuration.TLS, &traefiktls.Configuration{Certificate: certificate, EntryPoints: []string{p.EntryPoint}}) - } - p.configurationChan <- config -} - -// Timeout calculates the maximum of time allowed to resolved an ACME challenge -func (p *Provider) Timeout() (timeout, interval time.Duration) { - return 60 * time.Second, 5 * time.Second -} - -func (p *Provider) renewCertificates() { - log.Info("Testing certificate renew...") - for _, certificate := range p.certificates { - crt, err := getX509Certificate(certificate) - // If there's an error, we assume the cert is broken, and needs update - // <= 30 days left, renew certificate - if err != nil || crt == nil || crt.NotAfter.Before(time.Now().Add(24*30*time.Hour)) { - client, err := p.getClient() - if err != nil { - log.Infof("Error renewing certificate from LE : %+v, %v", certificate.Domain, err) - continue - } - - log.Infof("Renewing certificate from LE : %+v", certificate.Domain) - - renewedCert, err := client.RenewCertificate(acme.CertificateResource{ - Domain: certificate.Domain.Main, - PrivateKey: certificate.Key, - Certificate: certificate.Certificate, - }, true, OSCPMustStaple) - - if err != nil { - log.Errorf("Error renewing certificate from LE: %v, %v", certificate.Domain, err) - continue - } - - if len(renewedCert.Certificate) == 0 || len(renewedCert.PrivateKey) == 0 { - log.Errorf("domains %v renew certificate with no value: %v", certificate.Domain.ToStrArray(), certificate) - continue - } - - p.addCertificateForDomain(certificate.Domain, renewedCert.Certificate, renewedCert.PrivateKey) - } - } -} - -// AddRoutes add routes on internal router -func (p *Provider) AddRoutes(router *mux.Router) { - router.Methods(http.MethodGet). - Path(acme.HTTP01ChallengePath("{token}")). - Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - if token, ok := vars["token"]; ok { - domain, _, err := net.SplitHostPort(req.Host) - if err != nil { - log.Debugf("Unable to split host and port: %v. Fallback to request host.", err) - domain = req.Host - } - - tokenValue := getTokenValue(token, domain, p.Store) - if len(tokenValue) > 0 { - rw.WriteHeader(http.StatusOK) - _, err = rw.Write(tokenValue) - if err != nil { - log.Errorf("Unable to write token : %v", err) - } - return - } - } - rw.WriteHeader(http.StatusNotFound) - })) -} - -// Get provided certificate which check a domains list (Main and SANs) -// from static and dynamic provided certificates -func (p *Provider) getUncheckedDomains(domainsToCheck []string, checkConfigurationDomains bool) []string { - log.Debugf("Looking for provided certificate(s) to validate %q...", domainsToCheck) - var allDomains []string - - allDomains = p.certificateStore.GetAllDomains() - - // Get ACME certificates - for _, certificate := range p.certificates { - allDomains = append(allDomains, strings.Join(certificate.Domain.ToStrArray(), ",")) - } - - // Get Configuration Domains - if checkConfigurationDomains { - for i := 0; i < len(p.Domains); i++ { - allDomains = append(allDomains, strings.Join(p.Domains[i].ToStrArray(), ",")) - } - } - - return searchUncheckedDomains(domainsToCheck, allDomains) -} - -func searchUncheckedDomains(domainsToCheck []string, existentDomains []string) []string { - var uncheckedDomains []string - for _, domainToCheck := range domainsToCheck { - if !isDomainAlreadyChecked(domainToCheck, existentDomains) { - uncheckedDomains = append(uncheckedDomains, domainToCheck) - } - } - if len(uncheckedDomains) == 0 { - log.Debugf("No ACME certificate to generate for domains %q.", domainsToCheck) - } else { - log.Debugf("Domains %q need ACME certificates generation for domains %q.", domainsToCheck, strings.Join(uncheckedDomains, ",")) - } - return uncheckedDomains -} - -func getX509Certificate(certificate *Certificate) (*x509.Certificate, error) { - var crt *x509.Certificate - tlsCert, err := tls.X509KeyPair(certificate.Certificate, certificate.Key) - if err != nil { - log.Errorf("Failed to load TLS keypair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", certificate.Domain.Main, strings.Join(certificate.Domain.SANs, ","), err) - return nil, err - } - crt = tlsCert.Leaf - if crt == nil { - crt, err = x509.ParseCertificate(tlsCert.Certificate[0]) - if err != nil { - log.Errorf("Failed to parse TLS keypair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", certificate.Domain.Main, strings.Join(certificate.Domain.SANs, ","), err) - } - } - return crt, err -} - -// getValidDomains checks if given domain is allowed to generate a ACME certificate and return it -func (p *Provider) getValidDomains(domain types.Domain, wildcardAllowed bool) ([]string, error) { - domains := domain.ToStrArray() - if len(domains) == 0 { - return nil, errors.New("unable to generate a certificate in ACME provider when no domain is given") - } - if strings.HasPrefix(domain.Main, "*") { - if !wildcardAllowed { - return nil, fmt.Errorf("unable to generate a wildcard certificate in ACME provider for domain %q from a 'Host' rule", strings.Join(domains, ",")) - } - if p.DNSChallenge == nil { - return nil, fmt.Errorf("unable to generate a wildcard certificate in ACME provider for domain %q : ACME needs a DNSChallenge", strings.Join(domains, ",")) - } - if strings.HasPrefix(domain.Main, "*.*") { - return nil, fmt.Errorf("unable to generate a wildcard certificate in ACME provider for domain %q : ACME does not allow '*.*' wildcard domain", strings.Join(domains, ",")) - } - } - for _, san := range domain.SANs { - if strings.HasPrefix(san, "*") { - return nil, fmt.Errorf("unable to generate a certificate in ACME provider for domains %q: SAN %q can not be a wildcard domain", strings.Join(domains, ","), san) - } - } - - domains = fun.Map(types.CanonicalDomain, domains).([]string) - return domains, nil -} - -func isDomainAlreadyChecked(domainToCheck string, existentDomains []string) bool { - for _, certDomains := range existentDomains { - for _, certDomain := range strings.Split(certDomains, ",") { - if types.MatchDomain(domainToCheck, certDomain) { - return true - } - } - } - return false -} - // deleteUnnecessaryDomains deletes from the configuration : // - Duplicated domains // - Domains which are checked by wildcard domain @@ -643,3 +447,193 @@ func (p *Provider) deleteUnnecessaryDomains() { p.Domains = newDomains } + +func (p *Provider) watchCertificate() { + p.certsChan = make(chan *Certificate) + p.pool.Go(func(stop chan bool) { + for { + select { + case cert := <-p.certsChan: + certUpdated := false + for _, domainsCertificate := range p.certificates { + if reflect.DeepEqual(cert.Domain, domainsCertificate.Domain) { + domainsCertificate.Certificate = cert.Certificate + domainsCertificate.Key = cert.Key + certUpdated = true + break + } + } + if !certUpdated { + p.certificates = append(p.certificates, cert) + } + + err := p.saveCertificates() + if err != nil { + log.Error(err) + } + + case <-stop: + return + } + } + }) +} + +func (p *Provider) saveCertificates() error { + err := p.Store.SaveCertificates(p.certificates) + + p.refreshCertificates() + + return err +} + +func (p *Provider) refreshCertificates() { + config := types.ConfigMessage{ + ProviderName: "ACME", + Configuration: &types.Configuration{ + Backends: map[string]*types.Backend{}, + Frontends: map[string]*types.Frontend{}, + TLS: []*traefiktls.Configuration{}, + }, + } + + for _, cert := range p.certificates { + certificate := &traefiktls.Certificate{CertFile: traefiktls.FileOrContent(cert.Certificate), KeyFile: traefiktls.FileOrContent(cert.Key)} + config.Configuration.TLS = append(config.Configuration.TLS, &traefiktls.Configuration{Certificate: certificate, EntryPoints: []string{p.EntryPoint}}) + } + p.configurationChan <- config +} + +func (p *Provider) renewCertificates() { + log.Info("Testing certificate renew...") + for _, certificate := range p.certificates { + crt, err := getX509Certificate(certificate) + // If there's an error, we assume the cert is broken, and needs update + // <= 30 days left, renew certificate + if err != nil || crt == nil || crt.NotAfter.Before(time.Now().Add(24*30*time.Hour)) { + client, err := p.getClient() + if err != nil { + log.Infof("Error renewing certificate from LE : %+v, %v", certificate.Domain, err) + continue + } + + log.Infof("Renewing certificate from LE : %+v", certificate.Domain) + + renewedCert, err := client.RenewCertificate(acme.CertificateResource{ + Domain: certificate.Domain.Main, + PrivateKey: certificate.Key, + Certificate: certificate.Certificate, + }, true, OSCPMustStaple) + + if err != nil { + log.Errorf("Error renewing certificate from LE: %v, %v", certificate.Domain, err) + continue + } + + if len(renewedCert.Certificate) == 0 || len(renewedCert.PrivateKey) == 0 { + log.Errorf("domains %v renew certificate with no value: %v", certificate.Domain.ToStrArray(), certificate) + continue + } + + p.addCertificateForDomain(certificate.Domain, renewedCert.Certificate, renewedCert.PrivateKey) + } + } +} + +// Get provided certificate which check a domains list (Main and SANs) +// from static and dynamic provided certificates +func (p *Provider) getUncheckedDomains(domainsToCheck []string, checkConfigurationDomains bool) []string { + log.Debugf("Looking for provided certificate(s) to validate %q...", domainsToCheck) + + allDomains := p.certificateStore.GetAllDomains() + + // Get ACME certificates + for _, certificate := range p.certificates { + allDomains = append(allDomains, strings.Join(certificate.Domain.ToStrArray(), ",")) + } + + // Get Configuration Domains + if checkConfigurationDomains { + for i := 0; i < len(p.Domains); i++ { + allDomains = append(allDomains, strings.Join(p.Domains[i].ToStrArray(), ",")) + } + } + + return searchUncheckedDomains(domainsToCheck, allDomains) +} + +func searchUncheckedDomains(domainsToCheck []string, existentDomains []string) []string { + var uncheckedDomains []string + for _, domainToCheck := range domainsToCheck { + if !isDomainAlreadyChecked(domainToCheck, existentDomains) { + uncheckedDomains = append(uncheckedDomains, domainToCheck) + } + } + + if len(uncheckedDomains) == 0 { + log.Debugf("No ACME certificate to generate for domains %q.", domainsToCheck) + } else { + log.Debugf("Domains %q need ACME certificates generation for domains %q.", domainsToCheck, strings.Join(uncheckedDomains, ",")) + } + return uncheckedDomains +} + +func getX509Certificate(certificate *Certificate) (*x509.Certificate, error) { + tlsCert, err := tls.X509KeyPair(certificate.Certificate, certificate.Key) + if err != nil { + log.Errorf("Failed to load TLS keypair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", certificate.Domain.Main, strings.Join(certificate.Domain.SANs, ","), err) + return nil, err + } + + crt := tlsCert.Leaf + if crt == nil { + crt, err = x509.ParseCertificate(tlsCert.Certificate[0]) + if err != nil { + log.Errorf("Failed to parse TLS keypair from ACME certificate for domain %q (SAN : %q), certificate will be renewed : %v", certificate.Domain.Main, strings.Join(certificate.Domain.SANs, ","), err) + } + } + + return crt, err +} + +// getValidDomains checks if given domain is allowed to generate a ACME certificate and return it +func (p *Provider) getValidDomains(domain types.Domain, wildcardAllowed bool) ([]string, error) { + domains := domain.ToStrArray() + if len(domains) == 0 { + return nil, errors.New("unable to generate a certificate in ACME provider when no domain is given") + } + + if strings.HasPrefix(domain.Main, "*") { + if !wildcardAllowed { + return nil, fmt.Errorf("unable to generate a wildcard certificate in ACME provider for domain %q from a 'Host' rule", strings.Join(domains, ",")) + } + + if p.DNSChallenge == nil { + return nil, fmt.Errorf("unable to generate a wildcard certificate in ACME provider for domain %q : ACME needs a DNSChallenge", strings.Join(domains, ",")) + } + + if strings.HasPrefix(domain.Main, "*.*") { + return nil, fmt.Errorf("unable to generate a wildcard certificate in ACME provider for domain %q : ACME does not allow '*.*' wildcard domain", strings.Join(domains, ",")) + } + } + + for _, san := range domain.SANs { + if strings.HasPrefix(san, "*") { + return nil, fmt.Errorf("unable to generate a certificate in ACME provider for domains %q: SAN %q can not be a wildcard domain", strings.Join(domains, ","), san) + } + } + + domains = fun.Map(types.CanonicalDomain, domains).([]string) + return domains, nil +} + +func isDomainAlreadyChecked(domainToCheck string, existentDomains []string) bool { + for _, certDomains := range existentDomains { + for _, certDomain := range strings.Split(certDomains, ",") { + if types.MatchDomain(domainToCheck, certDomain) { + return true + } + } + } + return false +} diff --git a/provider/acme/store.go b/provider/acme/store.go index 77062d722..efd97852b 100644 --- a/provider/acme/store.go +++ b/provider/acme/store.go @@ -5,6 +5,7 @@ type StoredData struct { Account *Account Certificates []*Certificate HTTPChallenges map[string]map[string][]byte + TLSChallenges map[string]*Certificate } // Store is a generic interface to represents a storage @@ -13,6 +14,11 @@ type Store interface { SaveAccount(*Account) error GetCertificates() ([]*Certificate, error) SaveCertificates([]*Certificate) error + GetHTTPChallenges() (map[string]map[string][]byte, error) SaveHTTPChallenges(map[string]map[string][]byte) error + + AddTLSChallenge(domain string, cert *Certificate) error + GetTLSChallenge(domain string) (*Certificate, error) + RemoveTLSChallenge(domain string) error } diff --git a/server/server.go b/server/server.go index 340ee650d..9a929a7e5 100644 --- a/server/server.go +++ b/server/server.go @@ -37,6 +37,7 @@ import ( "github.com/containous/traefik/whitelist" "github.com/sirupsen/logrus" "github.com/urfave/negroni" + "github.com/xenolf/lego/acme" ) var httpServerLogger = stdlog.New(log.WriterLevel(logrus.DebugLevel), "", 0) @@ -68,6 +69,7 @@ type EntryPoint struct { InternalRouter types.InternalRouter Configuration *configuration.EntryPoint OnDemandListener func(string) (*tls.Certificate, error) + TLSALPNGetter func(string) (*tls.Certificate, error) CertificateStore *traefiktls.CertificateStore } @@ -79,6 +81,7 @@ type serverEntryPoint struct { httpRouter *middlewares.HandlerSwitcher certs *safe.Safe onDemandListener func(string) (*tls.Certificate, error) + tlsALPNGetter func(string) (*tls.Certificate, error) } // NewServer returns an initialized Server. @@ -274,6 +277,7 @@ func (s *Server) AddListener(listener func(types.Configuration)) { // getCertificate allows to customize tlsConfig.GetCertificate behaviour to get the certificates inserted dynamically func (s *serverEntryPoint) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { domainToCheck := types.CanonicalDomain(clientHello.ServerName) + if s.certs.Get() != nil { for domains, cert := range s.certs.Get().(map[string]*tls.Certificate) { for _, certDomain := range strings.Split(domains, ",") { @@ -284,9 +288,22 @@ func (s *serverEntryPoint) getCertificate(clientHello *tls.ClientHelloInfo) (*tl } log.Debugf("No certificate provided dynamically can check the domain %q, a per default certificate will be used.", domainToCheck) } + + if s.tlsALPNGetter != nil { + cert, err := s.tlsALPNGetter(domainToCheck) + if err != nil { + return nil, err + } + + if cert != nil { + return cert, nil + } + } + if s.onDemandListener != nil { return s.onDemandListener(domainToCheck) } + return nil, nil } @@ -319,8 +336,9 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL } s.serverEntryPoints[entryPointName].certs.Set(make(map[string]*tls.Certificate)) + // ensure http2 enabled - config.NextProtos = []string{"h2", "http/1.1"} + config.NextProtos = []string{"h2", "http/1.1", acme.ACMETLS1Protocol} if len(tlsOption.ClientCAFiles) > 0 { log.Warnf("Deprecated configuration found during TLS configuration creation: %s. Please use %s (which allows to make the CA Files optional).", "tls.ClientCAFiles", "tls.ClientCA.files") diff --git a/server/server_configuration.go b/server/server_configuration.go index ec9b904a8..e1d271132 100644 --- a/server/server_configuration.go +++ b/server/server_configuration.go @@ -22,6 +22,7 @@ import ( traefiktls "github.com/containous/traefik/tls" "github.com/containous/traefik/types" "github.com/eapache/channels" + "github.com/sirupsen/logrus" "github.com/urfave/negroni" "github.com/vulcand/oxy/forward" ) @@ -291,9 +292,10 @@ func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) { s.defaultConfigurationValues(configMsg.Configuration) currentConfigurations := s.currentConfigurations.Get().(types.Configurations) - jsonConf, _ := json.Marshal(configMsg.Configuration) - - log.Debugf("Configuration received from provider %s: %s", configMsg.ProviderName, string(jsonConf)) + if log.GetLevel() == logrus.DebugLevel { + jsonConf, _ := json.Marshal(configMsg.Configuration) + log.Debugf("Configuration received from provider %s: %s", configMsg.ProviderName, string(jsonConf)) + } if configMsg.Configuration == nil || configMsg.Configuration.Backends == nil && configMsg.Configuration.Frontends == nil && configMsg.Configuration.TLS == nil { log.Infof("Skipping empty Configuration for provider %s", configMsg.ProviderName) @@ -553,6 +555,7 @@ func (s *Server) buildServerEntryPoints() map[string]*serverEntryPoint { serverEntryPoints[entryPointName] = &serverEntryPoint{ httpRouter: middlewares.NewHandlerSwitcher(s.buildDefaultHTTPRouter()), onDemandListener: entryPoint.OnDemandListener, + tlsALPNGetter: entryPoint.TLSALPNGetter, } if entryPoint.CertificateStore != nil { serverEntryPoints[entryPointName].certs = entryPoint.CertificateStore.DynamicCerts diff --git a/tls/generate/generate.go b/tls/generate/generate.go index d0660cba5..91d73a731 100644 --- a/tls/generate/generate.go +++ b/tls/generate/generate.go @@ -1,7 +1,6 @@ package generate import ( - "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha256" @@ -15,6 +14,9 @@ import ( "time" ) +// DefaultDomain Traefik domain for the default certificate +const DefaultDomain = "TRAEFIK DEFAULT CERT" + // DefaultCertificate generates random TLS certificates func DefaultCertificate() (*tls.Certificate, error) { randomBytes := make([]byte, 100) @@ -78,7 +80,7 @@ func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]by template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - CommonName: "TRAEFIK DEFAULT CERT", + CommonName: DefaultDomain, }, NotBefore: time.Now(), NotAfter: expiration, @@ -90,21 +92,3 @@ func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]by return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey) } - -// PemEncode encodes date in PEM format -func PemEncode(data interface{}) []byte { - var pemBlock *pem.Block - switch key := data.(type) { - case *ecdsa.PrivateKey: - keyBytes, _ := x509.MarshalECPrivateKey(key) - pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes} - case *rsa.PrivateKey: - pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)} - case *x509.CertificateRequest: - pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw} - case []byte: - pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: data.([]byte)} - } - - return pem.EncodeToMemory(pemBlock) -} diff --git a/vendor/github.com/OpenDNS/vegadns2client/LICENSE b/vendor/github.com/OpenDNS/vegadns2client/LICENSE new file mode 100644 index 000000000..f9361e31c --- /dev/null +++ b/vendor/github.com/OpenDNS/vegadns2client/LICENSE @@ -0,0 +1,13 @@ +Copyright 2018, Cisco Systems, Inc. + +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/OpenDNS/vegadns2client/client.go b/vendor/github.com/OpenDNS/vegadns2client/client.go new file mode 100644 index 000000000..21096568a --- /dev/null +++ b/vendor/github.com/OpenDNS/vegadns2client/client.go @@ -0,0 +1,71 @@ +package vegadns2client + +import ( + "fmt" + "net/http" + "net/url" + "strings" +) + +// VegaDNSClient - Struct for holding VegaDNSClient specific attributes +type VegaDNSClient struct { + client http.Client + baseurl string + version string + User string + Pass string + APIKey string + APISecret string + token Token +} + +// Send - Central place for sending requests +// Input: method, endpoint, params +// Output: *http.Response +func (vega *VegaDNSClient) Send(method string, endpoint string, params map[string]string) (*http.Response, error) { + vegaURL := vega.getURL(endpoint) + + p := url.Values{} + for k, v := range params { + p.Set(k, v) + } + + var err error + var req *http.Request + + if (method == "GET") || (method == "DELETE") { + vegaURL = fmt.Sprintf("%s?%s", vegaURL, p.Encode()) + req, err = http.NewRequest(method, vegaURL, nil) + } else { + req, err = http.NewRequest(method, vegaURL, strings.NewReader(p.Encode())) + } + + if err != nil { + return nil, fmt.Errorf("Error preparing request: %s", err) + } + + if vega.User != "" && vega.Pass != "" { + // Basic Auth + req.SetBasicAuth(vega.User, vega.Pass) + } else if vega.APIKey != "" && vega.APISecret != "" { + // OAuth + vega.getAuthToken() + err = vega.token.valid() + if err != nil { + return nil, err + } + req.Header.Set("Authorization", vega.getBearer()) + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + return vega.client.Do(req) +} + +func (vega *VegaDNSClient) getURL(endpoint string) string { + return fmt.Sprintf("%s/%s/%s", vega.baseurl, vega.version, endpoint) +} + +func (vega *VegaDNSClient) stillAuthorized() error { + return vega.token.valid() +} diff --git a/vendor/github.com/OpenDNS/vegadns2client/domains.go b/vendor/github.com/OpenDNS/vegadns2client/domains.go new file mode 100644 index 000000000..b9713f4ed --- /dev/null +++ b/vendor/github.com/OpenDNS/vegadns2client/domains.go @@ -0,0 +1,80 @@ +package vegadns2client + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "strings" +) + +// Domain - struct containing a domain object +type Domain struct { + Status string `json:"status"` + Domain string `json:"domain"` + DomainID int `json:"domain_id"` + OwnerID int `json:"owner_id"` +} + +// DomainResponse - api response of a domain list +type DomainResponse struct { + Status string `json:"status"` + Total int `json:"total_domains"` + Domains []Domain `json:"domains"` +} + +// GetDomainID - returns the id for a domain +// Input: domain +// Output: int, err +func (vega *VegaDNSClient) GetDomainID(domain string) (int, error) { + params := make(map[string]string) + params["search"] = domain + + resp, err := vega.Send("GET", "domains", params) + + if err != nil { + return -1, fmt.Errorf("Error sending GET to GetDomainID: %s", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return -1, fmt.Errorf("Error reading response from GET to GetDomainID: %s", err) + } + if resp.StatusCode != http.StatusOK { + return -1, fmt.Errorf("Got bad answer from VegaDNS on GetDomainID. Code: %d. Message: %s", resp.StatusCode, string(body)) + } + + answer := DomainResponse{} + if err := json.Unmarshal(body, &answer); err != nil { + return -1, fmt.Errorf("Error unmarshalling body from GetDomainID: %s", err) + } + log.Println(answer) + for _, d := range answer.Domains { + if d.Domain == domain { + return d.DomainID, nil + } + } + + return -1, fmt.Errorf("Didnt find domain %s", domain) + +} + +// GetAuthZone retrieves the closest match to a given +// domain. Example: Given an argument "a.b.c.d.e", and a VegaDNS +// hosted domain of "c.d.e", GetClosestMatchingDomain will return +// "c.d.e". +func (vega *VegaDNSClient) GetAuthZone(fqdn string) (string, int, error) { + fqdn = strings.TrimSuffix(fqdn, ".") + numComponents := len(strings.Split(fqdn, ".")) + for i := 1; i < numComponents; i++ { + tmpHostname := strings.SplitN(fqdn, ".", i)[i-1] + log.Printf("tmpHostname for i = %d: %s\n", i, tmpHostname) + if domainID, err := vega.GetDomainID(tmpHostname); err == nil { + log.Printf("Found zone: %s\n\tShortened to %s\n", tmpHostname, strings.TrimSuffix(tmpHostname, ".")) + return strings.TrimSuffix(tmpHostname, "."), domainID, nil + } + } + log.Println("Unable to find hosted zone in vegadns") + return "", -1, fmt.Errorf("Unable to find auth zone for fqdn %s", fqdn) +} diff --git a/vendor/github.com/OpenDNS/vegadns2client/main.go b/vendor/github.com/OpenDNS/vegadns2client/main.go new file mode 100644 index 000000000..0d3e79a61 --- /dev/null +++ b/vendor/github.com/OpenDNS/vegadns2client/main.go @@ -0,0 +1,19 @@ +package vegadns2client + +import ( + "net/http" + "time" +) + +// NewVegaDNSClient - helper to instantiate a client +// Input: url string +// Output: VegaDNSClient +func NewVegaDNSClient(url string) VegaDNSClient { + httpClient := http.Client{Timeout: 15 * time.Second} + return VegaDNSClient{ + client: httpClient, + baseurl: url, + version: "1.0", + token: Token{}, + } +} diff --git a/vendor/github.com/OpenDNS/vegadns2client/records.go b/vendor/github.com/OpenDNS/vegadns2client/records.go new file mode 100644 index 000000000..36bd8d906 --- /dev/null +++ b/vendor/github.com/OpenDNS/vegadns2client/records.go @@ -0,0 +1,113 @@ +package vegadns2client + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strings" +) + +// Record - struct representing a Record object +type Record struct { + Name string `json:"name"` + Value string `json:"value"` + RecordType string `json:"record_type"` + TTL int `json:"ttl"` + RecordID int `json:"record_id"` + LocationID string `json:"location_id"` + DomainID int `json:"domain_id"` +} + +// RecordsResponse - api response list of records +type RecordsResponse struct { + Status string `json:"status"` + Total int `json:"total_records"` + Domain Domain `json:"domain"` + Records []Record `json:"records"` +} + +// GetRecordID - helper to get the id of a record +// Input: domainID, record, recordType +// Output: int +func (vega *VegaDNSClient) GetRecordID(domainID int, record string, recordType string) (int, error) { + params := make(map[string]string) + params["domain_id"] = fmt.Sprintf("%d", domainID) + + resp, err := vega.Send("GET", "records", params) + + if err != nil { + return -1, fmt.Errorf("Error sending GET to GetRecordID: %s", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return -1, fmt.Errorf("Error reading response from GetRecordID: %s", err) + } + if resp.StatusCode != http.StatusOK { + return -1, fmt.Errorf("Got bad answer from VegaDNS on GetRecordID. Code: %d. Message: %s", resp.StatusCode, string(body)) + } + + answer := RecordsResponse{} + if err := json.Unmarshal(body, &answer); err != nil { + return -1, fmt.Errorf("Error unmarshalling body from GetRecordID: %s", err) + } + + for _, r := range answer.Records { + if r.Name == record && r.RecordType == recordType { + return r.RecordID, nil + } + } + + return -1, errors.New("Couldnt find record") +} + +// CreateTXT - Creates a TXT record +// Input: domainID, fqdn, value, ttl +// Output: nil or error +func (vega *VegaDNSClient) CreateTXT(domainID int, fqdn string, value string, ttl int) error { + params := make(map[string]string) + + params["record_type"] = "TXT" + params["ttl"] = fmt.Sprintf("%d", ttl) + params["domain_id"] = fmt.Sprintf("%d", domainID) + params["name"] = strings.TrimSuffix(fqdn, ".") + params["value"] = value + + resp, err := vega.Send("POST", "records", params) + + if err != nil { + return fmt.Errorf("Send POST error in CreateTXT: %s", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("Error reading POST response in CreateTXT: %s", err) + } + if resp.StatusCode != http.StatusCreated { + return fmt.Errorf("Got bad answer from VegaDNS on CreateTXT. Code: %d. Message: %s", resp.StatusCode, string(body)) + } + + return nil +} + +// DeleteRecord - deletes a record by id +// Input: recordID +// Output: nil or error +func (vega *VegaDNSClient) DeleteRecord(recordID int) error { + resp, err := vega.Send("DELETE", fmt.Sprintf("records/%d", recordID), nil) + if err != nil { + return fmt.Errorf("Send DELETE error in DeleteTXT: %s", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("Error reading DELETE response in DeleteTXT: %s", err) + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Got bad answer from VegaDNS on DeleteTXT. Code: %d. Message: %s", resp.StatusCode, string(body)) + } + + return nil +} diff --git a/vendor/github.com/OpenDNS/vegadns2client/tokens.go b/vendor/github.com/OpenDNS/vegadns2client/tokens.go new file mode 100644 index 000000000..9e7706b09 --- /dev/null +++ b/vendor/github.com/OpenDNS/vegadns2client/tokens.go @@ -0,0 +1,74 @@ +package vegadns2client + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "strings" + "time" +) + +// Token - struct to hold token information +type Token struct { + Token string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + ExpiresAt time.Time +} + +func (t Token) valid() error { + if time.Now().UTC().After(t.ExpiresAt) { + return errors.New("Token Expired") + } + return nil +} + +func (vega *VegaDNSClient) getBearer() string { + if vega.token.valid() != nil { + vega.getAuthToken() + } + return vega.token.formatBearer() +} + +func (t Token) formatBearer() string { + return fmt.Sprintf("Bearer %s", t.Token) +} + +func (vega *VegaDNSClient) getAuthToken() { + tokenEndpoint := vega.getURL("token") + v := url.Values{} + v.Set("grant_type", "client_credentials") + + req, err := http.NewRequest("POST", tokenEndpoint, strings.NewReader(v.Encode())) + if err != nil { + log.Fatalf("Error forming POST to getAuthToken: %s", err) + } + req.SetBasicAuth(vega.APIKey, vega.APISecret) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + issueTime := time.Now().UTC() + resp, err := vega.client.Do(req) + if err != nil { + log.Fatalf("Error sending POST to getAuthToken: %s", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatalf("Error reading response from POST to getAuthToken: %s", err) + } + if resp.StatusCode != http.StatusOK { + log.Fatalf("Got bad answer from VegaDNS on getAuthToken. Code: %d. Message: %s", resp.StatusCode, string(body)) + } + if err := json.Unmarshal(body, &vega.token); err != nil { + log.Fatalf("Error unmarshalling body of POST to getAuthToken: %s", err) + } + + if vega.token.TokenType != "bearer" { + log.Fatal("We don't support anything except bearer tokens") + } + vega.token.ExpiresAt = issueTime.Add(time.Duration(vega.token.ExpiresIn) * time.Second) +} diff --git a/vendor/github.com/sacloud/libsacloud/LICENSE.txt b/vendor/github.com/sacloud/libsacloud/LICENSE.txt new file mode 100644 index 000000000..67fd81f5c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/LICENSE.txt @@ -0,0 +1,207 @@ + + 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. + +================================================================================ + + Copyright 2015-2018 Kazumichi Yamamoto. + diff --git a/vendor/github.com/sacloud/libsacloud/api/archive.go b/vendor/github.com/sacloud/libsacloud/api/archive.go new file mode 100644 index 000000000..c752adb64 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/archive.go @@ -0,0 +1,344 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "github.com/sacloud/libsacloud/sacloud/ostype" + "strings" + "time" +) + +// ArchiveAPI アーカイブAPI +type ArchiveAPI struct { + *baseAPI + findFuncMapPerOSType map[ostype.ArchiveOSTypes]func() (*sacloud.Archive, error) +} + +var ( + archiveLatestStableCentOSTags = []string{"current-stable", "distro-centos"} + archiveLatestStableCentOS6Tags = []string{"distro-centos", "distro-ver-6.9"} + archiveLatestStableUbuntuTags = []string{"current-stable", "distro-ubuntu"} + archiveLatestStableDebianTags = []string{"current-stable", "distro-debian"} + archiveLatestStableVyOSTags = []string{"current-stable", "distro-vyos"} + archiveLatestStableCoreOSTags = []string{"current-stable", "distro-coreos"} + archiveLatestStableRancherOSTags = []string{"current-stable", "distro-rancheros"} + archiveLatestStableKusanagiTags = []string{"current-stable", "pkg-kusanagi"} + archiveLatestStableSophosUTMTags = []string{"current-stable", "pkg-sophosutm"} + archiveLatestStableFreeBSDTags = []string{"current-stable", "distro-freebsd"} + archiveLatestStableWindows2012Tags = []string{"os-windows", "distro-ver-2012.2"} + archiveLatestStableWindows2012RDSTags = []string{"os-windows", "distro-ver-2012.2", "windows-rds"} + archiveLatestStableWindows2012RDSOfficeTags = []string{"os-windows", "distro-ver-2012.2", "windows-rds", "with-office"} + archiveLatestStableWindows2016Tags = []string{"os-windows", "distro-ver-2016"} + archiveLatestStableWindows2016RDSTags = []string{"os-windows", "distro-ver-2016", "windows-rds"} + archiveLatestStableWindows2016RDSOfficeTags = []string{"os-windows", "distro-ver-2016", "windows-rds", "with-office"} + archiveLatestStableWindows2016SQLServerWeb = []string{"os-windows", "distro-ver-2016", "windows-sqlserver", "sqlserver-2016", "edition-web"} + archiveLatestStableWindows2016SQLServerStandard = []string{"os-windows", "distro-ver-2016", "windows-sqlserver", "sqlserver-2016", "edition-standard"} + archiveLatestStableWindows2016SQLServerStandardAll = []string{"os-windows", "distro-ver-2016", "windows-sqlserver", "sqlserver-2016", "edition-standard", "windows-rds", "with-office"} +) + +// NewArchiveAPI アーカイブAPI作成 +func NewArchiveAPI(client *Client) *ArchiveAPI { + api := &ArchiveAPI{ + baseAPI: &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "archive" + }, + }, + } + + api.findFuncMapPerOSType = map[ostype.ArchiveOSTypes]func() (*sacloud.Archive, error){ + ostype.CentOS: api.FindLatestStableCentOS, + ostype.CentOS6: api.FindLatestStableCentOS6, + ostype.Ubuntu: api.FindLatestStableUbuntu, + ostype.Debian: api.FindLatestStableDebian, + ostype.VyOS: api.FindLatestStableVyOS, + ostype.CoreOS: api.FindLatestStableCoreOS, + ostype.RancherOS: api.FindLatestStableRancherOS, + ostype.Kusanagi: api.FindLatestStableKusanagi, + ostype.SophosUTM: api.FindLatestStableSophosUTM, + ostype.FreeBSD: api.FindLatestStableFreeBSD, + ostype.Windows2012: api.FindLatestStableWindows2012, + ostype.Windows2012RDS: api.FindLatestStableWindows2012RDS, + ostype.Windows2012RDSOffice: api.FindLatestStableWindows2012RDSOffice, + ostype.Windows2016: api.FindLatestStableWindows2016, + ostype.Windows2016RDS: api.FindLatestStableWindows2016RDS, + ostype.Windows2016RDSOffice: api.FindLatestStableWindows2016RDSOffice, + ostype.Windows2016SQLServerWeb: api.FindLatestStableWindows2016SQLServerWeb, + ostype.Windows2016SQLServerStandard: api.FindLatestStableWindows2016SQLServerStandard, + ostype.Windows2016SQLServerStandardAll: api.FindLatestStableWindows2016SQLServerStandardAll, + } + + return api +} + +// OpenFTP FTP接続開始 +func (api *ArchiveAPI) OpenFTP(id int64) (*sacloud.FTPServer, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/ftp", api.getResourceURL(), id) + //body = map[string]bool{"ChangePassword": reset} + res = &sacloud.Response{} + ) + + result, err := api.action(method, uri, nil, res) + if !result || err != nil { + return nil, err + } + + return res.FTPServer, nil +} + +// CloseFTP FTP接続終了 +func (api *ArchiveAPI) CloseFTP(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/ftp", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) + +} + +// SleepWhileCopying コピー終了まで待機 +func (api *ArchiveAPI) SleepWhileCopying(id int64, timeout time.Duration) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *ArchiveAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, 0) + return poll(handler, timeout) +} + +// CanEditDisk ディスクの修正が可能か判定 +func (api *ArchiveAPI) CanEditDisk(id int64) (bool, error) { + + archive, err := api.Read(id) + if err != nil { + return false, err + } + + if archive == nil { + return false, nil + } + + // BundleInfoがあれば編集不可 + if archive.BundleInfo != nil && archive.BundleInfo.HostClass == bundleInfoWindowsHostClass { + // Windows + return false, nil + } + + // SophosUTMであれば編集不可 + if archive.HasTag("pkg-sophosutm") || archive.IsSophosUTM() { + return false, nil + } + + for _, t := range allowDiskEditTags { + if archive.HasTag(t) { + // 対応OSインストール済みディスク + return true, nil + } + } + + // ここまできても判定できないならソースに投げる + if archive.SourceDisk != nil && archive.SourceDisk.Availability != "discontinued" { + return api.client.Disk.CanEditDisk(archive.SourceDisk.ID) + } + if archive.SourceArchive != nil && archive.SourceArchive.Availability != "discontinued" { + return api.client.Archive.CanEditDisk(archive.SourceArchive.ID) + } + return false, nil + +} + +// GetPublicArchiveIDFromAncestors 祖先の中からパブリックアーカイブのIDを検索 +func (api *ArchiveAPI) GetPublicArchiveIDFromAncestors(id int64) (int64, bool) { + + emptyID := int64(0) + + archive, err := api.Read(id) + if err != nil { + return emptyID, false + } + + if archive == nil { + return emptyID, false + } + + // BundleInfoがあれば編集不可 + if archive.BundleInfo != nil && archive.BundleInfo.HostClass == bundleInfoWindowsHostClass { + // Windows + return emptyID, false + } + + // SophosUTMであれば編集不可 + if archive.HasTag("pkg-sophosutm") || archive.IsSophosUTM() { + return emptyID, false + } + + for _, t := range allowDiskEditTags { + if archive.HasTag(t) { + // 対応OSインストール済みディスク + return archive.ID, true + } + } + + // ここまできても判定できないならソースに投げる + if archive.SourceDisk != nil && archive.SourceDisk.Availability != "discontinued" { + return api.client.Disk.GetPublicArchiveIDFromAncestors(archive.SourceDisk.ID) + } + if archive.SourceArchive != nil && archive.SourceArchive.Availability != "discontinued" { + return api.client.Archive.GetPublicArchiveIDFromAncestors(archive.SourceArchive.ID) + } + return emptyID, false + +} + +// FindLatestStableCentOS 安定版最新のCentOSパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableCentOS() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableCentOSTags) +} + +// FindLatestStableCentOS6 安定版最新のCentOS6パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableCentOS6() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableCentOS6Tags) +} + +// FindLatestStableDebian 安定版最新のDebianパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableDebian() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableDebianTags) +} + +// FindLatestStableUbuntu 安定版最新のUbuntuパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableUbuntu() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableUbuntuTags) +} + +// FindLatestStableVyOS 安定版最新のVyOSパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableVyOS() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableVyOSTags) +} + +// FindLatestStableCoreOS 安定版最新のCoreOSパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableCoreOS() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableCoreOSTags) +} + +// FindLatestStableRancherOS 安定版最新のRancherOSパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableRancherOS() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableRancherOSTags) +} + +// FindLatestStableKusanagi 安定版最新のKusanagiパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableKusanagi() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableKusanagiTags) +} + +// FindLatestStableSophosUTM 安定板最新のSophosUTMパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableSophosUTM() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableSophosUTMTags) +} + +// FindLatestStableFreeBSD 安定版最新のFreeBSDパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableFreeBSD() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableFreeBSDTags) +} + +// FindLatestStableWindows2012 安定版最新のWindows2012パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2012() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2012Tags, map[string]interface{}{ + "Name": "Windows Server 2012 R2 Datacenter Edition", + }) +} + +// FindLatestStableWindows2012RDS 安定版最新のWindows2012RDSパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2012RDS() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2012RDSTags, map[string]interface{}{ + "Name": "Windows Server 2012 R2 for RDS", + }) +} + +// FindLatestStableWindows2012RDSOffice 安定版最新のWindows2012RDS(Office)パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2012RDSOffice() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2012RDSOfficeTags, map[string]interface{}{ + "Name": "Windows Server 2012 R2 for RDS(MS Office付)", + }) +} + +// FindLatestStableWindows2016 安定版最新のWindows2016パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2016() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2016Tags, map[string]interface{}{ + "Name": "Windows Server 2016 Datacenter Edition", + }) +} + +// FindLatestStableWindows2016RDS 安定版最新のWindows2016RDSパブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2016RDS() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2016RDSTags, map[string]interface{}{ + "Name": "Windows Server 2016 for RDS", + }) +} + +// FindLatestStableWindows2016RDSOffice 安定版最新のWindows2016RDS(Office)パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2016RDSOffice() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2016RDSOfficeTags, map[string]interface{}{ + "Name": "Windows Server 2016 for RDS(MS Office付)", + }) +} + +// FindLatestStableWindows2016SQLServerWeb 安定版最新のWindows2016 SQLServer(Web) パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2016SQLServerWeb() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2016SQLServerWeb, map[string]interface{}{ + "Name": "Windows Server 2016 for MS SQL 2016(Web)", + }) +} + +// FindLatestStableWindows2016SQLServerStandard 安定版最新のWindows2016 SQLServer(Standard) パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2016SQLServerStandard() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2016SQLServerStandard, map[string]interface{}{ + "Name": "Windows Server 2016 for MS SQL 2016(Standard)", + }) +} + +// FindLatestStableWindows2016SQLServerStandardAll 安定版最新のWindows2016 SQLServer(RDS+Office) パブリックアーカイブを取得 +func (api *ArchiveAPI) FindLatestStableWindows2016SQLServerStandardAll() (*sacloud.Archive, error) { + return api.findByOSTags(archiveLatestStableWindows2016SQLServerStandard, map[string]interface{}{ + "Name": "Windows Server 2016 for MS SQL 2016(Std) with RDS / MS Office", + }) +} + +// FindByOSType 指定のOS種別の安定版最新のパブリックアーカイブを取得 +func (api *ArchiveAPI) FindByOSType(os ostype.ArchiveOSTypes) (*sacloud.Archive, error) { + if f, ok := api.findFuncMapPerOSType[os]; ok { + return f() + } + + return nil, fmt.Errorf("OSType [%s] is invalid", os) +} + +func (api *ArchiveAPI) findByOSTags(tags []string, filterMap ...map[string]interface{}) (*sacloud.Archive, error) { + + api.Reset().WithTags(tags) + + for _, filters := range filterMap { + for key, filter := range filters { + api.FilterMultiBy(key, filter) + } + } + res, err := api.Find() + if err != nil { + return nil, fmt.Errorf("Archive [%s] error : %s", strings.Join(tags, ","), err) + } + + if len(res.Archives) == 0 { + return nil, fmt.Errorf("Archive [%s] Not Found", strings.Join(tags, ",")) + } + + return &res.Archives[0], nil + +} diff --git a/vendor/github.com/sacloud/libsacloud/api/archive_gen.go b/vendor/github.com/sacloud/libsacloud/api/archive_gen.go new file mode 100644 index 000000000..57af0a080 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/archive_gen.go @@ -0,0 +1,251 @@ +package api + +/************************************************ + generated by IDE. for [ArchiveAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件リセット +func (api *ArchiveAPI) Reset() *ArchiveAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ArchiveAPI) Offset(offset int) *ArchiveAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ArchiveAPI) Limit(limit int) *ArchiveAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ArchiveAPI) Include(key string) *ArchiveAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ArchiveAPI) Exclude(key string) *ArchiveAPI { + api.exclude(key) + return api +} + +// FilterBy 任意項目でのフィルタ(部分一致) +func (api *ArchiveAPI) FilterBy(key string, value interface{}) *ArchiveAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ArchiveAPI) FilterMultiBy(key string, value interface{}) *ArchiveAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ArchiveAPI) WithNameLike(name string) *ArchiveAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ArchiveAPI) WithTag(tag string) *ArchiveAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ArchiveAPI) WithTags(tags []string) *ArchiveAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// WithSizeGib アーカイブサイズ条件 +func (api *ArchiveAPI) WithSizeGib(size int) *ArchiveAPI { + api.FilterBy("SizeMB", size*1024) + return api +} + +// WithSharedScope 共有スコープ条件 +func (api *ArchiveAPI) WithSharedScope() *ArchiveAPI { + api.FilterBy("Scope", "shared") + return api +} + +// WithUserScope ユーザースコープ条件 +func (api *ArchiveAPI) WithUserScope() *ArchiveAPI { + api.FilterBy("Scope", "user") + return api +} + +// SortBy 任意項目でのソート指定 +func (api *ArchiveAPI) SortBy(key string, reverse bool) *ArchiveAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ArchiveAPI) SortByName(reverse bool) *ArchiveAPI { + api.sortByName(reverse) + return api +} + +// SortBySize サイズでのソート +func (api *ArchiveAPI) SortBySize(reverse bool) *ArchiveAPI { + api.sortBy("SizeMB", reverse) + return api +} + +/************************************************ + To support Setxxx interfaces for Find() +************************************************/ + +// SetEmpty 検索条件リセット +func (api *ArchiveAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ArchiveAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ArchiveAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ArchiveAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ArchiveAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 任意項目でのフィルタ(部分一致) +func (api *ArchiveAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ArchiveAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ArchiveAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ArchiveAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ArchiveAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// SetSizeGib アーカイブサイズ条件 +func (api *ArchiveAPI) SetSizeGib(size int) { + api.FilterBy("SizeMB", size*1024) +} + +// SetSharedScope 共有スコープ条件 +func (api *ArchiveAPI) SetSharedScope() { + api.FilterBy("Scope", "shared") +} + +// SetUserScope ユーザースコープ条件 +func (api *ArchiveAPI) SetUserScope() { + api.FilterBy("Scope", "user") +} + +// SetSortBy 任意項目でのソート指定 +func (api *ArchiveAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ArchiveAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// SetSortBySize サイズでのソート +func (api *ArchiveAPI) SetSortBySize(reverse bool) { + api.sortBy("SizeMB", reverse) +} + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// Create 新規作成 +func (api *ArchiveAPI) Create(value *sacloud.Archive) (*sacloud.Archive, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *ArchiveAPI) Read(id int64) (*sacloud.Archive, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *ArchiveAPI) Update(id int64, value *sacloud.Archive) (*sacloud.Archive, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *ArchiveAPI) Delete(id int64) (*sacloud.Archive, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +// New 作成用パラメータ作成 +func (api *ArchiveAPI) New() *sacloud.Archive { + return &sacloud.Archive{} +} + +/************************************************ + Inner functions +************************************************/ + +func (api *ArchiveAPI) setStateValue(setFunc func(*sacloud.Request)) *ArchiveAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ArchiveAPI) request(f func(*sacloud.Response) error) (*sacloud.Archive, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Archive, nil +} + +func (api *ArchiveAPI) createRequest(value *sacloud.Archive) *sacloud.Request { + req := &sacloud.Request{} + req.Archive = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/auth_status.go b/vendor/github.com/sacloud/libsacloud/api/auth_status.go new file mode 100644 index 000000000..d9fd2c73d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/auth_status.go @@ -0,0 +1,42 @@ +package api + +import ( + "encoding/json" + "github.com/sacloud/libsacloud/sacloud" +) + +// AuthStatusAPI 認証状態API +type AuthStatusAPI struct { + *baseAPI +} + +// NewAuthStatusAPI 認証状態API作成 +func NewAuthStatusAPI(client *Client) *AuthStatusAPI { + return &AuthStatusAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "auth-status" + }, + }, + } +} + +// Read 読み取り +func (api *AuthStatusAPI) Read() (*sacloud.AuthStatus, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), nil) + if err != nil { + return nil, err + } + var res sacloud.AuthStatus + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +// Find 検索 +func (api *AuthStatusAPI) Find() (*sacloud.AuthStatus, error) { + return api.Read() +} diff --git a/vendor/github.com/sacloud/libsacloud/api/auto_backup.go b/vendor/github.com/sacloud/libsacloud/api/auto_backup.go new file mode 100644 index 000000000..d9944e099 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/auto_backup.go @@ -0,0 +1,116 @@ +package api + +import ( + "encoding/json" + // "strings" + "github.com/sacloud/libsacloud/sacloud" +) + +// SearchAutoBackupResponse 自動バックアップ 検索レスポンス +type SearchAutoBackupResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // CommonServiceAutoBackupItems 自動バックアップ リスト + CommonServiceAutoBackupItems []sacloud.AutoBackup `json:"CommonServiceItems,omitempty"` +} + +type autoBackupRequest struct { + CommonServiceAutoBackupItem *sacloud.AutoBackup `json:"CommonServiceItem,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type autoBackupResponse struct { + *sacloud.ResultFlagValue + *sacloud.AutoBackup `json:"CommonServiceItem,omitempty"` +} + +// AutoBackupAPI 自動バックアップAPI +type AutoBackupAPI struct { + *baseAPI +} + +// NewAutoBackupAPI 自動バックアップAPI作成 +func NewAutoBackupAPI(client *Client) *AutoBackupAPI { + return &AutoBackupAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "commonserviceitem" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Provider.Class", "autobackup") + return res + }, + }, + } +} + +// Find 検索 +func (api *AutoBackupAPI) Find() (*SearchAutoBackupResponse, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchAutoBackupResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *AutoBackupAPI) request(f func(*autoBackupResponse) error) (*sacloud.AutoBackup, error) { + res := &autoBackupResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.AutoBackup, nil +} + +func (api *AutoBackupAPI) createRequest(value *sacloud.AutoBackup) *autoBackupResponse { + return &autoBackupResponse{AutoBackup: value} +} + +// New 新規作成用パラメーター作成 +func (api *AutoBackupAPI) New(name string, diskID int64) *sacloud.AutoBackup { + return sacloud.CreateNewAutoBackup(name, diskID) +} + +// Create 新規作成 +func (api *AutoBackupAPI) Create(value *sacloud.AutoBackup) (*sacloud.AutoBackup, error) { + return api.request(func(res *autoBackupResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *AutoBackupAPI) Read(id int64) (*sacloud.AutoBackup, error) { + return api.request(func(res *autoBackupResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *AutoBackupAPI) Update(id int64, value *sacloud.AutoBackup) (*sacloud.AutoBackup, error) { + return api.request(func(res *autoBackupResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *AutoBackupAPI) Delete(id int64) (*sacloud.AutoBackup, error) { + return api.request(func(res *autoBackupResponse) error { + return api.delete(id, nil, res) + }) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/auto_backup_gen.go b/vendor/github.com/sacloud/libsacloud/api/auto_backup_gen.go new file mode 100644 index 000000000..4d6f8ac1d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/auto_backup_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [AutoBackupAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件リセット +func (api *AutoBackupAPI) Reset() *AutoBackupAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *AutoBackupAPI) Offset(offset int) *AutoBackupAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *AutoBackupAPI) Limit(limit int) *AutoBackupAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *AutoBackupAPI) Include(key string) *AutoBackupAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *AutoBackupAPI) Exclude(key string) *AutoBackupAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルタ +func (api *AutoBackupAPI) FilterBy(key string, value interface{}) *AutoBackupAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *AutoBackupAPI) FilterMultiBy(key string, value interface{}) *AutoBackupAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *AutoBackupAPI) WithNameLike(name string) *AutoBackupAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *AutoBackupAPI) WithTag(tag string) *AutoBackupAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *AutoBackupAPI) WithTags(tags []string) *AutoBackupAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *AutoBackupAPI) WithSizeGib(size int) *AutoBackupAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *AutoBackupAPI) WithSharedScope() *AutoBackupAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *AutoBackupAPI) WithUserScope() *AutoBackupAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *AutoBackupAPI) SortBy(key string, reverse bool) *AutoBackupAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名前でのソート +func (api *AutoBackupAPI) SortByName(reverse bool) *AutoBackupAPI { + api.sortByName(reverse) + return api +} + +// func (api *AutoBackupAPI) SortBySize(reverse bool) *AutoBackupAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件リセット +func (api *AutoBackupAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *AutoBackupAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *AutoBackupAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *AutoBackupAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *AutoBackupAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルタ +func (api *AutoBackupAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *AutoBackupAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *AutoBackupAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *AutoBackupAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *AutoBackupAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *AutoBackupAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *AutoBackupAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *AutoBackupAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *AutoBackupAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名前でのソート +func (api *AutoBackupAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *AutoBackupAPI) SortBySize(reverse bool) *AutoBackupAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *AutoBackupAPI) New() *sacloud.AutoBackup { +// return &sacloud.AutoBackup{} +// } + +// func (api *AutoBackupAPI) Create(value *sacloud.AutoBackup) (*sacloud.AutoBackup, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *AutoBackupAPI) Read(id string) (*sacloud.AutoBackup, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *AutoBackupAPI) Update(id string, value *sacloud.AutoBackup) (*sacloud.AutoBackup, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *AutoBackupAPI) Delete(id string) (*sacloud.AutoBackup, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *AutoBackupAPI) setStateValue(setFunc func(*sacloud.Request)) *AutoBackupAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *AutoBackupAPI) request(f func(*sacloud.Response) error) (*sacloud.AutoBackup, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.AutoBackup, nil +//} +// +//func (api *AutoBackupAPI) createRequest(value *sacloud.AutoBackup) *sacloud.Request { +// req := &sacloud.Request{} +// req.AutoBackup = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/base_api.go b/vendor/github.com/sacloud/libsacloud/api/base_api.go new file mode 100644 index 000000000..a071869ce --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/base_api.go @@ -0,0 +1,277 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "net/url" +) + +type baseAPI struct { + client *Client + FuncGetResourceURL func() string + FuncBaseSearchCondition func() *sacloud.Request + state *sacloud.Request + apiRootSuffix string +} + +var ( + sakuraCloudAPIRootSuffix = "api/cloud/1.1" + sakuraBillingAPIRootSuffix = "api/system/1.0" + sakuraWebAccelAPIRootSuffix = "api/webaccel/1.0" +) + +func (api *baseAPI) getResourceURL() string { + + suffix := api.apiRootSuffix + //デフォルト : クラウドAPI + if suffix == "" { + suffix = sakuraCloudAPIRootSuffix + } + + url := "" + if api.FuncGetResourceURL != nil { + url = api.FuncGetResourceURL() + } + + if suffix == "" { + return url + } + if url == "" { + return suffix + } + + return fmt.Sprintf("%s/%s", suffix, url) +} + +func (api *baseAPI) getSearchState() *sacloud.Request { + if api.state == nil { + api.reset() + } + return api.state +} +func (api *baseAPI) sortBy(key string, reverse bool) *baseAPI { + return api.setStateValue(func(state *sacloud.Request) { + if state.Sort == nil { + state.Sort = []string{} + } + + col := key + if reverse { + col = "-" + col + } + state.Sort = append(state.Sort, col) + + }) + +} + +func (api *baseAPI) reset() *baseAPI { + if api.FuncBaseSearchCondition == nil { + api.state = &sacloud.Request{} + } else { + api.state = api.FuncBaseSearchCondition() + } + return api +} + +func (api *baseAPI) setStateValue(setFunc func(*sacloud.Request)) *baseAPI { + state := api.getSearchState() + setFunc(state) + return api + +} + +func (api *baseAPI) offset(offset int) *baseAPI { + return api.setStateValue(func(state *sacloud.Request) { + state.From = offset + }) +} + +func (api *baseAPI) limit(limit int) *baseAPI { + return api.setStateValue(func(state *sacloud.Request) { + state.Count = limit + }) +} + +func (api *baseAPI) include(key string) *baseAPI { + return api.setStateValue(func(state *sacloud.Request) { + if state.Include == nil { + state.Include = []string{} + } + state.Include = append(state.Include, key) + }) +} + +func (api *baseAPI) exclude(key string) *baseAPI { + return api.setStateValue(func(state *sacloud.Request) { + if state.Exclude == nil { + state.Exclude = []string{} + } + state.Exclude = append(state.Exclude, key) + }) +} + +func (api *baseAPI) filterBy(key string, value interface{}, multiple bool) *baseAPI { + return api.setStateValue(func(state *sacloud.Request) { + + //HACK さくらのクラウド側でqueryStringでの+エスケープに対応していないため、 + // %20にエスケープされるurl.Pathを利用する。 + // http://qiita.com/shibukawa/items/c0730092371c0e243f62 + if strValue, ok := value.(string); ok { + u := &url.URL{Path: strValue} + value = u.String() + } + + if state.Filter == nil { + state.Filter = map[string]interface{}{} + } + if multiple { + if state.Filter[key] == nil { + state.Filter[key] = []interface{}{} + } + + state.Filter[key] = append(state.Filter[key].([]interface{}), value) + } else { + // どちらもstring型の場合はスペース区切りで繋げる + if f, ok := state.Filter[key]; ok { + if s, ok := f.(string); ok && s != "" { + if v, ok := value.(string); ok { + state.Filter[key] = fmt.Sprintf("%s %s", s, v) + return + } + } + } + state.Filter[key] = value + + } + }) +} + +func (api *baseAPI) withNameLike(name string) *baseAPI { + return api.filterBy("Name", name, false) +} + +func (api *baseAPI) withTag(tag string) *baseAPI { + return api.filterBy("Tags.Name", tag, false) +} + +func (api *baseAPI) withTags(tags []string) *baseAPI { + return api.filterBy("Tags.Name", tags, false) +} + +func (api *baseAPI) sortByName(reverse bool) *baseAPI { + return api.sortBy("Name", reverse) +} + +func (api *baseAPI) Find() (*sacloud.SearchResponse, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res sacloud.SearchResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *baseAPI) request(method string, uri string, body interface{}, res interface{}) error { + data, err := api.client.newRequest(method, uri, body) + if err != nil { + return err + } + + if res != nil { + if err := json.Unmarshal(data, &res); err != nil { + return err + } + } + return nil +} + +func (api *baseAPI) create(body interface{}, res interface{}) error { + var ( + method = "POST" + uri = api.getResourceURL() + ) + + return api.request(method, uri, body, res) +} + +func (api *baseAPI) read(id int64, body interface{}, res interface{}) error { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d", api.getResourceURL(), id) + ) + + return api.request(method, uri, body, res) +} + +func (api *baseAPI) update(id int64, body interface{}, res interface{}) error { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d", api.getResourceURL(), id) + ) + return api.request(method, uri, body, res) +} + +func (api *baseAPI) delete(id int64, body interface{}, res interface{}) error { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d", api.getResourceURL(), id) + ) + return api.request(method, uri, body, res) +} + +func (api *baseAPI) modify(method string, uri string, body interface{}) (bool, error) { + res := &sacloud.ResultFlagValue{} + err := api.request(method, uri, body, res) + if err != nil { + return false, err + } + return res.IsOk, nil +} + +func (api *baseAPI) action(method string, uri string, body interface{}, res interface{}) (bool, error) { + err := api.request(method, uri, body, res) + if err != nil { + return false, err + } + return true, nil +} + +func (api *baseAPI) monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/monitor", api.getResourceURL(), id) + ) + res := &sacloud.ResourceMonitorResponse{} + err := api.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Data, nil +} + +func (api *baseAPI) applianceMonitorBy(id int64, target string, nicIndex int, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/%s/%d/monitor", api.getResourceURL(), id, target, nicIndex) + ) + if nicIndex == 0 { + uri = fmt.Sprintf("%s/%d/%s/monitor", api.getResourceURL(), id, target) + } + + res := &sacloud.ResourceMonitorResponse{} + err := api.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Data, nil +} + +func (api *baseAPI) NewResourceMonitorRequest() *sacloud.ResourceMonitorRequest { + return &sacloud.ResourceMonitorRequest{} +} diff --git a/vendor/github.com/sacloud/libsacloud/api/bill.go b/vendor/github.com/sacloud/libsacloud/api/bill.go new file mode 100644 index 000000000..2caba0b25 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/bill.go @@ -0,0 +1,185 @@ +package api + +import ( + "encoding/csv" + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "io" + "strings" + "time" +) + +// BillAPI 請求情報API +type BillAPI struct { + *baseAPI +} + +// NewBillAPI 請求情報API作成 +func NewBillAPI(client *Client) *BillAPI { + return &BillAPI{ + &baseAPI{ + client: client, + apiRootSuffix: sakuraBillingAPIRootSuffix, + FuncGetResourceURL: func() string { + return "bill" + }, + }, + } +} + +// BillResponse 請求情報レスポンス +type BillResponse struct { + *sacloud.ResultFlagValue + // Count 件数 + Count int `json:",omitempty"` + // ResponsedAt 応答日時 + ResponsedAt *time.Time `json:",omitempty"` + // Bills 請求情報 リスト + Bills []*sacloud.Bill +} + +// BillDetailResponse 請求明細レスポンス +type BillDetailResponse struct { + *sacloud.ResultFlagValue + // Count 件数 + Count int `json:",omitempty"` + // ResponsedAt 応答日時 + ResponsedAt *time.Time `json:",omitempty"` + // BillDetails 請求明細 リスト + BillDetails []*sacloud.BillDetail +} + +// BillDetailCSVResponse 請求明細CSVレスポンス +type BillDetailCSVResponse struct { + *sacloud.ResultFlagValue + // Count 件数 + Count int `json:",omitempty"` + // ResponsedAt 応答日時 + ResponsedAt *time.Time `json:",omitempty"` + // Filename ファイル名 + Filename string `json:",omitempty"` + // RawBody ボディ(未加工) + RawBody string `json:"Body,omitempty"` + // HeaderRow ヘッダ行 + HeaderRow []string + // BodyRows ボディ(各行/各列での配列) + BodyRows [][]string +} + +func (res *BillDetailCSVResponse) buildCSVBody() { + + if res == nil || res.RawBody == "" { + return + } + + //CSV分割(先頭行/それ以降)、 + reader := csv.NewReader(strings.NewReader(res.RawBody)) + reader.LazyQuotes = true + + isFirst := true + res.BodyRows = [][]string{} + for { + record, err := reader.Read() + if err == io.EOF { + break + } else if err != nil { + panic(err) + } + + if isFirst { + res.HeaderRow = record + isFirst = false + } else { + res.BodyRows = append(res.BodyRows, record) + } + } +} + +// ByContract アカウントIDごとの請求取得 +func (api *BillAPI) ByContract(accountID int64) (*BillResponse, error) { + + uri := fmt.Sprintf("%s/by-contract/%d", api.getResourceURL(), accountID) + return api.getContract(uri) +} + +// ByContractYear 年指定での請求取得 +func (api *BillAPI) ByContractYear(accountID int64, year int) (*BillResponse, error) { + uri := fmt.Sprintf("%s/by-contract/%d/%d", api.getResourceURL(), accountID, year) + return api.getContract(uri) +} + +// ByContractYearMonth 年月指定での請求指定 +func (api *BillAPI) ByContractYearMonth(accountID int64, year int, month int) (*BillResponse, error) { + uri := fmt.Sprintf("%s/by-contract/%d/%d/%d", api.getResourceURL(), accountID, year, month) + return api.getContract(uri) +} + +// Read 読み取り +func (api *BillAPI) Read(billNo int64) (*BillResponse, error) { + uri := fmt.Sprintf("%s/id/%d/", api.getResourceURL(), billNo) + return api.getContract(uri) + +} + +func (api *BillAPI) getContract(uri string) (*BillResponse, error) { + + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + var res BillResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil + +} + +// GetDetail 請求明細取得 +func (api *BillAPI) GetDetail(memberCD string, billNo int64) (*BillDetailResponse, error) { + + oldFunc := api.FuncGetResourceURL + defer func() { api.FuncGetResourceURL = oldFunc }() + api.FuncGetResourceURL = func() string { + return "billdetail" + } + + uri := fmt.Sprintf("%s/%s/%d", api.getResourceURL(), memberCD, billNo) + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + var res BillDetailResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil + +} + +// GetDetailCSV 請求明細CSV取得 +func (api *BillAPI) GetDetailCSV(memberCD string, billNo int64) (*BillDetailCSVResponse, error) { + + oldFunc := api.FuncGetResourceURL + defer func() { api.FuncGetResourceURL = oldFunc }() + api.FuncGetResourceURL = func() string { + return "billdetail" + } + + uri := fmt.Sprintf("%s/%s/%d/csv", api.getResourceURL(), memberCD, billNo) + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + var res BillDetailCSVResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + + // build HeaderRow and BodyRows from RawBody + res.buildCSVBody() + + return &res, nil + +} diff --git a/vendor/github.com/sacloud/libsacloud/api/bridge.go b/vendor/github.com/sacloud/libsacloud/api/bridge.go new file mode 100644 index 000000000..d5ab003a9 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/bridge.go @@ -0,0 +1,18 @@ +package api + +// BridgeAPI ブリッジAPI +type BridgeAPI struct { + *baseAPI +} + +// NewBridgeAPI ブリッジAPI作成 +func NewBridgeAPI(client *Client) *BridgeAPI { + return &BridgeAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "bridge" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/bridge_gen.go b/vendor/github.com/sacloud/libsacloud/api/bridge_gen.go new file mode 100644 index 000000000..e4a0b5ba3 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/bridge_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [BridgeAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件リセット +func (api *BridgeAPI) Reset() *BridgeAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *BridgeAPI) Offset(offset int) *BridgeAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *BridgeAPI) Limit(limit int) *BridgeAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *BridgeAPI) Include(key string) *BridgeAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *BridgeAPI) Exclude(key string) *BridgeAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *BridgeAPI) FilterBy(key string, value interface{}) *BridgeAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *BridgeAPI) FilterMultiBy(key string, value interface{}) *BridgeAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *BridgeAPI) WithNameLike(name string) *BridgeAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *BridgeAPI) WithTag(tag string) *BridgeAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *BridgeAPI) WithTags(tags []string) *BridgeAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *BridgeAPI) WithSizeGib(size int) *BridgeAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *BridgeAPI) WithSharedScope() *BridgeAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *BridgeAPI) WithUserScope() *BridgeAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *BridgeAPI) SortBy(key string, reverse bool) *BridgeAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *BridgeAPI) SortByName(reverse bool) *BridgeAPI { + api.sortByName(reverse) + return api +} + +// func (api *BridgeAPI) SortBySize(reverse bool) *BridgeAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件リセット +func (api *BridgeAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *BridgeAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *BridgeAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *BridgeAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *BridgeAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *BridgeAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *BridgeAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *BridgeAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *BridgeAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *BridgeAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *BridgeAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *BridgeAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *BridgeAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *BridgeAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *BridgeAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *BridgeAPI) SortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *BridgeAPI) New() *sacloud.Bridge { + return &sacloud.Bridge{} +} + +// Create 新規作成 +func (api *BridgeAPI) Create(value *sacloud.Bridge) (*sacloud.Bridge, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *BridgeAPI) Read(id int64) (*sacloud.Bridge, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *BridgeAPI) Update(id int64, value *sacloud.Bridge) (*sacloud.Bridge, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *BridgeAPI) Delete(id int64) (*sacloud.Bridge, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *BridgeAPI) setStateValue(setFunc func(*sacloud.Request)) *BridgeAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *BridgeAPI) request(f func(*sacloud.Response) error) (*sacloud.Bridge, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Bridge, nil +} + +func (api *BridgeAPI) createRequest(value *sacloud.Bridge) *sacloud.Request { + req := &sacloud.Request{} + req.Bridge = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/cdrom.go b/vendor/github.com/sacloud/libsacloud/api/cdrom.go new file mode 100644 index 000000000..5bda66b97 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/cdrom.go @@ -0,0 +1,80 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +// CDROMAPI ISOイメージAPI +type CDROMAPI struct { + *baseAPI +} + +// NewCDROMAPI ISOイメージAPI新規作成 +func NewCDROMAPI(client *Client) *CDROMAPI { + return &CDROMAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "cdrom" + }, + }, + } +} + +// Create 新規作成 +func (api *CDROMAPI) Create(value *sacloud.CDROM) (*sacloud.CDROM, *sacloud.FTPServer, error) { + f := func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + } + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, nil, err + } + return res.CDROM, res.FTPServer, nil +} + +// OpenFTP FTP接続開始 +func (api *CDROMAPI) OpenFTP(id int64, reset bool) (*sacloud.FTPServer, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/ftp", api.getResourceURL(), id) + body = map[string]bool{"ChangePassword": reset} + res = &sacloud.Response{} + ) + + result, err := api.action(method, uri, body, res) + if !result || err != nil { + return nil, err + } + + return res.FTPServer, nil +} + +// CloseFTP FTP接続終了 +func (api *CDROMAPI) CloseFTP(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/ftp", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) + +} + +// SleepWhileCopying コピー終了まで待機 +func (api *CDROMAPI) SleepWhileCopying(id int64, timeout time.Duration) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *CDROMAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, 0) + return poll(handler, timeout) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/cdrom_gen.go b/vendor/github.com/sacloud/libsacloud/api/cdrom_gen.go new file mode 100644 index 000000000..05bc4d5ba --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/cdrom_gen.go @@ -0,0 +1,250 @@ +package api + +/************************************************ + generated by IDE. for [CDROMAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件リセット +func (api *CDROMAPI) Reset() *CDROMAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *CDROMAPI) Offset(offset int) *CDROMAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *CDROMAPI) Limit(limit int) *CDROMAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *CDROMAPI) Include(key string) *CDROMAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *CDROMAPI) Exclude(key string) *CDROMAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *CDROMAPI) FilterBy(key string, value interface{}) *CDROMAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *CDROMAPI) FilterMultiBy(key string, value interface{}) *CDROMAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *CDROMAPI) WithNameLike(name string) *CDROMAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *CDROMAPI) WithTag(tag string) *CDROMAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *CDROMAPI) WithTags(tags []string) *CDROMAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// WithSizeGib サイズ条件 +func (api *CDROMAPI) WithSizeGib(size int) *CDROMAPI { + api.FilterBy("SizeMB", size*1024) + return api +} + +// WithSharedScope 公開スコープ条件 +func (api *CDROMAPI) WithSharedScope() *CDROMAPI { + api.FilterBy("Scope", "shared") + return api +} + +// WithUserScope ユーザースコープ条件 +func (api *CDROMAPI) WithUserScope() *CDROMAPI { + api.FilterBy("Scope", "user") + return api +} + +// SortBy 指定キーでのソート +func (api *CDROMAPI) SortBy(key string, reverse bool) *CDROMAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *CDROMAPI) SortByName(reverse bool) *CDROMAPI { + api.sortByName(reverse) + return api +} + +// SortBySize サイズでのソート +func (api *CDROMAPI) SortBySize(reverse bool) *CDROMAPI { + api.sortBy("SizeMB", reverse) + return api +} + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件リセット +func (api *CDROMAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *CDROMAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *CDROMAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *CDROMAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *CDROMAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *CDROMAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *CDROMAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *CDROMAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *CDROMAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *CDROMAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// SetSizeGib サイズ条件 +func (api *CDROMAPI) SetSizeGib(size int) { + api.FilterBy("SizeMB", size*1024) +} + +// SetSharedScope 公開スコープ条件 +func (api *CDROMAPI) SetSharedScope() { + api.FilterBy("Scope", "shared") +} + +// SetUserScope ユーザースコープ条件 +func (api *CDROMAPI) SetUserScope() { + api.FilterBy("Scope", "user") +} + +// SetSortBy 指定キーでのソート +func (api *CDROMAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *CDROMAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// SetSortBySize サイズでのソート +func (api *CDROMAPI) SetSortBySize(reverse bool) { + api.sortBy("SizeMB", reverse) +} + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメータ作成 +func (api *CDROMAPI) New() *sacloud.CDROM { + return &sacloud.CDROM{} +} + +//func (api *CDROMAPI) Create(value *sacloud.CDROM) (*sacloud.CDROM, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +//} + +// Read 読み取り +func (api *CDROMAPI) Read(id int64) (*sacloud.CDROM, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *CDROMAPI) Update(id int64, value *sacloud.CDROM) (*sacloud.CDROM, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *CDROMAPI) Delete(id int64) (*sacloud.CDROM, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *CDROMAPI) setStateValue(setFunc func(*sacloud.Request)) *CDROMAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *CDROMAPI) request(f func(*sacloud.Response) error) (*sacloud.CDROM, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.CDROM, nil +} + +func (api *CDROMAPI) createRequest(value *sacloud.CDROM) *sacloud.Request { + req := &sacloud.Request{} + req.CDROM = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/client.go b/vendor/github.com/sacloud/libsacloud/api/client.go new file mode 100644 index 000000000..f3ca5a9af --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/client.go @@ -0,0 +1,608 @@ +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud" + "github.com/sacloud/libsacloud/sacloud" + "io" + "io/ioutil" + "log" + "net/http" + "strings" + "time" +) + +var ( + // SakuraCloudAPIRoot APIリクエスト送信先ルートURL(末尾にスラッシュを含まない) + SakuraCloudAPIRoot = "https://secure.sakura.ad.jp/cloud/zone" +) + +// Client APIクライアント +type Client struct { + // AccessToken アクセストークン + AccessToken string + // AccessTokenSecret アクセストークンシークレット + AccessTokenSecret string + // Zone 対象ゾーン + Zone string + *API + // TraceMode トレースモード + TraceMode bool + // DefaultTimeoutDuration デフォルトタイムアウト間隔 + DefaultTimeoutDuration time.Duration + // ユーザーエージェント + UserAgent string + // Accept-Language + AcceptLanguage string + // リクエストパラメーター トレーサー + RequestTracer io.Writer + // レスポンス トレーサー + ResponseTracer io.Writer + // 503エラー時のリトライ回数 + RetryMax int + // 503エラー時のリトライ待ち時間 + RetryInterval time.Duration +} + +// NewClient APIクライアント作成 +func NewClient(token, tokenSecret, zone string) *Client { + c := &Client{ + AccessToken: token, + AccessTokenSecret: tokenSecret, + Zone: zone, + TraceMode: false, + DefaultTimeoutDuration: 20 * time.Minute, + UserAgent: fmt.Sprintf("libsacloud/%s", libsacloud.Version), + AcceptLanguage: "", + RetryMax: 0, + RetryInterval: 5 * time.Second, + } + c.API = newAPI(c) + return c +} + +// Clone APIクライアント クローン作成 +func (c *Client) Clone() *Client { + n := &Client{ + AccessToken: c.AccessToken, + AccessTokenSecret: c.AccessTokenSecret, + Zone: c.Zone, + TraceMode: c.TraceMode, + DefaultTimeoutDuration: c.DefaultTimeoutDuration, + UserAgent: c.UserAgent, + AcceptLanguage: c.AcceptLanguage, + RetryMax: c.RetryMax, + RetryInterval: c.RetryInterval, + } + n.API = newAPI(n) + return n +} + +func (c *Client) getEndpoint() string { + return fmt.Sprintf("%s/%s", SakuraCloudAPIRoot, c.Zone) +} + +func (c *Client) isOkStatus(code int) bool { + codes := map[int]bool{ + 200: true, + 201: true, + 202: true, + 204: true, + 305: false, + 400: false, + 401: false, + 403: false, + 404: false, + 405: false, + 406: false, + 408: false, + 409: false, + 411: false, + 413: false, + 415: false, + 500: false, + 503: false, + } + return codes[code] +} + +func (c *Client) newRequest(method, uri string, body interface{}) ([]byte, error) { + var ( + client = &retryableHTTPClient{ + retryMax: c.RetryMax, + retryInterval: c.RetryInterval, + } + err error + req *request + ) + var url = uri + if !strings.HasPrefix(url, "https://") { + url = fmt.Sprintf("%s/%s", c.getEndpoint(), uri) + } + + if body != nil { + var bodyJSON []byte + bodyJSON, err = json.Marshal(body) + if err != nil { + return nil, err + } + if method == "GET" { + url = fmt.Sprintf("%s?%s", url, bytes.NewBuffer(bodyJSON)) + req, err = newRequest(method, url, nil) + } else { + req, err = newRequest(method, url, bytes.NewReader(bodyJSON)) + } + b, _ := json.MarshalIndent(body, "", "\t") + if c.TraceMode { + log.Printf("[libsacloud:Client#request] method : %#v , url : %s , \nbody : %s", method, url, b) + } + if c.RequestTracer != nil { + c.RequestTracer.Write(b) + } + } else { + req, err = newRequest(method, url, nil) + if c.TraceMode { + log.Printf("[libsacloud:Client#request] method : %#v , url : %s ", method, url) + } + } + + if err != nil { + return nil, fmt.Errorf("Error with request: %v - %q", url, err) + } + + req.SetBasicAuth(c.AccessToken, c.AccessTokenSecret) + req.Header.Add("X-Sakura-Bigint-As-Int", "1") //Use BigInt on resource ids. + //if c.TraceMode { + // req.Header.Add("X-Sakura-API-Beautify", "1") // format response-JSON + //} + req.Header.Add("User-Agent", c.UserAgent) + if c.AcceptLanguage != "" { + req.Header.Add("Accept-Language", c.AcceptLanguage) + } + req.Method = method + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + data, err := ioutil.ReadAll(resp.Body) + + v := &map[string]interface{}{} + json.Unmarshal(data, v) + b, _ := json.MarshalIndent(v, "", "\t") + if c.ResponseTracer != nil { + c.ResponseTracer.Write(b) + } + + if c.TraceMode { + log.Printf("[libsacloud:Client#response] : %s", b) + } + if !c.isOkStatus(resp.StatusCode) { + + errResponse := &sacloud.ResultErrorValue{} + err := json.Unmarshal(data, errResponse) + + if err != nil { + return nil, fmt.Errorf("Error in response: %s", string(data)) + } + return nil, NewError(resp.StatusCode, errResponse) + + } + if err != nil { + return nil, err + } + + return data, nil +} + +type lenReader interface { + Len() int +} + +type request struct { + // body is a seekable reader over the request body payload. This is + // used to rewind the request data in between retries. + body io.ReadSeeker + + // Embed an HTTP request directly. This makes a *Request act exactly + // like an *http.Request so that all meta methods are supported. + *http.Request +} + +func newRequest(method, url string, body io.ReadSeeker) (*request, error) { + var rcBody io.ReadCloser + if body != nil { + rcBody = ioutil.NopCloser(body) + } + + httpReq, err := http.NewRequest(method, url, rcBody) + if err != nil { + return nil, err + } + + if lr, ok := body.(lenReader); ok { + httpReq.ContentLength = int64(lr.Len()) + } + + return &request{body, httpReq}, nil +} + +type retryableHTTPClient struct { + http.Client + retryInterval time.Duration + retryMax int +} + +func (c *retryableHTTPClient) Do(req *request) (*http.Response, error) { + for i := 0; ; i++ { + + if req.body != nil { + if _, err := req.body.Seek(0, 0); err != nil { + return nil, fmt.Errorf("failed to seek body: %v", err) + } + } + + res, err := c.Client.Do(req.Request) + if res != nil && res.StatusCode != 503 { + return res, err + } + if res != nil && res.Body != nil { + res.Body.Close() + } + + if err != nil { + return res, err + } + + remain := c.retryMax - i + if remain == 0 { + break + } + time.Sleep(c.retryInterval) + } + + return nil, fmt.Errorf("%s %s giving up after %d attempts", + req.Method, req.URL, c.retryMax+1) +} + +// API libsacloudでサポートしているAPI群 +type API struct { + AuthStatus *AuthStatusAPI // 認証状態API + AutoBackup *AutoBackupAPI // 自動バックアップAPI + Archive *ArchiveAPI // アーカイブAPI + Bill *BillAPI // 請求情報API + Bridge *BridgeAPI // ブリッジAPi + CDROM *CDROMAPI // ISOイメージAPI + Database *DatabaseAPI // データベースAPI + Disk *DiskAPI // ディスクAPI + DNS *DNSAPI // DNS API + Facility *FacilityAPI // ファシリティAPI + GSLB *GSLBAPI // GSLB API + Icon *IconAPI // アイコンAPI + Interface *InterfaceAPI // インターフェースAPI + Internet *InternetAPI // ルーターAPI + IPAddress *IPAddressAPI // IPアドレスAPI + IPv6Addr *IPv6AddrAPI // IPv6アドレスAPI + IPv6Net *IPv6NetAPI // IPv6ネットワークAPI + License *LicenseAPI // ライセンスAPI + LoadBalancer *LoadBalancerAPI // ロードバランサーAPI + MobileGateway *MobileGatewayAPI // モバイルゲートウェイAPI + NewsFeed *NewsFeedAPI // フィード(障害/メンテナンス情報)API + NFS *NFSAPI // NFS API + Note *NoteAPI // スタートアップスクリプトAPI + PacketFilter *PacketFilterAPI // パケットフィルタAPI + PrivateHost *PrivateHostAPI // 専有ホストAPI + Product *ProductAPI // 製品情報API + Server *ServerAPI // サーバーAPI + SIM *SIMAPI // SIM API + SimpleMonitor *SimpleMonitorAPI // シンプル監視API + SSHKey *SSHKeyAPI // 公開鍵API + Subnet *SubnetAPI // IPv4ネットワークAPI + Switch *SwitchAPI // スイッチAPI + VPCRouter *VPCRouterAPI // VPCルーターAPI + WebAccel *WebAccelAPI // ウェブアクセラレータAPI +} + +// GetAuthStatusAPI 認証状態API取得 +func (api *API) GetAuthStatusAPI() *AuthStatusAPI { + return api.AuthStatus +} + +// GetAutoBackupAPI 自動バックアップAPI取得 +func (api *API) GetAutoBackupAPI() *AutoBackupAPI { + return api.AutoBackup +} + +// GetArchiveAPI アーカイブAPI取得 +func (api *API) GetArchiveAPI() *ArchiveAPI { + return api.Archive +} + +// GetBillAPI 請求情報API取得 +func (api *API) GetBillAPI() *BillAPI { + return api.Bill +} + +// GetBridgeAPI ブリッジAPI取得 +func (api *API) GetBridgeAPI() *BridgeAPI { + return api.Bridge +} + +// GetCDROMAPI ISOイメージAPI取得 +func (api *API) GetCDROMAPI() *CDROMAPI { + return api.CDROM +} + +// GetDatabaseAPI データベースAPI取得 +func (api *API) GetDatabaseAPI() *DatabaseAPI { + return api.Database +} + +// GetDiskAPI ディスクAPI取得 +func (api *API) GetDiskAPI() *DiskAPI { + return api.Disk +} + +// GetDNSAPI DNSAPI取得 +func (api *API) GetDNSAPI() *DNSAPI { + return api.DNS +} + +// GetRegionAPI リージョンAPI取得 +func (api *API) GetRegionAPI() *RegionAPI { + return api.Facility.GetRegionAPI() +} + +// GetZoneAPI ゾーンAPI取得 +func (api *API) GetZoneAPI() *ZoneAPI { + return api.Facility.GetZoneAPI() +} + +// GetGSLBAPI GSLB API取得 +func (api *API) GetGSLBAPI() *GSLBAPI { + return api.GSLB +} + +// GetIconAPI アイコンAPI取得 +func (api *API) GetIconAPI() *IconAPI { + return api.Icon +} + +// GetInterfaceAPI インターフェースAPI取得 +func (api *API) GetInterfaceAPI() *InterfaceAPI { + return api.Interface +} + +// GetInternetAPI ルーターAPI取得 +func (api *API) GetInternetAPI() *InternetAPI { + return api.Internet +} + +// GetIPAddressAPI IPアドレスAPI取得 +func (api *API) GetIPAddressAPI() *IPAddressAPI { + return api.IPAddress +} + +// GetIPv6AddrAPI IPv6アドレスAPI取得 +func (api *API) GetIPv6AddrAPI() *IPv6AddrAPI { + return api.IPv6Addr +} + +// GetIPv6NetAPI IPv6ネットワークAPI取得 +func (api *API) GetIPv6NetAPI() *IPv6NetAPI { + return api.IPv6Net +} + +// GetLicenseAPI ライセンスAPI取得 +func (api *API) GetLicenseAPI() *LicenseAPI { + return api.License +} + +// GetLoadBalancerAPI ロードバランサーAPI取得 +func (api *API) GetLoadBalancerAPI() *LoadBalancerAPI { + return api.LoadBalancer +} + +// GetMobileGatewayAPI モバイルゲートウェイAPI取得 +func (api *API) GetMobileGatewayAPI() *MobileGatewayAPI { + return api.MobileGateway +} + +// GetNewsFeedAPI フィード(障害/メンテナンス情報)API取得 +func (api *API) GetNewsFeedAPI() *NewsFeedAPI { + return api.NewsFeed +} + +// GetNFSAPI NFS API取得 +func (api *API) GetNFSAPI() *NFSAPI { + return api.NFS +} + +// GetNoteAPI スタートアップAPI取得 +func (api *API) GetNoteAPI() *NoteAPI { + return api.Note +} + +// GetPacketFilterAPI パケットフィルタAPI取得 +func (api *API) GetPacketFilterAPI() *PacketFilterAPI { + return api.PacketFilter +} + +// GetPrivateHostAPI 専有ホストAPI取得 +func (api *API) GetPrivateHostAPI() *PrivateHostAPI { + return api.PrivateHost +} + +// GetProductServerAPI サーバープランAPI取得 +func (api *API) GetProductServerAPI() *ProductServerAPI { + return api.Product.GetProductServerAPI() +} + +// GetProductLicenseAPI ライセンスプランAPI取得 +func (api *API) GetProductLicenseAPI() *ProductLicenseAPI { + return api.Product.GetProductLicenseAPI() +} + +// GetProductDiskAPI ディスクプランAPI取得 +func (api *API) GetProductDiskAPI() *ProductDiskAPI { + return api.Product.GetProductDiskAPI() +} + +// GetProductInternetAPI ルータープランAPI取得 +func (api *API) GetProductInternetAPI() *ProductInternetAPI { + return api.Product.GetProductInternetAPI() +} + +// GetPublicPriceAPI 価格情報API取得 +func (api *API) GetPublicPriceAPI() *PublicPriceAPI { + return api.Product.GetPublicPriceAPI() +} + +// GetServerAPI サーバーAPI取得 +func (api *API) GetServerAPI() *ServerAPI { + return api.Server +} + +// GetSIMAPI SIM API取得 +func (api *API) GetSIMAPI() *SIMAPI { + return api.SIM +} + +// GetSimpleMonitorAPI シンプル監視API取得 +func (api *API) GetSimpleMonitorAPI() *SimpleMonitorAPI { + return api.SimpleMonitor +} + +// GetSSHKeyAPI SSH公開鍵API取得 +func (api *API) GetSSHKeyAPI() *SSHKeyAPI { + return api.SSHKey +} + +// GetSubnetAPI サブネットAPI取得 +func (api *API) GetSubnetAPI() *SubnetAPI { + return api.Subnet +} + +// GetSwitchAPI スイッチAPI取得 +func (api *API) GetSwitchAPI() *SwitchAPI { + return api.Switch +} + +// GetVPCRouterAPI VPCルーターAPI取得 +func (api *API) GetVPCRouterAPI() *VPCRouterAPI { + return api.VPCRouter +} + +// GetWebAccelAPI ウェブアクセラレータAPI取得 +func (api *API) GetWebAccelAPI() *WebAccelAPI { + return api.WebAccel +} + +// ProductAPI 製品情報関連API群 +type ProductAPI struct { + Server *ProductServerAPI // サーバープランAPI + License *ProductLicenseAPI // ライセンスプランAPI + Disk *ProductDiskAPI // ディスクプランAPI + Internet *ProductInternetAPI // ルータープランAPI + PrivateHost *ProductPrivateHostAPI // 専有ホストプランAPI + Price *PublicPriceAPI // 価格情報API +} + +// GetProductServerAPI サーバープランAPI取得 +func (api *ProductAPI) GetProductServerAPI() *ProductServerAPI { + return api.Server +} + +// GetProductLicenseAPI ライセンスプランAPI取得 +func (api *ProductAPI) GetProductLicenseAPI() *ProductLicenseAPI { + return api.License +} + +// GetProductDiskAPI ディスクプランAPI取得 +func (api *ProductAPI) GetProductDiskAPI() *ProductDiskAPI { + return api.Disk +} + +// GetProductInternetAPI ルータープランAPI取得 +func (api *ProductAPI) GetProductInternetAPI() *ProductInternetAPI { + return api.Internet +} + +// GetProductPrivateHostAPI 専有ホストプラン取得API +func (api *ProductAPI) GetProductPrivateHostAPI() *ProductPrivateHostAPI { + return api.PrivateHost +} + +// GetPublicPriceAPI 価格情報API取得 +func (api *ProductAPI) GetPublicPriceAPI() *PublicPriceAPI { + return api.Price +} + +// FacilityAPI ファシリティ関連API群 +type FacilityAPI struct { + Region *RegionAPI // リージョンAPI + Zone *ZoneAPI // ゾーンAPI +} + +// GetRegionAPI リージョンAPI取得 +func (api *FacilityAPI) GetRegionAPI() *RegionAPI { + return api.Region +} + +// GetZoneAPI ゾーンAPI取得 +func (api *FacilityAPI) GetZoneAPI() *ZoneAPI { + return api.Zone +} + +func newAPI(client *Client) *API { + return &API{ + AuthStatus: NewAuthStatusAPI(client), + AutoBackup: NewAutoBackupAPI(client), + Archive: NewArchiveAPI(client), + Bill: NewBillAPI(client), + Bridge: NewBridgeAPI(client), + CDROM: NewCDROMAPI(client), + Database: NewDatabaseAPI(client), + Disk: NewDiskAPI(client), + DNS: NewDNSAPI(client), + Facility: &FacilityAPI{ + Region: NewRegionAPI(client), + Zone: NewZoneAPI(client), + }, + GSLB: NewGSLBAPI(client), + Icon: NewIconAPI(client), + Interface: NewInterfaceAPI(client), + Internet: NewInternetAPI(client), + IPAddress: NewIPAddressAPI(client), + IPv6Addr: NewIPv6AddrAPI(client), + IPv6Net: NewIPv6NetAPI(client), + License: NewLicenseAPI(client), + LoadBalancer: NewLoadBalancerAPI(client), + MobileGateway: NewMobileGatewayAPI(client), + NewsFeed: NewNewsFeedAPI(client), + NFS: NewNFSAPI(client), + Note: NewNoteAPI(client), + PacketFilter: NewPacketFilterAPI(client), + PrivateHost: NewPrivateHostAPI(client), + Product: &ProductAPI{ + Server: NewProductServerAPI(client), + License: NewProductLicenseAPI(client), + Disk: NewProductDiskAPI(client), + Internet: NewProductInternetAPI(client), + PrivateHost: NewProductPrivateHostAPI(client), + Price: NewPublicPriceAPI(client), + }, + Server: NewServerAPI(client), + SIM: NewSIMAPI(client), + SimpleMonitor: NewSimpleMonitorAPI(client), + SSHKey: NewSSHKeyAPI(client), + Subnet: NewSubnetAPI(client), + Switch: NewSwitchAPI(client), + VPCRouter: NewVPCRouterAPI(client), + WebAccel: NewWebAccelAPI(client), + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/database.go b/vendor/github.com/sacloud/libsacloud/api/database.go new file mode 100644 index 000000000..9ebe9a148 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/database.go @@ -0,0 +1,429 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +//HACK: さくらのAPI側仕様: Applianceの内容によってJSONフォーマットが異なるため +// ロードバランサ/VPCルータそれぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchDatabaseResponse データベース検索レスポンス +type SearchDatabaseResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // Databases データベースリスト + Databases []sacloud.Database `json:"Appliances,omitempty"` +} + +type databaseRequest struct { + Database *sacloud.Database `json:"Appliance,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type databaseResponse struct { + *sacloud.ResultFlagValue + *sacloud.Database `json:"Appliance,omitempty"` + // Success + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{}で受ける +} + +type databaseStatusResponse struct { + *sacloud.ResultFlagValue + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} + Appliance *struct { + SettingsResponse *sacloud.DatabaseStatus + } +} + +type databaseBackupResponse struct { + Log string `json:",omitempty"` + IsOk bool `json:"is_ok,omitempty"` +} + +// DatabaseAPI データベースAPI +type DatabaseAPI struct { + *baseAPI +} + +// NewDatabaseAPI データベースAPI作成 +func NewDatabaseAPI(client *Client) *DatabaseAPI { + return &DatabaseAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "appliance" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Class", "database") + return res + }, + }, + } +} + +// Find 検索 +func (api *DatabaseAPI) Find() (*SearchDatabaseResponse, error) { + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchDatabaseResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *DatabaseAPI) request(f func(*databaseResponse) error) (*sacloud.Database, error) { + res := &databaseResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.Database, nil +} + +func (api *DatabaseAPI) createRequest(value *sacloud.Database) *databaseResponse { + return &databaseResponse{Database: value} +} + +// New 新規作成用パラメーター作成 +func (api *DatabaseAPI) New(values *sacloud.CreateDatabaseValue) *sacloud.Database { + return sacloud.CreateNewDatabase(values) +} + +// Create 新規作成 +func (api *DatabaseAPI) Create(value *sacloud.Database) (*sacloud.Database, error) { + return api.request(func(res *databaseResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *DatabaseAPI) Read(id int64) (*sacloud.Database, error) { + return api.request(func(res *databaseResponse) error { + return api.read(id, nil, res) + }) +} + +// Status DBの設定/起動状態の取得 +func (api *DatabaseAPI) Status(id int64) (*sacloud.DatabaseStatus, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/status", api.getResourceURL(), id) + ) + + res := &databaseStatusResponse{} + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res.Appliance.SettingsResponse, nil +} + +// Backup バックアップ取得 +func (api *DatabaseAPI) Backup(id int64) (string, error) { + var ( + method = "POST" + uri = fmt.Sprintf("%s/%d/action/history", api.getResourceURL(), id) + ) + + body := map[string]interface{}{ + "Appliance": map[string]interface{}{ + "Settings": map[string]interface{}{ + "DBConf": map[string]interface{}{ + "backup": map[string]string{ + "availability": "discontinued", + }, + }, + }, + }, + } + + res := &databaseBackupResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return "", err + } + return res.Log, nil +} + +// DownloadLog ログ取得 +func (api *DatabaseAPI) DownloadLog(id int64, logID string) (string, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/download/log/%s", api.getResourceURL(), id, logID) + ) + + res := &databaseBackupResponse{} + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return "", err + } + return res.Log, nil +} + +// Restore バックアップからの復元 +func (api *DatabaseAPI) Restore(id int64, backupID string) (string, error) { + var ( + method = "POST" + uri = fmt.Sprintf("%s/%d/action/history/%s", api.getResourceURL(), id, backupID) + ) + + body := map[string]interface{}{ + "Appliance": map[string]interface{}{}, + } + + res := &databaseBackupResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return "", err + } + return res.Log, nil +} + +// DeleteBackup バックアップの削除 +func (api *DatabaseAPI) DeleteBackup(id int64, backupID string) (string, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/action/history/%s", api.getResourceURL(), id, backupID) + ) + + body := map[string]interface{}{ + "Appliance": map[string]interface{}{}, + } + + res := &databaseBackupResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return "", err + } + return res.Log, nil +} + +// HistoryLock バックアップ削除ロック +func (api *DatabaseAPI) HistoryLock(id int64, backupID string) (string, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/action/history-lock/%s", api.getResourceURL(), id, backupID) + ) + + body := map[string]interface{}{ + "Appliance": map[string]interface{}{}, + } + + res := &databaseBackupResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return "", err + } + return res.Log, nil +} + +// HistoryUnlock バックアップ削除アンロック +func (api *DatabaseAPI) HistoryUnlock(id int64, backupID string) (string, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/action/history-lock/%s", api.getResourceURL(), id, backupID) + ) + + body := map[string]interface{}{ + "Appliance": map[string]interface{}{}, + } + + res := &databaseBackupResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return "", err + } + return res.Log, nil +} + +// Update 更新 +func (api *DatabaseAPI) Update(id int64, value *sacloud.Database) (*sacloud.Database, error) { + return api.request(func(res *databaseResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// UpdateSetting 設定更新 +func (api *DatabaseAPI) UpdateSetting(id int64, value *sacloud.Database) (*sacloud.Database, error) { + req := &sacloud.Database{ + // Settings + Settings: value.Settings, + } + return api.request(func(res *databaseResponse) error { + return api.update(id, api.createRequest(req), res) + }) +} + +// Delete 削除 +func (api *DatabaseAPI) Delete(id int64) (*sacloud.Database, error) { + return api.request(func(res *databaseResponse) error { + return api.delete(id, nil, res) + }) +} + +// Config 設定変更の反映 +func (api *DatabaseAPI) Config(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// IsUp 起動しているか判定 +func (api *DatabaseAPI) IsUp(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsUp(), nil +} + +// IsDown ダウンしているか判定 +func (api *DatabaseAPI) IsDown(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsDown(), nil +} + +// IsDatabaseRunning データベースプロセスが起動しているか判定 +func (api *DatabaseAPI) IsDatabaseRunning(id int64) (bool, error) { + db, err := api.Status(id) + if err != nil { + return false, err + } + return db.IsUp(), nil + +} + +// Boot 起動 +func (api *DatabaseAPI) Boot(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Shutdown シャットダウン(graceful) +func (api *DatabaseAPI) Shutdown(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Stop シャットダウン(force) +func (api *DatabaseAPI) Stop(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"Force": true}) +} + +// RebootForce 再起動 +func (api *DatabaseAPI) RebootForce(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// ResetForce リセット +func (api *DatabaseAPI) ResetForce(id int64, recycleProcess bool) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"RecycleProcess": recycleProcess}) +} + +// SleepUntilUp 起動するまで待機 +func (api *DatabaseAPI) SleepUntilUp(id int64, timeout time.Duration) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepUntilDatabaseRunning 起動するまで待機 +func (api *DatabaseAPI) SleepUntilDatabaseRunning(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) +} + +// SleepUntilDown ダウンするまで待機 +func (api *DatabaseAPI) SleepUntilDown(id int64, timeout time.Duration) error { + handler := waitingForDownFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepWhileCopying コピー終了まで待機 +func (api *DatabaseAPI) SleepWhileCopying(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *DatabaseAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return poll(handler, timeout) +} + +// MonitorCPU CPUアクティビティーモニター取得 +func (api *DatabaseAPI) MonitorCPU(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "cpu", 0, body) +} + +// MonitorDatabase データーベース固有項目アクティビティモニター取得 +func (api *DatabaseAPI) MonitorDatabase(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "database", 0, body) +} + +// MonitorInterface NICアクティビティーモニター取得 +func (api *DatabaseAPI) MonitorInterface(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "interface", 0, body) +} + +// MonitorSystemDisk システムディスクアクティビティーモニター取得 +func (api *DatabaseAPI) MonitorSystemDisk(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "disk", 1, body) +} + +// MonitorBackupDisk バックアップディスクアクティビティーモニター取得 +func (api *DatabaseAPI) MonitorBackupDisk(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "disk", 2, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/database_gen.go b/vendor/github.com/sacloud/libsacloud/api/database_gen.go new file mode 100644 index 000000000..26345aab4 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/database_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [DatabaseAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *DatabaseAPI) Reset() *DatabaseAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *DatabaseAPI) Offset(offset int) *DatabaseAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *DatabaseAPI) Limit(limit int) *DatabaseAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *DatabaseAPI) Include(key string) *DatabaseAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *DatabaseAPI) Exclude(key string) *DatabaseAPI { + api.exclude(key) + return api +} + +// FilterBy 指定項目でのフィルター +func (api *DatabaseAPI) FilterBy(key string, value interface{}) *DatabaseAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *DatabaseAPI) FilterMultiBy(key string, value interface{}) *DatabaseAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *DatabaseAPI) WithNameLike(name string) *DatabaseAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *DatabaseAPI) WithTag(tag string) *DatabaseAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *DatabaseAPI) WithTags(tags []string) *DatabaseAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *DatabaseAPI) WithSizeGib(size int) *DatabaseAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *DatabaseAPI) WithSharedScope() *DatabaseAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *DatabaseAPI) WithUserScope() *DatabaseAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *DatabaseAPI) SortBy(key string, reverse bool) *DatabaseAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *DatabaseAPI) SortByName(reverse bool) *DatabaseAPI { + api.sortByName(reverse) + return api +} + +// func (api *DatabaseAPI) SortBySize(reverse bool) *DatabaseAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *DatabaseAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *DatabaseAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *DatabaseAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *DatabaseAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *DatabaseAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定項目でのフィルター +func (api *DatabaseAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *DatabaseAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *DatabaseAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *DatabaseAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *DatabaseAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *DatabaseAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *DatabaseAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *DatabaseAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *DatabaseAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *DatabaseAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *DatabaseAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *DatabaseAPI) New() *sacloud.Database { +// return &sacloud.Database{} +// } + +// func (api *DatabaseAPI) Create(value *sacloud.Database) (*sacloud.Database, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *DatabaseAPI) Read(id string) (*sacloud.Database, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *DatabaseAPI) Update(id string, value *sacloud.Database) (*sacloud.Database, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *DatabaseAPI) Delete(id string) (*sacloud.Database, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *DatabaseAPI) setStateValue(setFunc func(*sacloud.Request)) *DatabaseAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *DatabaseAPI) request(f func(*sacloud.Response) error) (*sacloud.Database, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.Database, nil +//} +// +//func (api *DatabaseAPI) createRequest(value *sacloud.Database) *sacloud.Request { +// req := &sacloud.Request{} +// req.Database = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/disk.go b/vendor/github.com/sacloud/libsacloud/api/disk.go new file mode 100644 index 000000000..a90a4b00c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/disk.go @@ -0,0 +1,283 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +var ( + // allowDiskEditTags ディスクの編集可否判定に用いるタグ + allowDiskEditTags = []string{ + "os-unix", + "os-linux", + } + + // bundleInfoWindowsHostClass ディスクの編集可否判定に用いる、BundleInfoでのWindows判定文字列 + bundleInfoWindowsHostClass = "ms_windows" +) + +// DiskAPI ディスクAPI +type DiskAPI struct { + *baseAPI +} + +// NewDiskAPI ディスクAPI作成 +func NewDiskAPI(client *Client) *DiskAPI { + return &DiskAPI{ + &baseAPI{ + client: client, + // FuncGetResourceURL + FuncGetResourceURL: func() string { + return "disk" + }, + }, + } +} + +// SortByConnectionOrder 接続順でのソート +func (api *DiskAPI) SortByConnectionOrder(reverse bool) *DiskAPI { + api.sortBy("ConnectionOrder", reverse) + return api +} + +// WithServerID サーバーID条件 +func (api *DiskAPI) WithServerID(id int64) *DiskAPI { + api.FilterBy("Server.ID", id) + return api +} + +// Create 新規作成 +func (api *DiskAPI) Create(value *sacloud.Disk) (*sacloud.Disk, error) { + //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないため文字列で受ける + type diskResponse struct { + *sacloud.Response + // Success + Success string `json:",omitempty"` + } + res := &diskResponse{} + err := api.create(api.createRequest(value), res) + if err != nil { + return nil, err + } + return res.Disk, nil +} + +// NewCondig ディスクの修正用パラメーター作成 +func (api *DiskAPI) NewCondig() *sacloud.DiskEditValue { + return &sacloud.DiskEditValue{} +} + +// Config ディスクの修正 +func (api *DiskAPI) Config(id int64, disk *sacloud.DiskEditValue) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) + ) + + return api.modify(method, uri, disk) +} + +func (api *DiskAPI) install(id int64, body *sacloud.Disk) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/install", api.getResourceURL(), id) + ) + //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないため文字列で受ける + type diskResponse struct { + *sacloud.ResultFlagValue + // Success + Success string `json:",omitempty"` + } + res := &diskResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return false, err + } + return res.IsOk, nil +} + +// ReinstallFromBlank ブランクディスクから再インストール +func (api *DiskAPI) ReinstallFromBlank(id int64, sizeMB int) (bool, error) { + var body = &sacloud.Disk{} + body.SetSizeMB(sizeMB) + + return api.install(id, body) +} + +// ReinstallFromArchive アーカイブからの再インストール +func (api *DiskAPI) ReinstallFromArchive(id int64, archiveID int64, distantFrom ...int64) (bool, error) { + var body = &sacloud.Disk{} + body.SetSourceArchive(archiveID) + if len(distantFrom) > 0 { + body.SetDistantFrom(distantFrom) + } + return api.install(id, body) +} + +// ReinstallFromDisk ディスクからの再インストール +func (api *DiskAPI) ReinstallFromDisk(id int64, diskID int64, distantFrom ...int64) (bool, error) { + var body = &sacloud.Disk{} + body.SetSourceDisk(diskID) + if len(distantFrom) > 0 { + body.SetDistantFrom(distantFrom) + } + return api.install(id, body) +} + +// ToBlank ディスクを空にする +func (api *DiskAPI) ToBlank(diskID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/to/blank", api.getResourceURL(), diskID) + ) + return api.modify(method, uri, nil) +} + +// ResizePartition パーティションのリサイズ +func (api *DiskAPI) ResizePartition(diskID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/resize-partition", api.getResourceURL(), diskID) + ) + return api.modify(method, uri, nil) +} + +// DisconnectFromServer サーバーとの接続解除 +func (api *DiskAPI) DisconnectFromServer(diskID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/to/server", api.getResourceURL(), diskID) + ) + return api.modify(method, uri, nil) +} + +// ConnectToServer サーバーとの接続 +func (api *DiskAPI) ConnectToServer(diskID int64, serverID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/to/server/%d", api.getResourceURL(), diskID, serverID) + ) + return api.modify(method, uri, nil) +} + +// State ディスクの状態を取得し有効な状態か判定 +func (api *DiskAPI) State(diskID int64) (bool, error) { + disk, err := api.Read(diskID) + if err != nil { + return false, err + } + return disk.IsAvailable(), nil +} + +// SleepWhileCopying コピー終了まで待機 +func (api *DiskAPI) SleepWhileCopying(id int64, timeout time.Duration) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *DiskAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, 0) + return poll(handler, timeout) +} + +// Monitor アクティビティーモニター取得 +func (api *DiskAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.monitor(id, body) +} + +// CanEditDisk ディスクの修正が可能か判定 +func (api *DiskAPI) CanEditDisk(id int64) (bool, error) { + + disk, err := api.Read(id) + if err != nil { + return false, err + } + + if disk == nil { + return false, nil + } + + // BundleInfoがあれば編集不可 + if disk.BundleInfo != nil && disk.BundleInfo.HostClass == bundleInfoWindowsHostClass { + // Windows + return false, nil + } + + // SophosUTMであれば編集不可 + if disk.HasTag("pkg-sophosutm") || disk.IsSophosUTM() { + return false, nil + } + + // ソースアーカイブ/ソースディスクともに持っていない場合 + if disk.SourceArchive == nil && disk.SourceDisk == nil { + //ブランクディスクがソース + return false, nil + } + + for _, t := range allowDiskEditTags { + if disk.HasTag(t) { + // 対応OSインストール済みディスク + return true, nil + } + } + + // ここまできても判定できないならソースに投げる + if disk.SourceDisk != nil && disk.SourceDisk.Availability != "discontinued" { + return api.client.Disk.CanEditDisk(disk.SourceDisk.ID) + } + if disk.SourceArchive != nil && disk.SourceArchive.Availability != "discontinued" { + return api.client.Archive.CanEditDisk(disk.SourceArchive.ID) + } + + return false, nil + +} + +// GetPublicArchiveIDFromAncestors 祖先の中からパブリックアーカイブのIDを検索 +func (api *DiskAPI) GetPublicArchiveIDFromAncestors(id int64) (int64, bool) { + + emptyID := int64(0) + + disk, err := api.Read(id) + if err != nil { + return emptyID, false + } + + if disk == nil { + return emptyID, false + } + + // BundleInfoがあれば編集不可 + if disk.BundleInfo != nil && disk.BundleInfo.HostClass == bundleInfoWindowsHostClass { + // Windows + return emptyID, false + } + + // SophosUTMであれば編集不可 + if disk.HasTag("pkg-sophosutm") || disk.IsSophosUTM() { + return emptyID, false + } + + for _, t := range allowDiskEditTags { + if disk.HasTag(t) { + // 対応OSインストール済みディスク + return disk.ID, true + } + } + + // ここまできても判定できないならソースに投げる + if disk.SourceDisk != nil && disk.SourceDisk.Availability != "discontinued" { + return api.client.Disk.GetPublicArchiveIDFromAncestors(disk.SourceDisk.ID) + } + if disk.SourceArchive != nil && disk.SourceArchive.Availability != "discontinued" { + return api.client.Archive.GetPublicArchiveIDFromAncestors(disk.SourceArchive.ID) + } + return emptyID, false + +} diff --git a/vendor/github.com/sacloud/libsacloud/api/disk_gen.go b/vendor/github.com/sacloud/libsacloud/api/disk_gen.go new file mode 100644 index 000000000..114d12dff --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/disk_gen.go @@ -0,0 +1,228 @@ +package api + +/************************************************ + generated by IDE. for [DiskAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *DiskAPI) Reset() *DiskAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *DiskAPI) Offset(offset int) *DiskAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *DiskAPI) Limit(limit int) *DiskAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *DiskAPI) Include(key string) *DiskAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *DiskAPI) Exclude(key string) *DiskAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *DiskAPI) FilterBy(key string, value interface{}) *DiskAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *DiskAPI) FilterMultiBy(key string, value interface{}) *DiskAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *DiskAPI) WithNameLike(name string) *DiskAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *DiskAPI) WithTag(tag string) *DiskAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *DiskAPI) WithTags(tags []string) *DiskAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// WithSizeGib サイズ条件 +func (api *DiskAPI) WithSizeGib(size int) *DiskAPI { + api.FilterBy("SizeMB", size*1024) + return api +} + +// SortBy 指定キーでのソート +func (api *DiskAPI) SortBy(key string, reverse bool) *DiskAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *DiskAPI) SortByName(reverse bool) *DiskAPI { + api.sortByName(reverse) + return api +} + +// SortBySize サイズでのソート +func (api *DiskAPI) SortBySize(reverse bool) *DiskAPI { + api.sortBy("SizeMB", reverse) + return api +} + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *DiskAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *DiskAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *DiskAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *DiskAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *DiskAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *DiskAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *DiskAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *DiskAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *DiskAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *DiskAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// SetSizeGib サイズ条件 +func (api *DiskAPI) SetSizeGib(size int) { + api.FilterBy("SizeMB", size*1024) +} + +// SetSortBy 指定キーでのソート +func (api *DiskAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *DiskAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// SetSortBySize サイズでのソート +func (api *DiskAPI) SetSortBySize(reverse bool) { + api.sortBy("SizeMB", reverse) +} + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *DiskAPI) New() *sacloud.Disk { + return sacloud.CreateNewDisk() +} + +//func (api *DiskAPI) Create(value *sacloud.Disk) (*sacloud.Disk, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +//} + +// Read 読み取り +func (api *DiskAPI) Read(id int64) (*sacloud.Disk, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *DiskAPI) Update(id int64, value *sacloud.Disk) (*sacloud.Disk, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *DiskAPI) Delete(id int64) (*sacloud.Disk, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *DiskAPI) setStateValue(setFunc func(*sacloud.Request)) *DiskAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *DiskAPI) request(f func(*sacloud.Response) error) (*sacloud.Disk, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Disk, nil +} + +func (api *DiskAPI) createRequest(value *sacloud.Disk) *sacloud.Request { + req := &sacloud.Request{} + req.Disk = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/dns.go b/vendor/github.com/sacloud/libsacloud/api/dns.go new file mode 100644 index 000000000..3483a8355 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/dns.go @@ -0,0 +1,206 @@ +package api + +import ( + "encoding/json" + "github.com/sacloud/libsacloud/sacloud" + "strings" +) + +//HACK: さくらのAPI側仕様: CommonServiceItemsの内容によってJSONフォーマットが異なるため +// DNS/GSLB/シンプル監視それぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchDNSResponse DNS検索レスポンス +type SearchDNSResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // CommonServiceDNSItems DNSリスト + CommonServiceDNSItems []sacloud.DNS `json:"CommonServiceItems,omitempty"` +} +type dnsRequest struct { + CommonServiceDNSItem *sacloud.DNS `json:"CommonServiceItem,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} +type dnsResponse struct { + *sacloud.ResultFlagValue + *sacloud.DNS `json:"CommonServiceItem,omitempty"` +} + +// DNSAPI DNS API +type DNSAPI struct { + *baseAPI +} + +// NewDNSAPI DNS API作成 +func NewDNSAPI(client *Client) *DNSAPI { + return &DNSAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "commonserviceitem" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Provider.Class", "dns") + return res + }, + }, + } +} + +// Find 検索 +func (api *DNSAPI) Find() (*SearchDNSResponse, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchDNSResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *DNSAPI) request(f func(*dnsResponse) error) (*sacloud.DNS, error) { + res := &dnsResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.DNS, nil +} + +func (api *DNSAPI) createRequest(value *sacloud.DNS) *dnsRequest { + req := &dnsRequest{} + req.CommonServiceDNSItem = value + return req +} + +// Create 新規作成 +func (api *DNSAPI) Create(value *sacloud.DNS) (*sacloud.DNS, error) { + return api.request(func(res *dnsResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// New 新規作成用パラメーター作成 +func (api *DNSAPI) New(zoneName string) *sacloud.DNS { + return sacloud.CreateNewDNS(zoneName) +} + +// Read 読み取り +func (api *DNSAPI) Read(id int64) (*sacloud.DNS, error) { + return api.request(func(res *dnsResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *DNSAPI) Update(id int64, value *sacloud.DNS) (*sacloud.DNS, error) { + return api.request(func(res *dnsResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *DNSAPI) Delete(id int64) (*sacloud.DNS, error) { + return api.request(func(res *dnsResponse) error { + return api.delete(id, nil, res) + }) +} + +// SetupDNSRecord DNSレコード作成 +func (api *DNSAPI) SetupDNSRecord(zoneName string, hostName string, ip string) ([]string, error) { + + dnsItem, err := api.findOrCreateBy(zoneName) + if err != nil { + return nil, err + } + + if strings.HasSuffix(hostName, zoneName) { + hostName = strings.Replace(hostName, zoneName, "", -1) + } + + dnsItem.Settings.DNS.AddDNSRecordSet(hostName, ip) + + res, err := api.updateDNSRecord(dnsItem) + if err != nil { + return nil, err + } + + if dnsItem.ID == sacloud.EmptyID { + return res.Status.NS, nil + } + + return nil, nil + +} + +// DeleteDNSRecord DNSレコード削除 +func (api *DNSAPI) DeleteDNSRecord(zoneName string, hostName string, ip string) error { + dnsItem, err := api.findOrCreateBy(zoneName) + if err != nil { + return err + } + dnsItem.Settings.DNS.DeleteDNSRecordSet(hostName, ip) + + if dnsItem.HasDNSRecord() { + _, err = api.updateDNSRecord(dnsItem) + if err != nil { + return err + } + + } else { + _, err = api.Delete(dnsItem.ID) + if err != nil { + return err + } + + } + return nil +} + +func (api *DNSAPI) findOrCreateBy(zoneName string) (*sacloud.DNS, error) { + + res, err := api.Reset().WithNameLike(zoneName).Limit(1).Find() + if err != nil { + return nil, err + } + + //すでに登録されている場合 + var dnsItem *sacloud.DNS + if res.Count > 0 { + dnsItem = &res.CommonServiceDNSItems[0] + } else { + dnsItem = sacloud.CreateNewDNS(zoneName) + } + + return dnsItem, nil +} + +func (api *DNSAPI) updateDNSRecord(dnsItem *sacloud.DNS) (*sacloud.DNS, error) { + + var item *sacloud.DNS + var err error + + if dnsItem.ID == sacloud.EmptyID { + item, err = api.Create(dnsItem) + } else { + item, err = api.Update(dnsItem.ID, dnsItem) + } + + if err != nil { + return nil, err + } + + return item, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/dns_gen.go b/vendor/github.com/sacloud/libsacloud/api/dns_gen.go new file mode 100644 index 000000000..7ff419fcc --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/dns_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [DNSAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *DNSAPI) Reset() *DNSAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *DNSAPI) Offset(offset int) *DNSAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *DNSAPI) Limit(limit int) *DNSAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *DNSAPI) Include(key string) *DNSAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *DNSAPI) Exclude(key string) *DNSAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *DNSAPI) FilterBy(key string, value interface{}) *DNSAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *DNSAPI) FilterMultiBy(key string, value interface{}) *DNSAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *DNSAPI) WithNameLike(name string) *DNSAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *DNSAPI) WithTag(tag string) *DNSAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *DNSAPI) WithTags(tags []string) *DNSAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *DNSAPI) WithSizeGib(size int) *DNSAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *DNSAPI) WithSharedScope() *DNSAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *DNSAPI) WithUserScope() *DNSAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *DNSAPI) SortBy(key string, reverse bool) *DNSAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *DNSAPI) SortByName(reverse bool) *DNSAPI { + api.sortByName(reverse) + return api +} + +// func (api *DNSAPI) SortBySize(reverse bool) *DNSAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *DNSAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *DNSAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *DNSAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *DNSAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *DNSAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *DNSAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *DNSAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *DNSAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *DNSAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *DNSAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *DNSAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *DNSAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *DNSAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *DNSAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *DNSAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *DNSAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *DNSAPI) New() *sacloud.DNS { +// return &sacloud.DNS{} +// } + +// func (api *DNSAPI) Create(value *sacloud.DNS) (*sacloud.DNS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *DNSAPI) Read(id string) (*sacloud.DNS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *DNSAPI) Update(id string, value *sacloud.DNS) (*sacloud.DNS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *DNSAPI) Delete(id string) (*sacloud.DNS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *DNSAPI) setStateValue(setFunc func(*sacloud.Request)) *DNSAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *DNSAPI) request(f func(*sacloud.Response) error) (*sacloud.DNS, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.DNS, nil +//} +// +//func (api *DNSAPI) createRequest(value *sacloud.DNS) *dnsRequest { +// req := &dnsRequest{} +// req.CommonServiceDNSItem = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/doc.go b/vendor/github.com/sacloud/libsacloud/api/doc.go new file mode 100644 index 000000000..2ec4e0eb5 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/doc.go @@ -0,0 +1,176 @@ +// Package api is the Low Level APIs for maganing resources on SakuraCloud. +// +// さくらのクラウドでのリソース操作用の低レベルAPIです。 +// さくらのクラウドAPI呼び出しを行います。 +// +// Basic usage +// +// はじめにAPIクライアントを作成します。以降は作成したAPIクライアントを通じて各操作を行います。 +// +// APIクライアントの作成にはAPIトークン、APIシークレット、対象ゾーン情報が必要です。 +// +// あらかじめ、さくらのクラウド コントロールパネルにてAPIキーを発行しておいてください。 +// +// token := "PUT YOUR TOKEN" +// secret := "PUT YOUR SECRET" +// zone := "tk1a" +// +// // クライアントの作成 +// client := api.NewClient(token, secret, zone) +// +// Select target resource +// +// 操作対象のリソースごとにAPIクライアントのフィールドを用意しています。 +// 例えばサーバーの操作を行う場合はAPIクライアントの"Server"フィールドを通じて操作します。 +// +// フィールドの一覧は[type API]のドキュメントを参照してください。 +// +// // サーバーの検索の場合 +// client.Server.Find() +// +// // ディスクの検索の場合 +// client.Disk.Find() +// +// +// Find resource +// +// リソースの検索を行うには、条件を指定してFind()を呼び出します。 +// +// APIクライアントでは、検索条件の指定にFluent APIを採用しているため、メソッドチェーンで条件を指定できます。 +// +// // サーバーの検索 +// res, err := client.Server. +// WithNameLike("server name"). // サーバー名に"server name"が含まれる +// Offset(0). // 検索結果の位置0(先頭)から取得 +// Limit(5). // 5件取得 +// Include("Name"). // 結果にName列を含める +// Include("Description"). // 結果にDescription列を含める +// Find() // 検索実施 +// +// if err != nil { +// panic(err) +// } +// +// fmt.Printf("response: %#v", res.Servers) +// +// Create resource +// +// 新規作成用パラメーターを作成し、値を設定します。 +// その後APIクライアントのCreate()を呼び出します。 +// +// // スイッチの作成 +// param := client.Switch.New() // 新規作成用パラメーターの作成 +// param.Name = "example" // 値の設定(名前) +// param.Description = "example" // 値の設定(説明) +// sw, err := client.Switch.Create(param) // 作成 +// +// if err != nil { +// panic(err) +// } +// +// fmt.Printf("Created switch: %#v", sw) +// +// リソースの作成は非同期で行われます。 +// +// このため、サーバーやディスクなどの作成に時間がかかるリソースに対して +// Create()呼び出し直後に操作を行おうとした場合にエラーとなることがあります。 +// +// 必要に応じて適切なメソッドを呼び出し、リソースが適切な状態になるまで待機してください。 +// +// // パブリックアーカイブからディスク作成 +// archive, _ := client.Archive.FindLatestStableCentOS() +// // ディスクの作成 +// param := client.Disk.New() +// param.Name = "example" // 値の設定(名前) +// param.SetSourceArchive(archive.ID) // コピー元にCentOSパブリックアーカイブを指定 +// disk, err := client.Disk.Create(param) // 作成 +// +// if err != nil { +// panic(err) +// } +// +// // 作成完了まで待機 +// err = client.Disk.SleepWhileCopying(disk.ID, client.DefaultTimeoutDuration) +// +// // タイムアウト発生の場合errが返る +// if err != nil { +// panic(err) +// } +// +// fmt.Printf("Created disk: %#v", disk) +// +// Update resource +// +// APIクライアントのUpdate()メソッドを呼び出します。 +// +// req, err := client.Server.Find() +// if err != nil { +// panic(err) +// } +// server := &req.Servers[0] +// +// // 更新 +// server.Name = "update" // サーバー名を変更 +// server.AppendTag("example-tag") // タグを追加 +// updatedServer, err := client.Server.Update(server.ID, server) //更新実行 +// if err != nil { +// panic(err) +// } +// fmt.Printf("Updated server: %#v", updatedServer) +// +// 更新内容によってはあらかじめシャットダウンなどのリソースの操作が必要になる場合があります。 +// +// 詳細はさくらのクラウド ドキュメントを参照下さい。 +// http://cloud.sakura.ad.jp/document/ +// +// Delete resource +// +// APIクライアントのDeleteメソッドを呼び出します。 +// +// // 削除 +// deletedSwitch, err := client.Switch.Delete(sw.ID) +// if err != nil { +// panic(err) +// } +// fmt.Printf("Deleted switch: %#v", deletedSwitch) +// +// 対象リソースによってはあらかじめシャットダウンや切断などの操作が必要になる場合があります。 +// +// 詳細はさくらのクラウド ドキュメントを参照下さい。 +// http://cloud.sakura.ad.jp/document/ +// +// PowerManagement +// +// サーバーやアプライアンスなどの電源操作もAPIを通じて行えます。 +// +// // 起動 +// _, err = client.Server.Boot(server.ID) +// if err != nil { +// panic(err) +// } +// +// // 起動完了まで待機 +// err = client.Server.SleepUntilUp(server.ID, client.DefaultTimeoutDuration) +// +// // シャットダウン +// _, err = client.Server.Shutdown(server.ID) // gracefulシャットダウン +// +// if err != nil { +// panic(err) +// } +// +// // ダウンまで待機 +// err = client.Server.SleepUntilDown(server.ID, client.DefaultTimeoutDuration) +// +// 電源関連のAPI呼び出しは非同期で行われます。 +// +// 必要に応じて適切なメソッドを呼び出し、リソースが適切な状態になるまで待機してください。 +// +// Other +// +// APIクライアントでは開発時のデバッグ出力が可能です。 +// 以下のようにTraceModeフラグをtrueに設定した上でAPI呼び出しを行うと、標準出力へトレースログが出力されます。 +// +// client.TraceMode = true +// +package api diff --git a/vendor/github.com/sacloud/libsacloud/api/error.go b/vendor/github.com/sacloud/libsacloud/api/error.go new file mode 100644 index 000000000..5e0ca2a86 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/error.go @@ -0,0 +1,79 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +// Error APIコール時のエラー情報 +type Error interface { + // errorインターフェースを内包 + error + + // エラー発生時のレスポンスコード + ResponseCode() int + + // エラーコード + Code() string + + // エラー発生時のメッセージ + Message() string + + // エラー追跡用シリアルコード + Serial() string + + // エラー(オリジナル) + OrigErr() *sacloud.ResultErrorValue +} + +// NewError APIコール時のエラー情報 +func NewError(responseCode int, err *sacloud.ResultErrorValue) Error { + return &apiError{ + responseCode: responseCode, + origErr: err, + } +} + +type apiError struct { + responseCode int + origErr *sacloud.ResultErrorValue +} + +// Error errorインターフェース +func (e *apiError) Error() string { + return fmt.Sprintf("Error in response: %#v", e.origErr) +} + +// ResponseCode エラー発生時のレスポンスコード +func (e *apiError) ResponseCode() int { + return e.responseCode +} + +// Code エラーコード +func (e *apiError) Code() string { + if e.origErr != nil { + return e.origErr.ErrorCode + } + return "" +} + +// Message エラー発生時のメッセージ( +func (e *apiError) Message() string { + if e.origErr != nil { + return e.origErr.ErrorMessage + } + return "" +} + +// Serial エラー追跡用シリアルコード +func (e *apiError) Serial() string { + if e.origErr != nil { + return e.origErr.Serial + } + return "" +} + +// OrigErr エラー(オリジナル) +func (e *apiError) OrigErr() *sacloud.ResultErrorValue { + return e.origErr +} diff --git a/vendor/github.com/sacloud/libsacloud/api/gslb.go b/vendor/github.com/sacloud/libsacloud/api/gslb.go new file mode 100644 index 000000000..bce4cc2dd --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/gslb.go @@ -0,0 +1,202 @@ +package api + +import ( + "encoding/json" + // "strings" + "github.com/sacloud/libsacloud/sacloud" +) + +//HACK: さくらのAPI側仕様: CommonServiceItemsの内容によってJSONフォーマットが異なるため +// DNS/GSLB/シンプル監視それぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchGSLBResponse GSLB検索レスポンス +type SearchGSLBResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // CommonServiceGSLBItems GSLBリスト + CommonServiceGSLBItems []sacloud.GSLB `json:"CommonServiceItems,omitempty"` +} + +type gslbRequest struct { + CommonServiceGSLBItem *sacloud.GSLB `json:"CommonServiceItem,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type gslbResponse struct { + *sacloud.ResultFlagValue + *sacloud.GSLB `json:"CommonServiceItem,omitempty"` +} + +// GSLBAPI GSLB API +type GSLBAPI struct { + *baseAPI +} + +// NewGSLBAPI GSLB API作成 +func NewGSLBAPI(client *Client) *GSLBAPI { + return &GSLBAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "commonserviceitem" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Provider.Class", "gslb") + return res + }, + }, + } +} + +// Find 検索 +func (api *GSLBAPI) Find() (*SearchGSLBResponse, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchGSLBResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *GSLBAPI) request(f func(*gslbResponse) error) (*sacloud.GSLB, error) { + res := &gslbResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.GSLB, nil +} + +func (api *GSLBAPI) createRequest(value *sacloud.GSLB) *gslbResponse { + return &gslbResponse{GSLB: value} +} + +// New 新規作成用パラメーター作成 +func (api *GSLBAPI) New(name string) *sacloud.GSLB { + return sacloud.CreateNewGSLB(name) +} + +// Create 新規作成 +func (api *GSLBAPI) Create(value *sacloud.GSLB) (*sacloud.GSLB, error) { + return api.request(func(res *gslbResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *GSLBAPI) Read(id int64) (*sacloud.GSLB, error) { + return api.request(func(res *gslbResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *GSLBAPI) Update(id int64, value *sacloud.GSLB) (*sacloud.GSLB, error) { + return api.request(func(res *gslbResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *GSLBAPI) Delete(id int64) (*sacloud.GSLB, error) { + return api.request(func(res *gslbResponse) error { + return api.delete(id, nil, res) + }) +} + +// SetupGSLBRecord GSLB配下にサーバー追加 +func (api *GSLBAPI) SetupGSLBRecord(gslbName string, ip string) ([]string, error) { + + gslbItem, err := api.findOrCreateBy(gslbName) + + if err != nil { + return nil, err + } + gslbItem.Settings.GSLB.AddServer(ip) + res, err := api.updateGSLBServers(gslbItem) + if err != nil { + return nil, err + } + + if gslbItem.ID == sacloud.EmptyID { + return []string{res.Status.FQDN}, nil + } + return nil, nil + +} + +// DeleteGSLBServer GSLB配下のサーバー削除 +func (api *GSLBAPI) DeleteGSLBServer(gslbName string, ip string) error { + gslbItem, err := api.findOrCreateBy(gslbName) + if err != nil { + return err + } + gslbItem.Settings.GSLB.DeleteServer(ip) + + if gslbItem.HasGSLBServer() { + _, err = api.updateGSLBServers(gslbItem) + if err != nil { + return err + } + + } else { + _, err = api.Delete(gslbItem.ID) + if err != nil { + return err + } + + } + return nil +} + +func (api *GSLBAPI) findOrCreateBy(gslbName string) (*sacloud.GSLB, error) { + + req := &sacloud.Request{} + req.AddFilter("Name", gslbName) + res, err := api.Find() + if err != nil { + return nil, err + } + + //すでに登録されている場合 + var gslbItem *sacloud.GSLB + if res.Count > 0 { + gslbItem = &res.CommonServiceGSLBItems[0] + } else { + gslbItem = sacloud.CreateNewGSLB(gslbName) + } + + return gslbItem, nil +} + +func (api *GSLBAPI) updateGSLBServers(gslbItem *sacloud.GSLB) (*sacloud.GSLB, error) { + + var item *sacloud.GSLB + var err error + + if gslbItem.ID == sacloud.EmptyID { + item, err = api.Create(gslbItem) + } else { + item, err = api.Update(gslbItem.ID, gslbItem) + } + + if err != nil { + return nil, err + } + + return item, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/gslb_gen.go b/vendor/github.com/sacloud/libsacloud/api/gslb_gen.go new file mode 100644 index 000000000..2ea80d31f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/gslb_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [GSLBAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *GSLBAPI) Reset() *GSLBAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *GSLBAPI) Offset(offset int) *GSLBAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *GSLBAPI) Limit(limit int) *GSLBAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *GSLBAPI) Include(key string) *GSLBAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *GSLBAPI) Exclude(key string) *GSLBAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *GSLBAPI) FilterBy(key string, value interface{}) *GSLBAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *GSLBAPI) FilterMultiBy(key string, value interface{}) *GSLBAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *GSLBAPI) WithNameLike(name string) *GSLBAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *GSLBAPI) WithTag(tag string) *GSLBAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *GSLBAPI) WithTags(tags []string) *GSLBAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *GSLBAPI) WithSizeGib(size int) *GSLBAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *GSLBAPI) WithSharedScope() *GSLBAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *GSLBAPI) WithUserScope() *GSLBAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *GSLBAPI) SortBy(key string, reverse bool) *GSLBAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *GSLBAPI) SortByName(reverse bool) *GSLBAPI { + api.sortByName(reverse) + return api +} + +// func (api *GSLBAPI) SortBySize(reverse bool) *GSLBAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *GSLBAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *GSLBAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *GSLBAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *GSLBAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *GSLBAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *GSLBAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *GSLBAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *GSLBAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *GSLBAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *GSLBAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *GSLBAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *GSLBAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *GSLBAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *GSLBAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *GSLBAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *GSLBAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *GSLBAPI) New() *sacloud.GSLB { +// return &sacloud.GSLB{} +// } + +// func (api *GSLBAPI) Create(value *sacloud.GSLB) (*sacloud.GSLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *GSLBAPI) Read(id string) (*sacloud.GSLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *GSLBAPI) Update(id string, value *sacloud.GSLB) (*sacloud.GSLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *GSLBAPI) Delete(id string) (*sacloud.GSLB, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *GSLBAPI) setStateValue(setFunc func(*sacloud.Request)) *GSLBAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *GSLBAPI) request(f func(*sacloud.Response) error) (*sacloud.GSLB, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.GSLB, nil +//} +// +//func (api *GSLBAPI) createRequest(value *sacloud.GSLB) *sacloud.Request { +// req := &sacloud.Request{} +// req.GSLB = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/icon.go b/vendor/github.com/sacloud/libsacloud/api/icon.go new file mode 100644 index 000000000..db584c8cf --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/icon.go @@ -0,0 +1,31 @@ +package api + +import "github.com/sacloud/libsacloud/sacloud" + +// IconAPI アイコンAPI +type IconAPI struct { + *baseAPI +} + +// NewIconAPI アイコンAPI作成 +func NewIconAPI(client *Client) *IconAPI { + return &IconAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "icon" + }, + }, + } +} + +// GetImage アイコン画像データ(BASE64文字列)取得 +func (api *IconAPI) GetImage(id int64, size string) (*sacloud.Image, error) { + + res := &sacloud.Response{} + err := api.read(id, map[string]string{"Size": size}, res) + if err != nil { + return nil, err + } + return res.Image, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/icon_gen.go b/vendor/github.com/sacloud/libsacloud/api/icon_gen.go new file mode 100644 index 000000000..38546b805 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/icon_gen.go @@ -0,0 +1,247 @@ +package api + +/************************************************ + generated by IDE. for [IconAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *IconAPI) Reset() *IconAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *IconAPI) Offset(offset int) *IconAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *IconAPI) Limit(limit int) *IconAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *IconAPI) Include(key string) *IconAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *IconAPI) Exclude(key string) *IconAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *IconAPI) FilterBy(key string, value interface{}) *IconAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IconAPI) FilterMultiBy(key string, value interface{}) *IconAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *IconAPI) WithNameLike(name string) *IconAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *IconAPI) WithTag(tag string) *IconAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *IconAPI) WithTags(tags []string) *IconAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *IconAPI) WithSizeGib(size int) *IconAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// WithSharedScope 公開スコープ条件 +func (api *IconAPI) WithSharedScope() *IconAPI { + api.FilterBy("Scope", "shared") + return api +} + +// WithUserScope ユーザースコープ条件 +func (api *IconAPI) WithUserScope() *IconAPI { + api.FilterBy("Scope", "user") + return api +} + +// SortBy 指定キーでのソート +func (api *IconAPI) SortBy(key string, reverse bool) *IconAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *IconAPI) SortByName(reverse bool) *IconAPI { + api.sortByName(reverse) + return api +} + +// func (api *IconAPI) SortBySize(reverse bool) *IconAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *IconAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *IconAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *IconAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *IconAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *IconAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *IconAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IconAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *IconAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *IconAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *IconAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *IconAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// SetSharedScope 公開スコープ条件 +func (api *IconAPI) SetSharedScope() { + api.FilterBy("Scope", "shared") +} + +// SetUserScope ユーザースコープ条件 +func (api *IconAPI) SetUserScope() { + api.FilterBy("Scope", "user") +} + +// SetSortBy 指定キーでのソート +func (api *IconAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *IconAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *IconAPI) SortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *IconAPI) New() *sacloud.Icon { + return &sacloud.Icon{} +} + +// Create 新規作成 +func (api *IconAPI) Create(value *sacloud.Icon) (*sacloud.Icon, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *IconAPI) Read(id int64) (*sacloud.Icon, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *IconAPI) Update(id int64, value *sacloud.Icon) (*sacloud.Icon, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *IconAPI) Delete(id int64) (*sacloud.Icon, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *IconAPI) setStateValue(setFunc func(*sacloud.Request)) *IconAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *IconAPI) request(f func(*sacloud.Response) error) (*sacloud.Icon, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Icon, nil +} + +func (api *IconAPI) createRequest(value *sacloud.Icon) *sacloud.Request { + req := &sacloud.Request{} + req.Icon = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/interface.go b/vendor/github.com/sacloud/libsacloud/api/interface.go new file mode 100644 index 000000000..21282c6c2 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/interface.go @@ -0,0 +1,83 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +// InterfaceAPI インターフェースAPI +type InterfaceAPI struct { + *baseAPI +} + +// NewInterfaceAPI インターフェースAPI作成 +func NewInterfaceAPI(client *Client) *InterfaceAPI { + return &InterfaceAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "interface" + }, + }, + } +} + +// CreateAndConnectToServer 新規作成しサーバーへ接続する +func (api *InterfaceAPI) CreateAndConnectToServer(serverID int64) (*sacloud.Interface, error) { + iface := api.New() + iface.Server = &sacloud.Server{ + // Resource + Resource: &sacloud.Resource{ID: serverID}, + } + return api.Create(iface) +} + +// ConnectToSwitch スイッチへ接続する +func (api *InterfaceAPI) ConnectToSwitch(interfaceID int64, switchID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/to/switch/%d", api.getResourceURL(), interfaceID, switchID) + ) + return api.modify(method, uri, nil) +} + +// ConnectToSharedSegment 共有セグメントへ接続する +func (api *InterfaceAPI) ConnectToSharedSegment(interfaceID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/to/switch/shared", api.getResourceURL(), interfaceID) + ) + return api.modify(method, uri, nil) +} + +// DisconnectFromSwitch スイッチと切断する +func (api *InterfaceAPI) DisconnectFromSwitch(interfaceID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/to/switch", api.getResourceURL(), interfaceID) + ) + return api.modify(method, uri, nil) +} + +// Monitor アクティビティーモニター取得 +func (api *InterfaceAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.monitor(id, body) +} + +// ConnectToPacketFilter パケットフィルター適用 +func (api *InterfaceAPI) ConnectToPacketFilter(interfaceID int64, packetFilterID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("/%s/%d/to/packetfilter/%d", api.getResourceURL(), interfaceID, packetFilterID) + ) + return api.modify(method, uri, nil) +} + +// DisconnectFromPacketFilter パケットフィルター切断 +func (api *InterfaceAPI) DisconnectFromPacketFilter(interfaceID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("/%s/%d/to/packetfilter", api.getResourceURL(), interfaceID) + ) + return api.modify(method, uri, nil) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/interface_gen.go b/vendor/github.com/sacloud/libsacloud/api/interface_gen.go new file mode 100644 index 000000000..81bc2425e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/interface_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [InterfaceAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *InterfaceAPI) Reset() *InterfaceAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *InterfaceAPI) Offset(offset int) *InterfaceAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *InterfaceAPI) Limit(limit int) *InterfaceAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *InterfaceAPI) Include(key string) *InterfaceAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *InterfaceAPI) Exclude(key string) *InterfaceAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *InterfaceAPI) FilterBy(key string, value interface{}) *InterfaceAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *InterfaceAPI) FilterMultiBy(key string, value interface{}) *InterfaceAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *InterfaceAPI) WithNameLike(name string) *InterfaceAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *InterfaceAPI) WithTag(tag string) *InterfaceAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *InterfaceAPI) WithTags(tags []string) *InterfaceAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *InterfaceAPI) WithSizeGib(size int) *InterfaceAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *InterfaceAPI) WithSharedScope() *InterfaceAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *InterfaceAPI) WithUserScope() *InterfaceAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *InterfaceAPI) SortBy(key string, reverse bool) *InterfaceAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *InterfaceAPI) SortByName(reverse bool) *InterfaceAPI { + api.sortByName(reverse) + return api +} + +// func (api *InterfaceAPI) SortBySize(reverse bool) *InterfaceAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *InterfaceAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *InterfaceAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *InterfaceAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *InterfaceAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *InterfaceAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *InterfaceAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *InterfaceAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *InterfaceAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *InterfaceAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *InterfaceAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *InterfaceAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *InterfaceAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *InterfaceAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *InterfaceAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *InterfaceAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *InterfaceAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *InterfaceAPI) New() *sacloud.Interface { + return &sacloud.Interface{} +} + +// Create 新規作成 +func (api *InterfaceAPI) Create(value *sacloud.Interface) (*sacloud.Interface, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *InterfaceAPI) Read(id int64) (*sacloud.Interface, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *InterfaceAPI) Update(id int64, value *sacloud.Interface) (*sacloud.Interface, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *InterfaceAPI) Delete(id int64) (*sacloud.Interface, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *InterfaceAPI) setStateValue(setFunc func(*sacloud.Request)) *InterfaceAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *InterfaceAPI) request(f func(*sacloud.Response) error) (*sacloud.Interface, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Interface, nil +} + +func (api *InterfaceAPI) createRequest(value *sacloud.Interface) *sacloud.Request { + req := &sacloud.Request{} + req.Interface = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/internet.go b/vendor/github.com/sacloud/libsacloud/api/internet.go new file mode 100644 index 000000000..015134143 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/internet.go @@ -0,0 +1,143 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +// InternetAPI ルーターAPI +type InternetAPI struct { + *baseAPI +} + +// NewInternetAPI ルーターAPI作成 +func NewInternetAPI(client *Client) *InternetAPI { + return &InternetAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "internet" + }, + }, + } +} + +// UpdateBandWidth 帯域幅更新 +func (api *InternetAPI) UpdateBandWidth(id int64, bandWidth int) (*sacloud.Internet, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/bandwidth", api.getResourceURL(), id) + body = &sacloud.Request{} + ) + body.Internet = &sacloud.Internet{BandWidthMbps: bandWidth} + + return api.request(func(res *sacloud.Response) error { + return api.baseAPI.request(method, uri, body, res) + }) +} + +// AddSubnet IPアドレスブロックの追加割り当て +func (api *InternetAPI) AddSubnet(id int64, nwMaskLen int, nextHop string) (*sacloud.Subnet, error) { + var ( + method = "POST" + uri = fmt.Sprintf("%s/%d/subnet", api.getResourceURL(), id) + ) + body := &map[string]interface{}{ + "NetworkMaskLen": nwMaskLen, + "NextHop": nextHop, + } + + res := &sacloud.Response{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Subnet, nil +} + +// UpdateSubnet IPアドレスブロックの更新 +func (api *InternetAPI) UpdateSubnet(id int64, subnetID int64, nextHop string) (*sacloud.Subnet, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/subnet/%d", api.getResourceURL(), id, subnetID) + ) + body := &map[string]interface{}{ + "NextHop": nextHop, + } + + res := &sacloud.Response{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Subnet, nil +} + +// DeleteSubnet IPアドレスブロックの削除 +func (api *InternetAPI) DeleteSubnet(id int64, subnetID int64) (*sacloud.ResultFlagValue, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/subnet/%d", api.getResourceURL(), id, subnetID) + ) + + res := &sacloud.ResultFlagValue{} + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res, nil +} + +// EnableIPv6 IPv6有効化 +func (api *InternetAPI) EnableIPv6(id int64) (*sacloud.IPv6Net, error) { + var ( + method = "POST" + uri = fmt.Sprintf("%s/%d/ipv6net", api.getResourceURL(), id) + ) + + res := &sacloud.Response{} + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res.IPv6Net, nil +} + +// DisableIPv6 IPv6無効化 +func (api *InternetAPI) DisableIPv6(id int64, ipv6NetID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/ipv6net/%d", api.getResourceURL(), id, ipv6NetID) + ) + + res := &sacloud.Response{} + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return false, err + } + return true, nil +} + +// SleepWhileCreating 作成完了まで待機(リトライ10) +func (api *InternetAPI) SleepWhileCreating(id int64, timeout time.Duration) error { + handler := waitingForReadFunc(func() (interface{}, error) { + return api.Read(id) + }, 10) // 作成直後はReadが404を返すことがあるためリトライ + return blockingPoll(handler, timeout) + +} + +// RetrySleepWhileCreating 作成完了まで待機 作成直後はReadが404を返すことがあるためmaxRetryまでリトライする +func (api *InternetAPI) RetrySleepWhileCreating(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForReadFunc(func() (interface{}, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) + +} + +// Monitor アクティビティーモニター取得 +func (api *InternetAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.monitor(id, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/internet_gen.go b/vendor/github.com/sacloud/libsacloud/api/internet_gen.go new file mode 100644 index 000000000..f2c96fa7f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/internet_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [InternetAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *InternetAPI) Reset() *InternetAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *InternetAPI) Offset(offset int) *InternetAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *InternetAPI) Limit(limit int) *InternetAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *InternetAPI) Include(key string) *InternetAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *InternetAPI) Exclude(key string) *InternetAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *InternetAPI) FilterBy(key string, value interface{}) *InternetAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *InternetAPI) FilterMultiBy(key string, value interface{}) *InternetAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *InternetAPI) WithNameLike(name string) *InternetAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *InternetAPI) WithTag(tag string) *InternetAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *InternetAPI) WithTags(tags []string) *InternetAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *InternetAPI) WithSizeGib(size int) *InternetAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *InternetAPI) WithSharedScope() *InternetAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *InternetAPI) WithUserScope() *InternetAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *InternetAPI) SortBy(key string, reverse bool) *InternetAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *InternetAPI) SortByName(reverse bool) *InternetAPI { + api.sortByName(reverse) + return api +} + +// func (api *InternetAPI) SortBySize(reverse bool) *InternetAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *InternetAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *InternetAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *InternetAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *InternetAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *InternetAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *InternetAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *InternetAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *InternetAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *InternetAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *InternetAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *InternetAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *InternetAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *InternetAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *InternetAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *InternetAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *InternetAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *InternetAPI) New() *sacloud.Internet { + return &sacloud.Internet{} +} + +// Create 新規作成 +func (api *InternetAPI) Create(value *sacloud.Internet) (*sacloud.Internet, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *InternetAPI) Read(id int64) (*sacloud.Internet, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *InternetAPI) Update(id int64, value *sacloud.Internet) (*sacloud.Internet, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *InternetAPI) Delete(id int64) (*sacloud.Internet, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *InternetAPI) setStateValue(setFunc func(*sacloud.Request)) *InternetAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *InternetAPI) request(f func(*sacloud.Response) error) (*sacloud.Internet, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Internet, nil +} + +func (api *InternetAPI) createRequest(value *sacloud.Internet) *sacloud.Request { + req := &sacloud.Request{} + req.Internet = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ipaddress.go b/vendor/github.com/sacloud/libsacloud/api/ipaddress.go new file mode 100644 index 000000000..3de95259c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ipaddress.go @@ -0,0 +1,56 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +// IPAddressAPI IPアドレスAPI +type IPAddressAPI struct { + *baseAPI +} + +// NewIPAddressAPI IPアドレスAPI新規作成 +func NewIPAddressAPI(client *Client) *IPAddressAPI { + return &IPAddressAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "ipaddress" + }, + }, + } +} + +// Read 読み取り +func (api *IPAddressAPI) Read(ip string) (*sacloud.IPAddress, error) { + return api.request(func(res *sacloud.Response) error { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%s", api.getResourceURL(), ip) + ) + + return api.baseAPI.request(method, uri, nil, res) + }) + +} + +// Update 更新(ホスト名逆引き設定) +func (api *IPAddressAPI) Update(ip string, hostName string) (*sacloud.IPAddress, error) { + + type request struct { + // IPAddress + IPAddress map[string]string + } + + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%s", api.getResourceURL(), ip) + body = &request{IPAddress: map[string]string{}} + ) + body.IPAddress["HostName"] = hostName + + return api.request(func(res *sacloud.Response) error { + return api.baseAPI.request(method, uri, body, res) + }) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ipaddress_gen.go b/vendor/github.com/sacloud/libsacloud/api/ipaddress_gen.go new file mode 100644 index 000000000..d20f72e6b --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ipaddress_gen.go @@ -0,0 +1,231 @@ +package api + +/************************************************ + generated by IDE. for [IPAddressAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *IPAddressAPI) Reset() *IPAddressAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *IPAddressAPI) Offset(offset int) *IPAddressAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *IPAddressAPI) Limit(limit int) *IPAddressAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *IPAddressAPI) Include(key string) *IPAddressAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *IPAddressAPI) Exclude(key string) *IPAddressAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルタ +func (api *IPAddressAPI) FilterBy(key string, value interface{}) *IPAddressAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IPAddressAPI) FilterMultiBy(key string, value interface{}) *IPAddressAPI { + api.filterBy(key, value, true) + return api +} + +//func (api *IPAddressAPI) WithNameLike(name string) *IPAddressAPI { +// return api.FilterBy("Name", name) +//} + +//func (api *IPAddressAPI) WithTag(tag string) *IPAddressAPI { +// return api.FilterBy("Tags.Name", tag) +//} +//func (api *IPAddressAPI) WithTags(tags []string) *IPAddressAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} +// +//func (api *IPAddressAPI) WithSizeGib(size int) *IPAddressAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +//} +// +//func (api *IPAddressAPI) WithSharedScope() *IPAddressAPI { +// api.FilterBy("Scope", "shared") +// return api +//} +// +//func (api *IPAddressAPI) WithUserScope() *IPAddressAPI { +// api.FilterBy("Scope", "user") +// return api +//} + +// SortBy 指定キーでのソート +func (api *IPAddressAPI) SortBy(key string, reverse bool) *IPAddressAPI { + api.sortBy(key, reverse) + return api +} + +//// SortByName 名称でのソート +//func (api *IPAddressAPI) SortByName(reverse bool) *IPAddressAPI { +// api.sortByName(reverse) +// return api +//} + +//func (api *IPAddressAPI) SortBySize(reverse bool) *IPAddressAPI { +// api.sortBy("SizeMB", reverse) +// return api +//} + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *IPAddressAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *IPAddressAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *IPAddressAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *IPAddressAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *IPAddressAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルタ +func (api *IPAddressAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IPAddressAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +//func (api *IPAddressAPI) SetNameLike(name string) { +// api.FilterBy("Name", name) +//} + +//func (api *IPAddressAPI) SetTag(tag string) { +// api.FilterBy("Tags.Name", tag) +//} +//func (api *IPAddressAPI) SetTags(tags []string) { +// api.FilterBy("Tags.Name", []interface{}{tags}) +//} +// +//func (api *IPAddressAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +//} +// +//func (api *IPAddressAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +//} +// +//func (api *IPAddressAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +//} + +// SetSortBy 指定キーでのソート +func (api *IPAddressAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +//// SetSortByName 名称でのソート +//func (api *IPAddressAPI) SetSortByName(reverse bool) { +// api.sortByName(reverse) +//} + +//func (api *IPAddressAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +//} + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *IPAddressAPI) Create(value *sacloud.IPAddress) (*sacloud.IPAddress, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +//} + +//func (api *IPAddressAPI) Read(id int64) (*sacloud.IPAddress, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +//} + +//func (api *IPAddressAPI) Update(id int64, value *sacloud.IPAddress) (*sacloud.IPAddress, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +//} +// +//func (api *IPAddressAPI) Delete(id int64) (*sacloud.IPAddress, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +//} +// +//func (api *IPAddressAPI) New() *sacloud.IPAddress { +// return &sacloud.IPAddress{ +// } +//} + +/************************************************ + Inner functions +************************************************/ + +func (api *IPAddressAPI) setStateValue(setFunc func(*sacloud.Request)) *IPAddressAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *IPAddressAPI) request(f func(*sacloud.Response) error) (*sacloud.IPAddress, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.IPAddress, nil +} + +func (api *IPAddressAPI) createRequest(value *sacloud.IPAddress) *sacloud.Request { + req := &sacloud.Request{} + req.IPAddress = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ipv6addr.go b/vendor/github.com/sacloud/libsacloud/api/ipv6addr.go new file mode 100644 index 000000000..7186d3e05 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ipv6addr.go @@ -0,0 +1,96 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +// IPv6AddrAPI IPv6アドレスAPI +type IPv6AddrAPI struct { + *baseAPI +} + +// NewIPv6AddrAPI IPv6アドレスAPI新規作成 +func NewIPv6AddrAPI(client *Client) *IPv6AddrAPI { + return &IPv6AddrAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "ipv6addr" + }, + }, + } +} + +// Read 読み取り +func (api *IPv6AddrAPI) Read(ip string) (*sacloud.IPv6Addr, error) { + return api.request(func(res *sacloud.Response) error { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%s", api.getResourceURL(), ip) + ) + + return api.baseAPI.request(method, uri, nil, res) + }) + +} + +// Create 新規作成 +func (api *IPv6AddrAPI) Create(ip string, hostName string) (*sacloud.IPv6Addr, error) { + + type request struct { + // IPv6Addr + IPv6Addr map[string]string + } + + var ( + method = "POST" + uri = api.getResourceURL() + body = &request{IPv6Addr: map[string]string{}} + ) + body.IPv6Addr["IPv6Addr"] = ip + body.IPv6Addr["HostName"] = hostName + + return api.request(func(res *sacloud.Response) error { + return api.baseAPI.request(method, uri, body, res) + }) +} + +// Update 更新 +func (api *IPv6AddrAPI) Update(ip string, hostName string) (*sacloud.IPv6Addr, error) { + + type request struct { + // IPv6Addr + IPv6Addr map[string]string + } + + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%s", api.getResourceURL(), ip) + body = &request{IPv6Addr: map[string]string{}} + ) + body.IPv6Addr["HostName"] = hostName + + return api.request(func(res *sacloud.Response) error { + return api.baseAPI.request(method, uri, body, res) + }) +} + +// Delete 削除 +func (api *IPv6AddrAPI) Delete(ip string) (*sacloud.IPv6Addr, error) { + + type request struct { + // IPv6Addr + IPv6Addr map[string]string + } + + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%s", api.getResourceURL(), ip) + body = &request{IPv6Addr: map[string]string{}} + ) + + return api.request(func(res *sacloud.Response) error { + return api.baseAPI.request(method, uri, body, res) + }) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ipv6addr_gen.go b/vendor/github.com/sacloud/libsacloud/api/ipv6addr_gen.go new file mode 100644 index 000000000..132568dc7 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ipv6addr_gen.go @@ -0,0 +1,232 @@ +package api + +/************************************************ + generated by IDE. for [IPv6AddrAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *IPv6AddrAPI) Reset() *IPv6AddrAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *IPv6AddrAPI) Offset(offset int) *IPv6AddrAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *IPv6AddrAPI) Limit(limit int) *IPv6AddrAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *IPv6AddrAPI) Include(key string) *IPv6AddrAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *IPv6AddrAPI) Exclude(key string) *IPv6AddrAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *IPv6AddrAPI) FilterBy(key string, value interface{}) *IPv6AddrAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IPv6AddrAPI) FilterMultiBy(key string, value interface{}) *IPv6AddrAPI { + api.filterBy(key, value, true) + return api +} + +//func (api *IPv6AddrAPI) WithNameLike(name string) *IPv6AddrAPI { +// return api.FilterBy("Name", name) +//} + +//func (api *IPv6AddrAPI) WithTag(tag string) *IPv6AddrAPI { +// return api.FilterBy("Tags.Name", tag) +//} +//func (api *IPv6AddrAPI) WithTags(tags []string) *IPv6AddrAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} +// +//func (api *IPv6AddrAPI) WithSizeGib(size int) *IPv6AddrAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +//} +// +//func (api *IPv6AddrAPI) WithSharedScope() *IPv6AddrAPI { +// api.FilterBy("Scope", "shared") +// return api +//} +// +//func (api *IPv6AddrAPI) WithUserScope() *IPv6AddrAPI { +// api.FilterBy("Scope", "user") +// return api +//} + +// SortBy 指定キーでのソート +func (api *IPv6AddrAPI) SortBy(key string, reverse bool) *IPv6AddrAPI { + api.sortBy(key, reverse) + return api +} + +//// SortByName +//func (api *IPv6AddrAPI) SortByName(reverse bool) *IPv6AddrAPI { +// api.sortByName(reverse) +// return api +//} + +//func (api *IPv6AddrAPI) SortBySize(reverse bool) *IPv6AddrAPI { +// api.sortBy("SizeMB", reverse) +// return api +//} + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *IPv6AddrAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *IPv6AddrAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *IPv6AddrAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *IPv6AddrAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *IPv6AddrAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *IPv6AddrAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IPv6AddrAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +//func (api *IPv6AddrAPI) SetNameLike(name string) { +//} + +//func (api *IPv6AddrAPI) SetTag(tag string) { +// api.FilterBy("Tags.Name", tag) +//} +//func (api *IPv6AddrAPI) SetTags(tags []string) { +// api.FilterBy("Tags.Name", []interface{}{tags}) +//} +// +//func (api *IPv6AddrAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +//} +// +//func (api *IPv6AddrAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +//} +// +//func (api *IPv6AddrAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +//} + +// SetSortBy 指定キーでのソート +func (api *IPv6AddrAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +//// SetSortByName +//func (api *IPv6AddrAPI) SetSortByName(reverse bool) { +// api.sortByName(reverse) +//} + +//func (api *IPv6AddrAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +//} + +/************************************************ + // To + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *IPv6AddrAPI) Create(value *sacloud.IPv6Addr) (*sacloud.IPv6Addr, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +//} + +//func (api *IPv6AddrAPI) Read(id int64) (*sacloud.IPv6Addr, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +//} + +//func (api *IPv6AddrAPI) Update(id int64, value *sacloud.IPv6Addr) (*sacloud.IPv6Addr, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +//} +// +//func (api *IPv6AddrAPI) Delete(id int64) (*sacloud.IPv6Addr, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +//} +// + +// New 新規作成用パラメーター作成 +func (api *IPv6AddrAPI) New() *sacloud.IPv6Addr { + return sacloud.CreateNewIPv6Addr() +} + +/************************************************ + Inner functions +************************************************/ + +func (api *IPv6AddrAPI) setStateValue(setFunc func(*sacloud.Request)) *IPv6AddrAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *IPv6AddrAPI) request(f func(*sacloud.Response) error) (*sacloud.IPv6Addr, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.IPv6Addr, nil +} + +func (api *IPv6AddrAPI) createRequest(value *sacloud.IPv6Addr) *sacloud.Request { + req := &sacloud.Request{} + req.IPv6Addr = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ipv6net.go b/vendor/github.com/sacloud/libsacloud/api/ipv6net.go new file mode 100644 index 000000000..9eb1c4d5e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ipv6net.go @@ -0,0 +1,18 @@ +package api + +// IPv6NetAPI IPv6ネットワークAPI +type IPv6NetAPI struct { + *baseAPI +} + +// NewIPv6NetAPI IPv6ネットワークAPI作成 +func NewIPv6NetAPI(client *Client) *IPv6NetAPI { + return &IPv6NetAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "ipv6net" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ipv6net_gen.go b/vendor/github.com/sacloud/libsacloud/api/ipv6net_gen.go new file mode 100644 index 000000000..5accb90f7 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ipv6net_gen.go @@ -0,0 +1,229 @@ +package api + +/************************************************ + generated by IDE. for [IPv6NetAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のクリア +func (api *IPv6NetAPI) Reset() *IPv6NetAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *IPv6NetAPI) Offset(offset int) *IPv6NetAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *IPv6NetAPI) Limit(limit int) *IPv6NetAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *IPv6NetAPI) Include(key string) *IPv6NetAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *IPv6NetAPI) Exclude(key string) *IPv6NetAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *IPv6NetAPI) FilterBy(key string, value interface{}) *IPv6NetAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IPv6NetAPI) FilterMultiBy(key string, value interface{}) *IPv6NetAPI { + api.filterBy(key, value, true) + return api +} + +//func (api *IPv6NetAPI) WithNameLike(name string) *IPv6NetAPI { +// return api.FilterBy("Name", name) +//} +// +//func (api *IPv6NetAPI) WithTag(tag string) *IPv6NetAPI { +// return api.FilterBy("Tags.Name", tag) +//} +//func (api *IPv6NetAPI) WithTags(tags []string) *IPv6NetAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *IPv6NetAPI) WithSizeGib(size int) *IPv6NetAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *IPv6NetAPI) WithSharedScope() *IPv6NetAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *IPv6NetAPI) WithUserScope() *IPv6NetAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *IPv6NetAPI) SortBy(key string, reverse bool) *IPv6NetAPI { + api.sortBy(key, reverse) + return api +} + +//func (api *IPv6NetAPI) SortByName(reverse bool) *IPv6NetAPI { +// api.sortByName(reverse) +// return api +//} + +// func (api *IPv6NetAPI) SortBySize(reverse bool) *IPv6NetAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のクリア +func (api *IPv6NetAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *IPv6NetAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *IPv6NetAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *IPv6NetAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *IPv6NetAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *IPv6NetAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *IPv6NetAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +//func (api *IPv6NetAPI) SetNameLike(name string) { +// api.FilterBy("Name", name) +//} +// +//func (api *IPv6NetAPI) SetTag(tag string) { +// api.FilterBy("Tags.Name", tag) +//} +//func (api *IPv6NetAPI) SetTags(tags []string) { +// api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *IPv6NetAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *IPv6NetAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *IPv6NetAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *IPv6NetAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +//func (api *IPv6NetAPI) SetSortByName(reverse bool) { +// api.sortByName(reverse) +//} + +// func (api *IPv6NetAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *IPv6NetAPI) New() *sacloud.IPv6Net { +// return &sacloud.IPv6Net{} +//} + +//func (api *IPv6NetAPI) Create(value *sacloud.IPv6Net) (*sacloud.IPv6Net, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +//} + +// Read 読み取り +func (api *IPv6NetAPI) Read(id int64) (*sacloud.IPv6Net, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +//func (api *IPv6NetAPI) Update(id int64, value *sacloud.IPv6Net) (*sacloud.IPv6Net, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +//} +// +//func (api *IPv6NetAPI) Delete(id int64) (*sacloud.IPv6Net, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +//} + +/************************************************ + Inner functions +************************************************/ + +func (api *IPv6NetAPI) setStateValue(setFunc func(*sacloud.Request)) *IPv6NetAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *IPv6NetAPI) request(f func(*sacloud.Response) error) (*sacloud.IPv6Net, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.IPv6Net, nil +} + +func (api *IPv6NetAPI) createRequest(value *sacloud.IPv6Net) *sacloud.Request { + req := &sacloud.Request{} + req.IPv6Net = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/license.go b/vendor/github.com/sacloud/libsacloud/api/license.go new file mode 100644 index 000000000..dc2811ceb --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/license.go @@ -0,0 +1,18 @@ +package api + +// LicenseAPI ライセンスAPI +type LicenseAPI struct { + *baseAPI +} + +// NewLicenseAPI ライセンスAPI作成 +func NewLicenseAPI(client *Client) *LicenseAPI { + return &LicenseAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "license" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/license_gen.go b/vendor/github.com/sacloud/libsacloud/api/license_gen.go new file mode 100644 index 000000000..0c38524a7 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/license_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [LicenseAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *LicenseAPI) Reset() *LicenseAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *LicenseAPI) Offset(offset int) *LicenseAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *LicenseAPI) Limit(limit int) *LicenseAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *LicenseAPI) Include(key string) *LicenseAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *LicenseAPI) Exclude(key string) *LicenseAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *LicenseAPI) FilterBy(key string, value interface{}) *LicenseAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *LicenseAPI) FilterMultiBy(key string, value interface{}) *LicenseAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *LicenseAPI) WithNameLike(name string) *LicenseAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *LicenseAPI) WithTag(tag string) *LicenseAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *LicenseAPI) WithTags(tags []string) *LicenseAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *LicenseAPI) WithSizeGib(size int) *LicenseAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *LicenseAPI) WithSharedScope() *LicenseAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *LicenseAPI) WithUserScope() *LicenseAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *LicenseAPI) SortBy(key string, reverse bool) *LicenseAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *LicenseAPI) SortByName(reverse bool) *LicenseAPI { + api.sortByName(reverse) + return api +} + +// func (api *LicenseAPI) SortBySize(reverse bool) *LicenseAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *LicenseAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *LicenseAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *LicenseAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *LicenseAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *LicenseAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *LicenseAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *LicenseAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *LicenseAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *LicenseAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *LicenseAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *LicenseAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *LicenseAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *LicenseAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *LicenseAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *LicenseAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *LicenseAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *LicenseAPI) New() *sacloud.License { + return &sacloud.License{} +} + +// Create 新規作成 +func (api *LicenseAPI) Create(value *sacloud.License) (*sacloud.License, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *LicenseAPI) Read(id int64) (*sacloud.License, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *LicenseAPI) Update(id int64, value *sacloud.License) (*sacloud.License, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *LicenseAPI) Delete(id int64) (*sacloud.License, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *LicenseAPI) setStateValue(setFunc func(*sacloud.Request)) *LicenseAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *LicenseAPI) request(f func(*sacloud.Response) error) (*sacloud.License, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.License, nil +} + +func (api *LicenseAPI) createRequest(value *sacloud.License) *sacloud.Request { + req := &sacloud.Request{} + req.License = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/load_balancer.go b/vendor/github.com/sacloud/libsacloud/api/load_balancer.go new file mode 100644 index 000000000..c5873c696 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/load_balancer.go @@ -0,0 +1,232 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +//HACK: さくらのAPI側仕様: Applianceの内容によってJSONフォーマットが異なるため +// ロードバランサ/VPCルータそれぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchLoadBalancerResponse ロードバランサー検索レスポンス +type SearchLoadBalancerResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // LoadBalancers ロードバランサー リスト + LoadBalancers []sacloud.LoadBalancer `json:"Appliances,omitempty"` +} + +type loadBalancerRequest struct { + LoadBalancer *sacloud.LoadBalancer `json:"Appliance,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type loadBalancerResponse struct { + *sacloud.ResultFlagValue + *sacloud.LoadBalancer `json:"Appliance,omitempty"` + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} +} + +// LoadBalancerAPI ロードバランサーAPI +type LoadBalancerAPI struct { + *baseAPI +} + +// NewLoadBalancerAPI ロードバランサーAPI作成 +func NewLoadBalancerAPI(client *Client) *LoadBalancerAPI { + return &LoadBalancerAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "appliance" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Class", "loadbalancer") + return res + }, + }, + } +} + +// Find 検索 +func (api *LoadBalancerAPI) Find() (*SearchLoadBalancerResponse, error) { + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchLoadBalancerResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *LoadBalancerAPI) request(f func(*loadBalancerResponse) error) (*sacloud.LoadBalancer, error) { + res := &loadBalancerResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.LoadBalancer, nil +} + +func (api *LoadBalancerAPI) createRequest(value *sacloud.LoadBalancer) *loadBalancerResponse { + return &loadBalancerResponse{LoadBalancer: value} +} + +//func (api *LoadBalancerAPI) New() *sacloud.LoadBalancer { +// return sacloud.CreateNewLoadBalancer() +//} + +// Create 新規作成 +func (api *LoadBalancerAPI) Create(value *sacloud.LoadBalancer) (*sacloud.LoadBalancer, error) { + return api.request(func(res *loadBalancerResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *LoadBalancerAPI) Read(id int64) (*sacloud.LoadBalancer, error) { + return api.request(func(res *loadBalancerResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *LoadBalancerAPI) Update(id int64, value *sacloud.LoadBalancer) (*sacloud.LoadBalancer, error) { + return api.request(func(res *loadBalancerResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *LoadBalancerAPI) Delete(id int64) (*sacloud.LoadBalancer, error) { + return api.request(func(res *loadBalancerResponse) error { + return api.delete(id, nil, res) + }) +} + +// Config 設定変更の反映 +func (api *LoadBalancerAPI) Config(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// IsUp 起動しているか判定 +func (api *LoadBalancerAPI) IsUp(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsUp(), nil +} + +// IsDown ダウンしているか判定 +func (api *LoadBalancerAPI) IsDown(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsDown(), nil +} + +// Boot 起動 +func (api *LoadBalancerAPI) Boot(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Shutdown シャットダウン(graceful) +func (api *LoadBalancerAPI) Shutdown(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Stop シャットダウン(force) +func (api *LoadBalancerAPI) Stop(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"Force": true}) +} + +// RebootForce 再起動 +func (api *LoadBalancerAPI) RebootForce(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// ResetForce リセット +func (api *LoadBalancerAPI) ResetForce(id int64, recycleProcess bool) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"RecycleProcess": recycleProcess}) +} + +// SleepUntilUp 起動するまで待機 +func (api *LoadBalancerAPI) SleepUntilUp(id int64, timeout time.Duration) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepUntilDown ダウンするまで待機 +func (api *LoadBalancerAPI) SleepUntilDown(id int64, timeout time.Duration) error { + handler := waitingForDownFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepWhileCopying コピー終了まで待機 +func (api *LoadBalancerAPI) SleepWhileCopying(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *LoadBalancerAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return poll(handler, timeout) +} + +// Monitor アクティビティーモニター取得 +func (api *LoadBalancerAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "interface", 0, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/load_balancer_gen.go b/vendor/github.com/sacloud/libsacloud/api/load_balancer_gen.go new file mode 100644 index 000000000..52c4e7bd9 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/load_balancer_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [LoadBalancerAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *LoadBalancerAPI) Reset() *LoadBalancerAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *LoadBalancerAPI) Offset(offset int) *LoadBalancerAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *LoadBalancerAPI) Limit(limit int) *LoadBalancerAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *LoadBalancerAPI) Include(key string) *LoadBalancerAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *LoadBalancerAPI) Exclude(key string) *LoadBalancerAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *LoadBalancerAPI) FilterBy(key string, value interface{}) *LoadBalancerAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *LoadBalancerAPI) FilterMultiBy(key string, value interface{}) *LoadBalancerAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *LoadBalancerAPI) WithNameLike(name string) *LoadBalancerAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *LoadBalancerAPI) WithTag(tag string) *LoadBalancerAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *LoadBalancerAPI) WithTags(tags []string) *LoadBalancerAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *LoadBalancerAPI) WithSizeGib(size int) *LoadBalancerAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *LoadBalancerAPI) WithSharedScope() *LoadBalancerAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *LoadBalancerAPI) WithUserScope() *LoadBalancerAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *LoadBalancerAPI) SortBy(key string, reverse bool) *LoadBalancerAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *LoadBalancerAPI) SortByName(reverse bool) *LoadBalancerAPI { + api.sortByName(reverse) + return api +} + +// func (api *LoadBalancerAPI) SortBySize(reverse bool) *LoadBalancerAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *LoadBalancerAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *LoadBalancerAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *LoadBalancerAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *LoadBalancerAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *LoadBalancerAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *LoadBalancerAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *LoadBalancerAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *LoadBalancerAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *LoadBalancerAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *LoadBalancerAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *LoadBalancerAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *LoadBalancerAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *LoadBalancerAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *LoadBalancerAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *LoadBalancerAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *LoadBalancerAPI) SortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *LoadBalancerAPI) New() *sacloud.LoadBalancer { +// return &sacloud.LoadBalancer{} +// } + +// func (api *LoadBalancerAPI) Create(value *sacloud.LoadBalancer) (*sacloud.LoadBalancer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *LoadBalancerAPI) Read(id string) (*sacloud.LoadBalancer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *LoadBalancerAPI) Update(id string, value *sacloud.LoadBalancer) (*sacloud.LoadBalancer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *LoadBalancerAPI) Delete(id string) (*sacloud.LoadBalancer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *LoadBalancerAPI) setStateValue(setFunc func(*sacloud.Request)) *LoadBalancerAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *LoadBalancerAPI) request(f func(*sacloud.Response) error) (*sacloud.LoadBalancer, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.LoadBalancer, nil +//} +// +//func (api *LoadBalancerAPI) createRequest(value *sacloud.LoadBalancer) *sacloud.Request { +// req := &sacloud.Request{} +// req.LoadBalancer = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go b/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go new file mode 100644 index 000000000..3ead7f735 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/mobile_gateway.go @@ -0,0 +1,414 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +// SearchMobileGatewayResponse モバイルゲートウェイ検索レスポンス +type SearchMobileGatewayResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // MobileGateways モバイルゲートウェイ リスト + MobileGateways []sacloud.MobileGateway `json:"Appliances,omitempty"` +} + +// MobileGatewaySIMRequest SIM一覧取得リクエスト +type MobileGatewaySIMRequest struct { + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type mobileGatewayResponse struct { + *sacloud.ResultFlagValue + *sacloud.MobileGateway `json:"Appliance,omitempty"` + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} +} + +type mobileGatewaySIMResponse struct { + *sacloud.ResultFlagValue + SIM []sacloud.SIMInfo `json:"sim,omitempty"` + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} +} + +// MobileGatewayAPI モバイルゲートウェイAPI +type MobileGatewayAPI struct { + *baseAPI +} + +// NewMobileGatewayAPI モバイルゲートウェイAPI作成 +func NewMobileGatewayAPI(client *Client) *MobileGatewayAPI { + return &MobileGatewayAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "appliance" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Class", "mobilegateway") + return res + }, + }, + } +} + +// Find 検索 +func (api *MobileGatewayAPI) Find() (*SearchMobileGatewayResponse, error) { + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchMobileGatewayResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *MobileGatewayAPI) request(f func(*mobileGatewayResponse) error) (*sacloud.MobileGateway, error) { + res := &mobileGatewayResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.MobileGateway, nil +} + +func (api *MobileGatewayAPI) createRequest(value *sacloud.MobileGateway) *mobileGatewayResponse { + return &mobileGatewayResponse{MobileGateway: value} +} + +// Create 新規作成 +func (api *MobileGatewayAPI) Create(value *sacloud.MobileGateway) (*sacloud.MobileGateway, error) { + return api.request(func(res *mobileGatewayResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *MobileGatewayAPI) Read(id int64) (*sacloud.MobileGateway, error) { + return api.request(func(res *mobileGatewayResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *MobileGatewayAPI) Update(id int64, value *sacloud.MobileGateway) (*sacloud.MobileGateway, error) { + return api.request(func(res *mobileGatewayResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// UpdateSetting 設定更新 +func (api *MobileGatewayAPI) UpdateSetting(id int64, value *sacloud.MobileGateway) (*sacloud.MobileGateway, error) { + req := &sacloud.MobileGateway{ + // Settings + Settings: value.Settings, + } + return api.request(func(res *mobileGatewayResponse) error { + return api.update(id, api.createRequest(req), res) + }) +} + +// Delete 削除 +func (api *MobileGatewayAPI) Delete(id int64) (*sacloud.MobileGateway, error) { + return api.request(func(res *mobileGatewayResponse) error { + return api.delete(id, nil, res) + }) +} + +// Config 設定変更の反映 +func (api *MobileGatewayAPI) Config(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// IsUp 起動しているか判定 +func (api *MobileGatewayAPI) IsUp(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsUp(), nil +} + +// IsDown ダウンしているか判定 +func (api *MobileGatewayAPI) IsDown(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsDown(), nil +} + +// Boot 起動 +func (api *MobileGatewayAPI) Boot(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Shutdown シャットダウン(graceful) +func (api *MobileGatewayAPI) Shutdown(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Stop シャットダウン(force) +func (api *MobileGatewayAPI) Stop(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"Force": true}) +} + +// RebootForce 再起動 +func (api *MobileGatewayAPI) RebootForce(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// ResetForce リセット +func (api *MobileGatewayAPI) ResetForce(id int64, recycleProcess bool) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"RecycleProcess": recycleProcess}) +} + +// SleepUntilUp 起動するまで待機 +func (api *MobileGatewayAPI) SleepUntilUp(id int64, timeout time.Duration) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepUntilDown ダウンするまで待機 +func (api *MobileGatewayAPI) SleepUntilDown(id int64, timeout time.Duration) error { + handler := waitingForDownFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepWhileCopying コピー終了まで待機 +func (api *MobileGatewayAPI) SleepWhileCopying(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *MobileGatewayAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return poll(handler, timeout) +} + +// ConnectToSwitch 指定のインデックス位置のNICをスイッチへ接続 +func (api *MobileGatewayAPI) ConnectToSwitch(id int64, switchID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/interface/%d/to/switch/%d", api.getResourceURL(), id, 1, switchID) + ) + return api.modify(method, uri, nil) +} + +// DisconnectFromSwitch 指定のインデックス位置のNICをスイッチから切断 +func (api *MobileGatewayAPI) DisconnectFromSwitch(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/interface/%d/to/switch", api.getResourceURL(), id, 1) + ) + return api.modify(method, uri, nil) +} + +// GetDNS DNSサーバ設定 取得 +func (api *MobileGatewayAPI) GetDNS(id int64) (*sacloud.MobileGatewayResolver, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/mobilegateway/dnsresolver", api.getResourceURL(), id) + ) + + data, err := api.client.newRequest(method, uri, nil) + if err != nil { + return nil, err + } + var res sacloud.MobileGatewayResolver + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, err +} + +// SetDNS DNSサーバ設定 +func (api *MobileGatewayAPI) SetDNS(id int64, dns *sacloud.MobileGatewayResolver) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/mobilegateway/dnsresolver", api.getResourceURL(), id) + ) + + return api.modify(method, uri, dns) +} + +// GetSIMRoutes SIMルート 取得 +func (api *MobileGatewayAPI) GetSIMRoutes(id int64) ([]*sacloud.MobileGatewaySIMRoute, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/mobilegateway/simroutes", api.getResourceURL(), id) + ) + + data, err := api.client.newRequest(method, uri, nil) + if err != nil { + return nil, err + } + var res sacloud.MobileGatewaySIMRoutes + + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return res.SIMRoutes, err +} + +// SetSIMRoutes SIMルート 設定 +func (api *MobileGatewayAPI) SetSIMRoutes(id int64, simRoutes *sacloud.MobileGatewaySIMRoutes) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/mobilegateway/simroutes", api.getResourceURL(), id) + ) + + return api.modify(method, uri, simRoutes) +} + +// AddSIMRoute SIMルート 個別追加 +func (api *MobileGatewayAPI) AddSIMRoute(id int64, simID int64, prefix string) (bool, error) { + + routes, err := api.GetSIMRoutes(id) + if err != nil { + return false, err + } + + param := &sacloud.MobileGatewaySIMRoutes{ + SIMRoutes: routes, + } + added := param.AddSIMRoute(simID, prefix) + if !added { + return false, nil + } + + return api.SetSIMRoutes(id, param) +} + +// DeleteSIMRoute SIMルート 個別削除 +func (api *MobileGatewayAPI) DeleteSIMRoute(id int64, simID int64, prefix string) (bool, error) { + + routes, err := api.GetSIMRoutes(id) + if err != nil { + return false, err + } + + param := &sacloud.MobileGatewaySIMRoutes{ + SIMRoutes: routes, + } + deleted := param.DeleteSIMRoute(simID, prefix) + if !deleted { + return false, nil + } + + _, err = api.SetSIMRoutes(id, param) + return deleted, err +} + +// DeleteSIMRoutes SIMルート 全件削除 +func (api *MobileGatewayAPI) DeleteSIMRoutes(id int64) (bool, error) { + return api.SetSIMRoutes(id, &sacloud.MobileGatewaySIMRoutes{ + SIMRoutes: []*sacloud.MobileGatewaySIMRoute{}, + }) +} + +// ListSIM SIM一覧取得 +func (api *MobileGatewayAPI) ListSIM(id int64, req *MobileGatewaySIMRequest) ([]sacloud.SIMInfo, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/mobilegateway/sims", api.getResourceURL(), id) + ) + + data, err := api.client.newRequest(method, uri, req) + if err != nil { + return nil, err + } + var res mobileGatewaySIMResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return res.SIM, nil +} + +// AddSIM SIM登録 +func (api *MobileGatewayAPI) AddSIM(id int64, simID int64) (bool, error) { + var ( + method = "POST" + uri = fmt.Sprintf("%s/%d/mobilegateway/sims", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]interface{}{ + "sim": map[string]interface{}{ + "resource_id": fmt.Sprintf("%d", simID), + }, + }) +} + +// DeleteSIM SIM登録 +func (api *MobileGatewayAPI) DeleteSIM(id int64, simID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/mobilegateway/sims/%d", api.getResourceURL(), id, simID) + ) + return api.modify(method, uri, nil) +} + +// Logs セッションログ取得(複数SIM) +func (api *MobileGatewayAPI) Logs(id int64, body interface{}) ([]sacloud.SIMLog, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/mobilegateway/sessionlog", api.getResourceURL(), id) + ) + + res := &simLogResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Logs, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/mobile_gateway_gen.go b/vendor/github.com/sacloud/libsacloud/api/mobile_gateway_gen.go new file mode 100644 index 000000000..d7123924c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/mobile_gateway_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [MobileGatewayAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *MobileGatewayAPI) Reset() *MobileGatewayAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *MobileGatewayAPI) Offset(offset int) *MobileGatewayAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *MobileGatewayAPI) Limit(limit int) *MobileGatewayAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *MobileGatewayAPI) Include(key string) *MobileGatewayAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *MobileGatewayAPI) Exclude(key string) *MobileGatewayAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *MobileGatewayAPI) FilterBy(key string, value interface{}) *MobileGatewayAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *MobileGatewayAPI) FilterMultiBy(key string, value interface{}) *MobileGatewayAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *MobileGatewayAPI) WithNameLike(name string) *MobileGatewayAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *MobileGatewayAPI) WithTag(tag string) *MobileGatewayAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *MobileGatewayAPI) WithTags(tags []string) *MobileGatewayAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *MobileGatewayAPI) WithSizeGib(size int) *MobileGatewayAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *MobileGatewayAPI) WithSharedScope() *MobileGatewayAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *MobileGatewayAPI) WithUserScope() *MobileGatewayAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *MobileGatewayAPI) SortBy(key string, reverse bool) *MobileGatewayAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *MobileGatewayAPI) SortByName(reverse bool) *MobileGatewayAPI { + api.sortByName(reverse) + return api +} + +// func (api *MobileGatewayAPI) SortBySize(reverse bool) *MobileGatewayAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *MobileGatewayAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *MobileGatewayAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *MobileGatewayAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *MobileGatewayAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *MobileGatewayAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *MobileGatewayAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *MobileGatewayAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *MobileGatewayAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *MobileGatewayAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *MobileGatewayAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *MobileGatewayAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *MobileGatewayAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *MobileGatewayAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *MobileGatewayAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *MobileGatewayAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *MobileGatewayAPI) SortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *MobileGatewayAPI) New() *sacloud.MobileGateway { +// return &sacloud.MobileGateway{} +// } + +// func (api *MobileGatewayAPI) Create(value *sacloud.MobileGateway) (*sacloud.MobileGateway, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *MobileGatewayAPI) Read(id string) (*sacloud.MobileGateway, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *MobileGatewayAPI) Update(id string, value *sacloud.MobileGateway) (*sacloud.MobileGateway, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *MobileGatewayAPI) Delete(id string) (*sacloud.MobileGateway, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *MobileGatewayAPI) setStateValue(setFunc func(*sacloud.Request)) *MobileGatewayAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *MobileGatewayAPI) request(f func(*sacloud.Response) error) (*sacloud.MobileGateway, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.MobileGateway, nil +//} +// +//func (api *MobileGatewayAPI) createRequest(value *sacloud.MobileGateway) *sacloud.Request { +// req := &sacloud.Request{} +// req.MobileGateway = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/newsfeed.go b/vendor/github.com/sacloud/libsacloud/api/newsfeed.go new file mode 100644 index 000000000..4adab1caf --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/newsfeed.go @@ -0,0 +1,50 @@ +package api + +import ( + "encoding/json" + "github.com/sacloud/libsacloud/sacloud" +) + +// NewsFeedAPI フィード(障害/メンテナンス情報)API +type NewsFeedAPI struct { + client *Client +} + +// NewsFeedURL フィード取得URL +var NewsFeedURL = "https://secure.sakura.ad.jp/rss/sakuranews/getfeeds.php?service=cloud&format=json" + +// NewNewsFeedAPI フィード(障害/メンテナンス情報)API +func NewNewsFeedAPI(client *Client) *NewsFeedAPI { + return &NewsFeedAPI{ + client: client, + } +} + +// GetFeed フィード全件取得 +func (api *NewsFeedAPI) GetFeed() ([]sacloud.NewsFeed, error) { + + var res = []sacloud.NewsFeed{} + data, err := api.client.newRequest("GET", NewsFeedURL, nil) + if err != nil { + return res, err + } + + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return res, nil +} + +// GetFeedByURL 指定のURLを持つフィードを取得 +func (api *NewsFeedAPI) GetFeedByURL(url string) (*sacloud.NewsFeed, error) { + res, err := api.GetFeed() + if err != nil { + return nil, err + } + for _, r := range res { + if r.URL == url { + return &r, nil + } + } + return nil, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/nfs.go b/vendor/github.com/sacloud/libsacloud/api/nfs.go new file mode 100644 index 000000000..f044fc86a --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/nfs.go @@ -0,0 +1,234 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +// SearchNFSResponse NFS検索レスポンス +type SearchNFSResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // NFSs NFS リスト + NFS []sacloud.NFS `json:"Appliances,omitempty"` +} + +type nfsRequest struct { + NFS *sacloud.NFS `json:"Appliance,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type nfsResponse struct { + *sacloud.ResultFlagValue + *sacloud.NFS `json:"Appliance,omitempty"` + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} +} + +// NFSAPI NFSAPI +type NFSAPI struct { + *baseAPI +} + +// NewNFSAPI NFSAPI作成 +func NewNFSAPI(client *Client) *NFSAPI { + return &NFSAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "appliance" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Class", "nfs") + return res + }, + }, + } +} + +// Find 検索 +func (api *NFSAPI) Find() (*SearchNFSResponse, error) { + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchNFSResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *NFSAPI) request(f func(*nfsResponse) error) (*sacloud.NFS, error) { + res := &nfsResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.NFS, nil +} + +func (api *NFSAPI) createRequest(value *sacloud.NFS) *nfsResponse { + return &nfsResponse{NFS: value} +} + +//func (api *NFSAPI) New() *sacloud.NFS { +// return sacloud.CreateNewNFS() +//} + +// Create 新規作成 +func (api *NFSAPI) Create(value *sacloud.NFS) (*sacloud.NFS, error) { + return api.request(func(res *nfsResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *NFSAPI) Read(id int64) (*sacloud.NFS, error) { + return api.request(func(res *nfsResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *NFSAPI) Update(id int64, value *sacloud.NFS) (*sacloud.NFS, error) { + return api.request(func(res *nfsResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *NFSAPI) Delete(id int64) (*sacloud.NFS, error) { + return api.request(func(res *nfsResponse) error { + return api.delete(id, nil, res) + }) +} + +// Config 設定変更の反映 +func (api *NFSAPI) Config(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// IsUp 起動しているか判定 +func (api *NFSAPI) IsUp(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsUp(), nil +} + +// IsDown ダウンしているか判定 +func (api *NFSAPI) IsDown(id int64) (bool, error) { + lb, err := api.Read(id) + if err != nil { + return false, err + } + return lb.Instance.IsDown(), nil +} + +// Boot 起動 +func (api *NFSAPI) Boot(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Shutdown シャットダウン(graceful) +func (api *NFSAPI) Shutdown(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Stop シャットダウン(force) +func (api *NFSAPI) Stop(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"Force": true}) +} + +// RebootForce 再起動 +func (api *NFSAPI) RebootForce(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// ResetForce リセット +func (api *NFSAPI) ResetForce(id int64, recycleProcess bool) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"RecycleProcess": recycleProcess}) +} + +// SleepUntilUp 起動するまで待機 +func (api *NFSAPI) SleepUntilUp(id int64, timeout time.Duration) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepUntilDown ダウンするまで待機 +func (api *NFSAPI) SleepUntilDown(id int64, timeout time.Duration) error { + handler := waitingForDownFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepWhileCopying コピー終了まで待機 +func (api *NFSAPI) SleepWhileCopying(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *NFSAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return poll(handler, timeout) +} + +// MonitorNFS NFS固有項目アクティビティモニター取得 +func (api *NFSAPI) MonitorNFS(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "nfs", 0, body) +} + +// MonitorInterface NICアクティビティーモニター取得 +func (api *NFSAPI) MonitorInterface(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "interface", 0, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/nfs_gen.go b/vendor/github.com/sacloud/libsacloud/api/nfs_gen.go new file mode 100644 index 000000000..99a7f9303 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/nfs_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [NFSAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *NFSAPI) Reset() *NFSAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *NFSAPI) Offset(offset int) *NFSAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *NFSAPI) Limit(limit int) *NFSAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *NFSAPI) Include(key string) *NFSAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *NFSAPI) Exclude(key string) *NFSAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *NFSAPI) FilterBy(key string, value interface{}) *NFSAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *NFSAPI) FilterMultiBy(key string, value interface{}) *NFSAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *NFSAPI) WithNameLike(name string) *NFSAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *NFSAPI) WithTag(tag string) *NFSAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *NFSAPI) WithTags(tags []string) *NFSAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *NFSAPI) WithSizeGib(size int) *NFSAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *NFSAPI) WithSharedScope() *NFSAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *NFSAPI) WithUserScope() *NFSAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *NFSAPI) SortBy(key string, reverse bool) *NFSAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *NFSAPI) SortByName(reverse bool) *NFSAPI { + api.sortByName(reverse) + return api +} + +// func (api *NFSAPI) SortBySize(reverse bool) *NFSAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *NFSAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *NFSAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *NFSAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *NFSAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *NFSAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *NFSAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *NFSAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *NFSAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *NFSAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *NFSAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *NFSAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *NFSAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *NFSAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *NFSAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *NFSAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *NFSAPI) SortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *NFSAPI) New() *sacloud.NFS { +// return &sacloud.NFS{} +// } + +// func (api *NFSAPI) Create(value *sacloud.NFS) (*sacloud.NFS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *NFSAPI) Read(id string) (*sacloud.NFS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *NFSAPI) Update(id string, value *sacloud.NFS) (*sacloud.NFS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *NFSAPI) Delete(id string) (*sacloud.NFS, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *NFSAPI) setStateValue(setFunc func(*sacloud.Request)) *NFSAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *NFSAPI) request(f func(*sacloud.Response) error) (*sacloud.NFS, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.NFS, nil +//} +// +//func (api *NFSAPI) createRequest(value *sacloud.NFS) *sacloud.Request { +// req := &sacloud.Request{} +// req.NFS = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/note.go b/vendor/github.com/sacloud/libsacloud/api/note.go new file mode 100644 index 000000000..6869a3f37 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/note.go @@ -0,0 +1,18 @@ +package api + +// NoteAPI スタートアップスクリプトAPI +type NoteAPI struct { + *baseAPI +} + +// NewNoteAPI スタートアップスクリプトAPI作成 +func NewNoteAPI(client *Client) *NoteAPI { + return &NoteAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "note" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/note_gen.go b/vendor/github.com/sacloud/libsacloud/api/note_gen.go new file mode 100644 index 000000000..34579e947 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/note_gen.go @@ -0,0 +1,247 @@ +package api + +/************************************************ + generated by IDE. for [NoteAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *NoteAPI) Reset() *NoteAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *NoteAPI) Offset(offset int) *NoteAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *NoteAPI) Limit(limit int) *NoteAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *NoteAPI) Include(key string) *NoteAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *NoteAPI) Exclude(key string) *NoteAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *NoteAPI) FilterBy(key string, value interface{}) *NoteAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *NoteAPI) FilterMultiBy(key string, value interface{}) *NoteAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *NoteAPI) WithNameLike(name string) *NoteAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *NoteAPI) WithTag(tag string) *NoteAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *NoteAPI) WithTags(tags []string) *NoteAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *NoteAPI) WithSizeGib(size int) *NoteAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// WithSharedScope 公開スコープ条件 +func (api *NoteAPI) WithSharedScope() *NoteAPI { + api.FilterBy("Scope", "shared") + return api +} + +// WithUserScope ユーザースコープ条件 +func (api *NoteAPI) WithUserScope() *NoteAPI { + api.FilterBy("Scope", "user") + return api +} + +// SortBy 指定キーでのソート +func (api *NoteAPI) SortBy(key string, reverse bool) *NoteAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *NoteAPI) SortByName(reverse bool) *NoteAPI { + api.sortByName(reverse) + return api +} + +// func (api *NoteAPI) SortBySize(reverse bool) *NoteAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *NoteAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *NoteAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *NoteAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *NoteAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *NoteAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *NoteAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *NoteAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *NoteAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *NoteAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *NoteAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *NoteAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// SetSharedScope 公開スコープ条件 +func (api *NoteAPI) SetSharedScope() { + api.FilterBy("Scope", "shared") +} + +// SetUserScope ユーザースコープ条件 +func (api *NoteAPI) SetUserScope() { + api.FilterBy("Scope", "user") +} + +// SetSortBy 指定キーでのソート +func (api *NoteAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *NoteAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *NoteAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *NoteAPI) New() *sacloud.Note { + return &sacloud.Note{} +} + +// Create 新規作成 +func (api *NoteAPI) Create(value *sacloud.Note) (*sacloud.Note, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *NoteAPI) Read(id int64) (*sacloud.Note, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *NoteAPI) Update(id int64, value *sacloud.Note) (*sacloud.Note, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *NoteAPI) Delete(id int64) (*sacloud.Note, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *NoteAPI) setStateValue(setFunc func(*sacloud.Request)) *NoteAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *NoteAPI) request(f func(*sacloud.Response) error) (*sacloud.Note, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Note, nil +} + +func (api *NoteAPI) createRequest(value *sacloud.Note) *sacloud.Request { + req := &sacloud.Request{} + req.Note = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/packet_filter.go b/vendor/github.com/sacloud/libsacloud/api/packet_filter.go new file mode 100644 index 000000000..1b0b27975 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/packet_filter.go @@ -0,0 +1,18 @@ +package api + +// PacketFilterAPI パケットフィルターAPI +type PacketFilterAPI struct { + *baseAPI +} + +// NewPacketFilterAPI パケットフィルターAPI作成 +func NewPacketFilterAPI(client *Client) *PacketFilterAPI { + return &PacketFilterAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "packetfilter" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/packet_filter_gen.go b/vendor/github.com/sacloud/libsacloud/api/packet_filter_gen.go new file mode 100644 index 000000000..4e2e8f275 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/packet_filter_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [PacketFilterAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *PacketFilterAPI) Reset() *PacketFilterAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *PacketFilterAPI) Offset(offset int) *PacketFilterAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *PacketFilterAPI) Limit(limit int) *PacketFilterAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *PacketFilterAPI) Include(key string) *PacketFilterAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *PacketFilterAPI) Exclude(key string) *PacketFilterAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *PacketFilterAPI) FilterBy(key string, value interface{}) *PacketFilterAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *PacketFilterAPI) FilterMultiBy(key string, value interface{}) *PacketFilterAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *PacketFilterAPI) WithNameLike(name string) *PacketFilterAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *PacketFilterAPI) WithTag(tag string) *PacketFilterAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *PacketFilterAPI) WithTags(tags []string) *PacketFilterAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *PacketFilterAPI) WithSizeGib(size int) *PacketFilterAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *PacketFilterAPI) WithSharedScope() *PacketFilterAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *PacketFilterAPI) WithUserScope() *PacketFilterAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *PacketFilterAPI) SortBy(key string, reverse bool) *PacketFilterAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *PacketFilterAPI) SortByName(reverse bool) *PacketFilterAPI { + api.sortByName(reverse) + return api +} + +// func (api *PacketFilterAPI) SortBySize(reverse bool) *PacketFilterAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *PacketFilterAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *PacketFilterAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *PacketFilterAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *PacketFilterAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *PacketFilterAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *PacketFilterAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *PacketFilterAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *PacketFilterAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *PacketFilterAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *PacketFilterAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *PacketFilterAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *PacketFilterAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *PacketFilterAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *PacketFilterAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *PacketFilterAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *PacketFilterAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *PacketFilterAPI) New() *sacloud.PacketFilter { + return sacloud.CreateNewPacketFilter() +} + +// Create 新規作成 +func (api *PacketFilterAPI) Create(value *sacloud.PacketFilter) (*sacloud.PacketFilter, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *PacketFilterAPI) Read(id int64) (*sacloud.PacketFilter, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *PacketFilterAPI) Update(id int64, value *sacloud.PacketFilter) (*sacloud.PacketFilter, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *PacketFilterAPI) Delete(id int64) (*sacloud.PacketFilter, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *PacketFilterAPI) setStateValue(setFunc func(*sacloud.Request)) *PacketFilterAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *PacketFilterAPI) request(f func(*sacloud.Response) error) (*sacloud.PacketFilter, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.PacketFilter, nil +} + +func (api *PacketFilterAPI) createRequest(value *sacloud.PacketFilter) *sacloud.Request { + req := &sacloud.Request{} + req.PacketFilter = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/polling.go b/vendor/github.com/sacloud/libsacloud/api/polling.go new file mode 100644 index 000000000..bdff6bd43 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/polling.go @@ -0,0 +1,156 @@ +package api + +import ( + "fmt" + "time" +) + +type pollingHandler func() (exit bool, state interface{}, err error) + +func poll(handler pollingHandler, timeout time.Duration) (chan (interface{}), chan (interface{}), chan (error)) { + + compChan := make(chan interface{}) + progChan := make(chan interface{}) + errChan := make(chan error) + + tick := time.Tick(5 * time.Second) + bomb := time.After(timeout) + + go func() { + for { + select { + case <-tick: + exit, state, err := handler() + if err != nil { + errChan <- fmt.Errorf("Failed: poll: %s", err) + return + } + if state != nil { + progChan <- state + if exit { + compChan <- state + return + } + } + case <-bomb: + errChan <- fmt.Errorf("Timeout") + return + } + } + }() + return compChan, progChan, errChan +} + +func blockingPoll(handler pollingHandler, timeout time.Duration) error { + c, p, e := poll(handler, timeout) + for { + select { + case <-c: + return nil + case <-p: + // noop + case err := <-e: + return err + } + } +} + +type hasAvailable interface { + IsAvailable() bool +} +type hasFailed interface { + IsFailed() bool +} + +func waitingForAvailableFunc(readFunc func() (hasAvailable, error), maxRetry int) func() (bool, interface{}, error) { + counter := 0 + return func() (bool, interface{}, error) { + counter++ + v, err := readFunc() + if err != nil { + if maxRetry > 0 && counter < maxRetry { + return false, nil, nil + } + return false, nil, err + } + if v == nil { + return false, nil, fmt.Errorf("readFunc returns nil") + } + + if v.IsAvailable() { + return true, v, nil + } + if f, ok := v.(hasFailed); ok && f.IsFailed() { + return false, v, fmt.Errorf("InstanceState is failed: %#v", v) + } + + return false, v, nil + } +} + +type hasUpDown interface { + IsUp() bool + IsDown() bool +} + +func waitingForUpFunc(readFunc func() (hasUpDown, error), maxRetry int) func() (bool, interface{}, error) { + counter := 0 + return func() (bool, interface{}, error) { + counter++ + v, err := readFunc() + if err != nil { + if maxRetry > 0 && counter < maxRetry { + return false, nil, nil + } + return false, nil, err + } + if v == nil { + return false, nil, fmt.Errorf("readFunc returns nil") + } + + if v.IsUp() { + return true, v, nil + } + return false, v, nil + } +} + +func waitingForDownFunc(readFunc func() (hasUpDown, error), maxRetry int) func() (bool, interface{}, error) { + counter := 0 + return func() (bool, interface{}, error) { + counter++ + v, err := readFunc() + if err != nil { + if maxRetry > 0 && counter < maxRetry { + return false, nil, nil + } + return false, nil, err + } + if v == nil { + return false, nil, fmt.Errorf("readFunc returns nil") + } + + if v.IsDown() { + return true, v, nil + } + return false, v, nil + } +} + +func waitingForReadFunc(readFunc func() (interface{}, error), maxRetry int) func() (bool, interface{}, error) { + counter := 0 + return func() (bool, interface{}, error) { + counter++ + v, err := readFunc() + if err != nil { + if maxRetry > 0 && counter < maxRetry { + return false, nil, nil + } + return false, nil, err + } + if v != nil { + return true, v, nil + } + return false, v, nil + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/private_host.go b/vendor/github.com/sacloud/libsacloud/api/private_host.go new file mode 100644 index 000000000..18bef306d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/private_host.go @@ -0,0 +1,18 @@ +package api + +// PrivateHostAPI 専有ホストAPI +type PrivateHostAPI struct { + *baseAPI +} + +// NewPrivateHostAPI 専有ホストAPI作成 +func NewPrivateHostAPI(client *Client) *PrivateHostAPI { + return &PrivateHostAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "privatehost" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/private_host_gen.go b/vendor/github.com/sacloud/libsacloud/api/private_host_gen.go new file mode 100644 index 000000000..4159dc9d6 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/private_host_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [PrivateHostAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件リセット +func (api *PrivateHostAPI) Reset() *PrivateHostAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *PrivateHostAPI) Offset(offset int) *PrivateHostAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *PrivateHostAPI) Limit(limit int) *PrivateHostAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *PrivateHostAPI) Include(key string) *PrivateHostAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *PrivateHostAPI) Exclude(key string) *PrivateHostAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *PrivateHostAPI) FilterBy(key string, value interface{}) *PrivateHostAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *PrivateHostAPI) FilterMultiBy(key string, value interface{}) *PrivateHostAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *PrivateHostAPI) WithNameLike(name string) *PrivateHostAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *PrivateHostAPI) WithTag(tag string) *PrivateHostAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *PrivateHostAPI) WithTags(tags []string) *PrivateHostAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *PrivateHostAPI) WithSizeGib(size int) *PrivateHostAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *PrivateHostAPI) WithSharedScope() *PrivateHostAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *PrivateHostAPI) WithUserScope() *PrivateHostAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *PrivateHostAPI) SortBy(key string, reverse bool) *PrivateHostAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *PrivateHostAPI) SortByName(reverse bool) *PrivateHostAPI { + api.sortByName(reverse) + return api +} + +// func (api *PrivateHostAPI) SortBySize(reverse bool) *PrivateHostAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件リセット +func (api *PrivateHostAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *PrivateHostAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *PrivateHostAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *PrivateHostAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *PrivateHostAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *PrivateHostAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *PrivateHostAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *PrivateHostAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *PrivateHostAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *PrivateHostAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *PrivateHostAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *PrivateHostAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *PrivateHostAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *PrivateHostAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *PrivateHostAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *PrivateHostAPI) SortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *PrivateHostAPI) New() *sacloud.PrivateHost { + return &sacloud.PrivateHost{} +} + +// Create 新規作成 +func (api *PrivateHostAPI) Create(value *sacloud.PrivateHost) (*sacloud.PrivateHost, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *PrivateHostAPI) Read(id int64) (*sacloud.PrivateHost, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *PrivateHostAPI) Update(id int64, value *sacloud.PrivateHost) (*sacloud.PrivateHost, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *PrivateHostAPI) Delete(id int64) (*sacloud.PrivateHost, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *PrivateHostAPI) setStateValue(setFunc func(*sacloud.Request)) *PrivateHostAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *PrivateHostAPI) request(f func(*sacloud.Response) error) (*sacloud.PrivateHost, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.PrivateHost, nil +} + +func (api *PrivateHostAPI) createRequest(value *sacloud.PrivateHost) *sacloud.Request { + req := &sacloud.Request{} + req.PrivateHost = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_disk.go b/vendor/github.com/sacloud/libsacloud/api/product_disk.go new file mode 100644 index 000000000..1cf850d67 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_disk.go @@ -0,0 +1,18 @@ +package api + +// ProductDiskAPI ディスクプランAPI +type ProductDiskAPI struct { + *baseAPI +} + +// NewProductDiskAPI ディスクプランAPI作成 +func NewProductDiskAPI(client *Client) *ProductDiskAPI { + return &ProductDiskAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "product/disk" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_disk_gen.go b/vendor/github.com/sacloud/libsacloud/api/product_disk_gen.go new file mode 100644 index 000000000..5fcbd5fa6 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_disk_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [ProductDiskAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ProductDiskAPI) Reset() *ProductDiskAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ProductDiskAPI) Offset(offset int) *ProductDiskAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ProductDiskAPI) Limit(limit int) *ProductDiskAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ProductDiskAPI) Include(key string) *ProductDiskAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ProductDiskAPI) Exclude(key string) *ProductDiskAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ProductDiskAPI) FilterBy(key string, value interface{}) *ProductDiskAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductDiskAPI) FilterMultiBy(key string, value interface{}) *ProductDiskAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ProductDiskAPI) WithNameLike(name string) *ProductDiskAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ProductDiskAPI) WithTag(tag string) *ProductDiskAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ProductDiskAPI) WithTags(tags []string) *ProductDiskAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductDiskAPI) WithSizeGib(size int) *ProductDiskAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ProductDiskAPI) WithSharedScope() *ProductDiskAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ProductDiskAPI) WithUserScope() *ProductDiskAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ProductDiskAPI) SortBy(key string, reverse bool) *ProductDiskAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ProductDiskAPI) SortByName(reverse bool) *ProductDiskAPI { + api.sortByName(reverse) + return api +} + +// func (api *ProductDiskAPI) SortBySize(reverse bool) *ProductDiskAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ProductDiskAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ProductDiskAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ProductDiskAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ProductDiskAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ProductDiskAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ProductDiskAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductDiskAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ProductDiskAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ProductDiskAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ProductDiskAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductDiskAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ProductDiskAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ProductDiskAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ProductDiskAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ProductDiskAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ProductDiskAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *ProductDiskAPI) New() *sacloud.ProductDisk { +// return &sacloud.ProductDisk{} +// } + +// func (api *ProductDiskAPI) Create(value *sacloud.ProductDisk) (*sacloud.ProductDisk, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *ProductDiskAPI) Read(id int64) (*sacloud.ProductDisk, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *ProductDiskAPI) Update(id int64, value *sacloud.ProductDisk) (*sacloud.ProductDisk, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ProductDiskAPI) Delete(id int64) (*sacloud.ProductDisk, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ProductDiskAPI) setStateValue(setFunc func(*sacloud.Request)) *ProductDiskAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ProductDiskAPI) request(f func(*sacloud.Response) error) (*sacloud.ProductDisk, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.DiskPlan, nil +} + +func (api *ProductDiskAPI) createRequest(value *sacloud.ProductDisk) *sacloud.Request { + req := &sacloud.Request{} + req.DiskPlan = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_internet.go b/vendor/github.com/sacloud/libsacloud/api/product_internet.go new file mode 100644 index 000000000..a80c11217 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_internet.go @@ -0,0 +1,18 @@ +package api + +// ProductInternetAPI ルータープランAPI +type ProductInternetAPI struct { + *baseAPI +} + +// NewProductInternetAPI ルータープランAPI作成 +func NewProductInternetAPI(client *Client) *ProductInternetAPI { + return &ProductInternetAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "product/internet" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_internet_gen.go b/vendor/github.com/sacloud/libsacloud/api/product_internet_gen.go new file mode 100644 index 000000000..c1a0d1344 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_internet_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [ProductInternetAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ProductInternetAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ProductInternetAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ProductInternetAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ProductInternetAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ProductInternetAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ProductInternetAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductInternetAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ProductInternetAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ProductInternetAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ProductInternetAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductInternetAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ProductInternetAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ProductInternetAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ProductInternetAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ProductInternetAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ProductInternetAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ProductInternetAPI) Reset() *ProductInternetAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ProductInternetAPI) Offset(offset int) *ProductInternetAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ProductInternetAPI) Limit(limit int) *ProductInternetAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ProductInternetAPI) Include(key string) *ProductInternetAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ProductInternetAPI) Exclude(key string) *ProductInternetAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ProductInternetAPI) FilterBy(key string, value interface{}) *ProductInternetAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductInternetAPI) FilterMultiBy(key string, value interface{}) *ProductInternetAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ProductInternetAPI) WithNameLike(name string) *ProductInternetAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ProductInternetAPI) WithTag(tag string) *ProductInternetAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ProductInternetAPI) WithTags(tags []string) *ProductInternetAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductInternetAPI) WithSizeGib(size int) *ProductInternetAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ProductInternetAPI) WithSharedScope() *ProductInternetAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ProductInternetAPI) WithUserScope() *ProductInternetAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ProductInternetAPI) SortBy(key string, reverse bool) *ProductInternetAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ProductInternetAPI) SortByName(reverse bool) *ProductInternetAPI { + api.sortByName(reverse) + return api +} + +// func (api *ProductInternetAPI) SortBySize(reverse bool) *ProductInternetAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *ProductInternetAPI) New() *sacloud.ProductInternet { +// return &sacloud.ProductInternet{} +//} + +// func (api *ProductInternetAPI) Create(value *sacloud.ProductInternet) (*sacloud.ProductInternet, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *ProductInternetAPI) Read(id int64) (*sacloud.ProductInternet, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *ProductInternetAPI) Update(id int64, value *sacloud.ProductInternet) (*sacloud.ProductInternet, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ProductInternetAPI) Delete(id int64) (*sacloud.ProductInternet, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ProductInternetAPI) setStateValue(setFunc func(*sacloud.Request)) *ProductInternetAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ProductInternetAPI) request(f func(*sacloud.Response) error) (*sacloud.ProductInternet, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.InternetPlan, nil +} + +func (api *ProductInternetAPI) createRequest(value *sacloud.ProductInternet) *sacloud.Request { + req := &sacloud.Request{} + req.InternetPlan = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_license.go b/vendor/github.com/sacloud/libsacloud/api/product_license.go new file mode 100644 index 000000000..c4ec92983 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_license.go @@ -0,0 +1,18 @@ +package api + +// ProductLicenseAPI ライセンスプランAPI +type ProductLicenseAPI struct { + *baseAPI +} + +// NewProductLicenseAPI ライセンスプランAPI作成 +func NewProductLicenseAPI(client *Client) *ProductLicenseAPI { + return &ProductLicenseAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "product/license" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_license_gen.go b/vendor/github.com/sacloud/libsacloud/api/product_license_gen.go new file mode 100644 index 000000000..ad017c35d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_license_gen.go @@ -0,0 +1,235 @@ +package api + +/************************************************ + generated by IDE. for [ProductLicenseAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ProductLicenseAPI) Reset() *ProductLicenseAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ProductLicenseAPI) Offset(offset int) *ProductLicenseAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ProductLicenseAPI) Limit(limit int) *ProductLicenseAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ProductLicenseAPI) Include(key string) *ProductLicenseAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ProductLicenseAPI) Exclude(key string) *ProductLicenseAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ProductLicenseAPI) FilterBy(key string, value interface{}) *ProductLicenseAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductLicenseAPI) FilterMultiBy(key string, value interface{}) *ProductLicenseAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ProductLicenseAPI) WithNameLike(name string) *ProductLicenseAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ProductLicenseAPI) WithTag(tag string) *ProductLicenseAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ProductLicenseAPI) WithTags(tags []string) *ProductLicenseAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductLicenseAPI) WithSizeGib(size int) *ProductLicenseAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ProductLicenseAPI) WithSharedScope() *ProductLicenseAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ProductLicenseAPI) WithUserScope() *ProductLicenseAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ProductLicenseAPI) SortBy(key string, reverse bool) *ProductLicenseAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ProductLicenseAPI) SortByName(reverse bool) *ProductLicenseAPI { + api.sortByName(reverse) + return api +} + +// func (api *ProductLicenseAPI) SortBySize(reverse bool) *ProductLicenseAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ProductLicenseAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ProductLicenseAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ProductLicenseAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ProductLicenseAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ProductLicenseAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ProductLicenseAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductLicenseAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ProductLicenseAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ProductLicenseAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ProductLicenseAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductLicenseAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ProductLicenseAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ProductLicenseAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ProductLicenseAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ProductLicenseAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ProductLicenseAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *ProductLicenseAPI) Create(value *sacloud.ProductLicense) (*sacloud.ProductLicense, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *ProductLicenseAPI) Read(id int64) (*sacloud.ProductLicense, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *ProductLicenseAPI) Update(id int64, value *sacloud.ProductLicense) (*sacloud.ProductLicense, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ProductLicenseAPI) Delete(id int64) (*sacloud.ProductLicense, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ProductLicenseAPI) setStateValue(setFunc func(*sacloud.Request)) *ProductLicenseAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ProductLicenseAPI) request(f func(*sacloud.Response) error) (*sacloud.ProductLicense, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.LicenseInfo, nil +} + +func (api *ProductLicenseAPI) createRequest(value *sacloud.ProductLicense) *sacloud.Request { + req := &sacloud.Request{} + req.LicenseInfo = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_private_host.go b/vendor/github.com/sacloud/libsacloud/api/product_private_host.go new file mode 100644 index 000000000..01f9a313f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_private_host.go @@ -0,0 +1,19 @@ +package api + +// ProductPrivateHostAPI 専有ホストプランAPI +type ProductPrivateHostAPI struct { + *baseAPI +} + +// NewProductPrivateHostAPI 専有ホストプランAPI作成 +func NewProductPrivateHostAPI(client *Client) *ProductPrivateHostAPI { + return &ProductPrivateHostAPI{ + &baseAPI{ + client: client, + // FuncGetResourceURL + FuncGetResourceURL: func() string { + return "product/privatehost" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_private_host_gen.go b/vendor/github.com/sacloud/libsacloud/api/product_private_host_gen.go new file mode 100644 index 000000000..ef037709b --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_private_host_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [ProductPrivateHostAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ProductPrivateHostAPI) Reset() *ProductPrivateHostAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ProductPrivateHostAPI) Offset(offset int) *ProductPrivateHostAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ProductPrivateHostAPI) Limit(limit int) *ProductPrivateHostAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ProductPrivateHostAPI) Include(key string) *ProductPrivateHostAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ProductPrivateHostAPI) Exclude(key string) *ProductPrivateHostAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ProductPrivateHostAPI) FilterBy(key string, value interface{}) *ProductPrivateHostAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductPrivateHostAPI) FilterMultiBy(key string, value interface{}) *ProductPrivateHostAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ProductPrivateHostAPI) WithNameLike(name string) *ProductPrivateHostAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ProductPrivateHostAPI) WithTag(tag string) *ProductPrivateHostAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ProductPrivateHostAPI) WithTags(tags []string) *ProductPrivateHostAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductPrivateHostAPI) WithSizeGib(size int) *ProductPrivateHostAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ProductPrivateHostAPI) WithSharedScope() *ProductPrivateHostAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ProductPrivateHostAPI) WithUserScope() *ProductPrivateHostAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ProductPrivateHostAPI) SortBy(key string, reverse bool) *ProductPrivateHostAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ProductPrivateHostAPI) SortByName(reverse bool) *ProductPrivateHostAPI { + api.sortByName(reverse) + return api +} + +// func (api *ProductPrivateHostAPI) SortBySize(reverse bool) *ProductPrivateHostAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ProductPrivateHostAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ProductPrivateHostAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ProductPrivateHostAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ProductPrivateHostAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ProductPrivateHostAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ProductPrivateHostAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductPrivateHostAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ProductPrivateHostAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ProductPrivateHostAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ProductPrivateHostAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductPrivateHostAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ProductPrivateHostAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ProductPrivateHostAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ProductPrivateHostAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ProductPrivateHostAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ProductPrivateHostAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *ProductPrivateHostAPI) New() *sacloud.ProductPrivateHost { +// return &sacloud.ProductPrivateHost{} +//} + +// func (api *ProductPrivateHostAPI) Create(value *sacloud.ProductPrivateHost) (*sacloud.ProductPrivateHost, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *ProductPrivateHostAPI) Read(id int64) (*sacloud.ProductPrivateHost, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *ProductPrivateHostAPI) Update(id int64, value *sacloud.ProductPrivateHost) (*sacloud.ProductPrivateHost, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ProductPrivateHostAPI) Delete(id int64) (*sacloud.ProductPrivateHost, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ProductPrivateHostAPI) setStateValue(setFunc func(*sacloud.Request)) *ProductPrivateHostAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ProductPrivateHostAPI) request(f func(*sacloud.Response) error) (*sacloud.ProductPrivateHost, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.PrivateHostPlan, nil +} + +func (api *ProductPrivateHostAPI) createRequest(value *sacloud.ProductPrivateHost) *sacloud.Request { + req := &sacloud.Request{} + req.PrivateHostPlan = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_server.go b/vendor/github.com/sacloud/libsacloud/api/product_server.go new file mode 100644 index 000000000..f5efff13d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_server.go @@ -0,0 +1,71 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "strconv" +) + +// ProductServerAPI サーバープランAPI +type ProductServerAPI struct { + *baseAPI +} + +// NewProductServerAPI サーバープランAPI作成 +func NewProductServerAPI(client *Client) *ProductServerAPI { + return &ProductServerAPI{ + &baseAPI{ + client: client, + // FuncGetResourceURL + FuncGetResourceURL: func() string { + return "product/server" + }, + }, + } +} + +func (api *ProductServerAPI) getPlanIDBySpec(core int, memGB int) (int64, error) { + //assert args + if core <= 0 { + return -1, fmt.Errorf("Invalid Parameter: CPU Core") + } + if memGB <= 0 { + return -1, fmt.Errorf("Invalid Parameter: Memory Size(GB)") + } + + return strconv.ParseInt(fmt.Sprintf("%d%03d", memGB, core), 10, 64) +} + +// IsValidPlan 指定のコア数/メモリサイズのプランが存在し、有効であるか判定 +func (api *ProductServerAPI) IsValidPlan(core int, memGB int) (bool, error) { + + planID, err := api.getPlanIDBySpec(core, memGB) + if err != nil { + return false, err + } + productServer, err := api.Read(planID) + + if err != nil { + return false, err + } + + if productServer != nil { + return true, nil + } + + return false, fmt.Errorf("Server Plan[%d] Not Found", planID) + +} + +// GetBySpec 指定のコア数/メモリサイズのサーバープランを取得 +func (api *ProductServerAPI) GetBySpec(core int, memGB int) (*sacloud.ProductServer, error) { + planID, err := api.getPlanIDBySpec(core, memGB) + + productServer, err := api.Read(planID) + + if err != nil { + return nil, err + } + + return productServer, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/product_server_gen.go b/vendor/github.com/sacloud/libsacloud/api/product_server_gen.go new file mode 100644 index 000000000..7710aab7c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/product_server_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [ProductServerAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ProductServerAPI) Reset() *ProductServerAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ProductServerAPI) Offset(offset int) *ProductServerAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ProductServerAPI) Limit(limit int) *ProductServerAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ProductServerAPI) Include(key string) *ProductServerAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ProductServerAPI) Exclude(key string) *ProductServerAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ProductServerAPI) FilterBy(key string, value interface{}) *ProductServerAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductServerAPI) FilterMultiBy(key string, value interface{}) *ProductServerAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ProductServerAPI) WithNameLike(name string) *ProductServerAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ProductServerAPI) WithTag(tag string) *ProductServerAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ProductServerAPI) WithTags(tags []string) *ProductServerAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductServerAPI) WithSizeGib(size int) *ProductServerAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ProductServerAPI) WithSharedScope() *ProductServerAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ProductServerAPI) WithUserScope() *ProductServerAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ProductServerAPI) SortBy(key string, reverse bool) *ProductServerAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ProductServerAPI) SortByName(reverse bool) *ProductServerAPI { + api.sortByName(reverse) + return api +} + +// func (api *ProductServerAPI) SortBySize(reverse bool) *ProductServerAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ProductServerAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ProductServerAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ProductServerAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ProductServerAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ProductServerAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ProductServerAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ProductServerAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ProductServerAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ProductServerAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ProductServerAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ProductServerAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ProductServerAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ProductServerAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ProductServerAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ProductServerAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ProductServerAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *ProductServerAPI) New() *sacloud.ProductServer { +// return &sacloud.ProductServer{} +//} + +// func (api *ProductServerAPI) Create(value *sacloud.ProductServer) (*sacloud.ProductServer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *ProductServerAPI) Read(id int64) (*sacloud.ProductServer, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *ProductServerAPI) Update(id int64, value *sacloud.ProductServer) (*sacloud.ProductServer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ProductServerAPI) Delete(id int64) (*sacloud.ProductServer, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ProductServerAPI) setStateValue(setFunc func(*sacloud.Request)) *ProductServerAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ProductServerAPI) request(f func(*sacloud.Response) error) (*sacloud.ProductServer, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.ServerPlan, nil +} + +func (api *ProductServerAPI) createRequest(value *sacloud.ProductServer) *sacloud.Request { + req := &sacloud.Request{} + req.ServerPlan = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/public_price.go b/vendor/github.com/sacloud/libsacloud/api/public_price.go new file mode 100644 index 000000000..d3b1cc9cc --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/public_price.go @@ -0,0 +1,18 @@ +package api + +// PublicPriceAPI 料金情報API +type PublicPriceAPI struct { + *baseAPI +} + +// NewPublicPriceAPI 料金情報API +func NewPublicPriceAPI(client *Client) *PublicPriceAPI { + return &PublicPriceAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "public/price" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/public_price_gen.go b/vendor/github.com/sacloud/libsacloud/api/public_price_gen.go new file mode 100644 index 000000000..7332fdf23 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/public_price_gen.go @@ -0,0 +1,236 @@ +package api + +/************************************************ + generated by IDE. for [PublicPriceAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *PublicPriceAPI) Reset() *PublicPriceAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *PublicPriceAPI) Offset(offset int) *PublicPriceAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *PublicPriceAPI) Limit(limit int) *PublicPriceAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *PublicPriceAPI) Include(key string) *PublicPriceAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *PublicPriceAPI) Exclude(key string) *PublicPriceAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *PublicPriceAPI) FilterBy(key string, value interface{}) *PublicPriceAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *PublicPriceAPI) FilterMultiBy(key string, value interface{}) *PublicPriceAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件(DisplayName) +func (api *PublicPriceAPI) WithNameLike(name string) *PublicPriceAPI { + return api.FilterBy("DisplayName", name) +} + +//// WithTag +//func (api *PublicPriceAPI) WithTag(tag string) *PublicPriceAPI { +// return api.FilterBy("Tags.Name", tag) +//} + +//// WithTags +//func (api *PublicPriceAPI) WithTags(tags []string) *PublicPriceAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *PublicPriceAPI) WithSizeGib(size int) *PublicPriceAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *PublicPriceAPI) WithSharedScope() *PublicPriceAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *PublicPriceAPI) WithUserScope() *PublicPriceAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *PublicPriceAPI) SortBy(key string, reverse bool) *PublicPriceAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート(DisplayName) +func (api *PublicPriceAPI) SortByName(reverse bool) *PublicPriceAPI { + api.sortBy("DisplayName", reverse) + return api +} + +// func (api *PublicPriceAPI) SortBySize(reverse bool) *PublicPriceAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *PublicPriceAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *PublicPriceAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *PublicPriceAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *PublicPriceAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *PublicPriceAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *PublicPriceAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *PublicPriceAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件(DisplayName) +func (api *PublicPriceAPI) SetNameLike(name string) { + api.FilterBy("DisplayName", name) +} + +//// SetTag +//func (api *PublicPriceAPI) SetTag(tag string) { +//} + +//// SetTags +//func (api *PublicPriceAPI) SetTags(tags []string) { +//} + +// func (api *PublicPriceAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *PublicPriceAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *PublicPriceAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *PublicPriceAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート(DisplayName) +func (api *PublicPriceAPI) SetSortByName(reverse bool) { + api.sortBy("DisplayName", reverse) +} + +// func (api *PublicPriceAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *PublicPriceAPI) New() *sacloud.PublicPrice { +// return &sacloud.PublicPrice{} +// } + +// func (api *PublicPriceAPI) Create(value *sacloud.PublicPrice) (*sacloud.PublicPrice, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *PublicPriceAPI) Read(id int64) (*sacloud.PublicPrice, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *PublicPriceAPI) Update(id int64, value *sacloud.PublicPrice) (*sacloud.PublicPrice, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *PublicPriceAPI) Delete(id int64) (*sacloud.PublicPrice, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *PublicPriceAPI) setStateValue(setFunc func(*sacloud.Request)) *PublicPriceAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *PublicPriceAPI) request(f func(*sacloud.Response) error) (*sacloud.PublicPrice, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.ServiceClass, nil +//} +// +//func (api *PublicPriceAPI) createRequest(value *sacloud.PublicPrice) *sacloud.Request { +// req := &sacloud.Request{} +// req.ServiceClass = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/region.go b/vendor/github.com/sacloud/libsacloud/api/region.go new file mode 100644 index 000000000..4f77c2de3 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/region.go @@ -0,0 +1,18 @@ +package api + +// RegionAPI リージョンAPI +type RegionAPI struct { + *baseAPI +} + +// NewRegionAPI リージョンAPI作成 +func NewRegionAPI(client *Client) *RegionAPI { + return &RegionAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "region" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/region_gen.go b/vendor/github.com/sacloud/libsacloud/api/region_gen.go new file mode 100644 index 000000000..cb09050b4 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/region_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [RegionAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *RegionAPI) Reset() *RegionAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *RegionAPI) Offset(offset int) *RegionAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *RegionAPI) Limit(limit int) *RegionAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *RegionAPI) Include(key string) *RegionAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *RegionAPI) Exclude(key string) *RegionAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *RegionAPI) FilterBy(key string, value interface{}) *RegionAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *RegionAPI) FilterMultiBy(key string, value interface{}) *RegionAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *RegionAPI) WithNameLike(name string) *RegionAPI { + return api.FilterBy("Name", name) +} + +//// WithTag +//func (api *RegionAPI) WithTag(tag string) *RegionAPI { +// return api.FilterBy("Tags.Name", tag) +//} +// +//// WithTags +//func (api *RegionAPI) WithTags(tags []string) *RegionAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *RegionAPI) WithSizeGib(size int) *RegionAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *RegionAPI) WithSharedScope() *RegionAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *RegionAPI) WithUserScope() *RegionAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *RegionAPI) SortBy(key string, reverse bool) *RegionAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *RegionAPI) SortByName(reverse bool) *RegionAPI { + api.sortByName(reverse) + return api +} + +// func (api *RegionAPI) SortBySize(reverse bool) *RegionAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *RegionAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *RegionAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *RegionAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *RegionAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *RegionAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *RegionAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *RegionAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *RegionAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +//// SetTag +//func (api *RegionAPI) SetTag(tag string) { +// api.FilterBy("Tags.Name", tag) +//} +// +//// SetTags +//func (api *RegionAPI) SetTags(tags []string) { +// api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *RegionAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *RegionAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *RegionAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *RegionAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *RegionAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *RegionAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *RegionAPI) New() *sacloud.Region { +// return &sacloud.Region{} +//} + +// func (api *RegionAPI) Create(value *sacloud.Region) (*sacloud.Region, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *RegionAPI) Read(id int64) (*sacloud.Region, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *RegionAPI) Update(id int64, value *sacloud.Region) (*sacloud.Region, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *RegionAPI) Delete(id int64) (*sacloud.Region, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *RegionAPI) setStateValue(setFunc func(*sacloud.Request)) *RegionAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *RegionAPI) request(f func(*sacloud.Response) error) (*sacloud.Region, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Region, nil +} + +func (api *RegionAPI) createRequest(value *sacloud.Region) *sacloud.Request { + req := &sacloud.Request{} + req.Region = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/server.go b/vendor/github.com/sacloud/libsacloud/api/server.go new file mode 100644 index 000000000..16d81fe05 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/server.go @@ -0,0 +1,287 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +// ServerAPI サーバーAPI +type ServerAPI struct { + *baseAPI +} + +// NewServerAPI サーバーAPI作成 +func NewServerAPI(client *Client) *ServerAPI { + return &ServerAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "server" + }, + }, + } +} + +// WithPlan サーバープラン条件 +func (api *ServerAPI) WithPlan(planID string) *ServerAPI { + return api.FilterBy("ServerPlan.ID", planID) +} + +// WithStatus インスタンスステータス条件 +func (api *ServerAPI) WithStatus(status string) *ServerAPI { + return api.FilterBy("Instance.Status", status) +} + +// WithStatusUp 起動状態条件 +func (api *ServerAPI) WithStatusUp() *ServerAPI { + return api.WithStatus("up") +} + +// WithStatusDown ダウン状態条件 +func (api *ServerAPI) WithStatusDown() *ServerAPI { + return api.WithStatus("down") +} + +// WithISOImage ISOイメージ条件 +func (api *ServerAPI) WithISOImage(imageID int64) *ServerAPI { + return api.FilterBy("Instance.CDROM.ID", imageID) +} + +// SortByCPU CPUコア数でのソート +func (api *ServerAPI) SortByCPU(reverse bool) *ServerAPI { + api.sortBy("ServerPlan.CPU", reverse) + return api +} + +// SortByMemory メモリサイズでのソート +func (api *ServerAPI) SortByMemory(reverse bool) *ServerAPI { + api.sortBy("ServerPlan.MemoryMB", reverse) + return api +} + +// DeleteWithDisk 指定のディスクと共に削除する +func (api *ServerAPI) DeleteWithDisk(id int64, disks []int64) (*sacloud.Server, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, map[string]interface{}{"WithDisk": disks}, res) + }) +} + +// State ステータス(Availability)取得 +func (api *ServerAPI) State(id int64) (string, error) { + server, err := api.Read(id) + if err != nil { + return "", err + } + return string(server.Availability), nil +} + +// IsUp 起動しているか判定 +func (api *ServerAPI) IsUp(id int64) (bool, error) { + server, err := api.Read(id) + if err != nil { + return false, err + } + return server.Instance.IsUp(), nil +} + +// IsDown ダウンしているか判定 +func (api *ServerAPI) IsDown(id int64) (bool, error) { + server, err := api.Read(id) + if err != nil { + return false, err + } + return server.Instance.IsDown(), nil +} + +// Boot 起動 +func (api *ServerAPI) Boot(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Shutdown シャットダウン(graceful) +func (api *ServerAPI) Shutdown(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Stop シャットダウン(force) +func (api *ServerAPI) Stop(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"Force": true}) +} + +// RebootForce 再起動 +func (api *ServerAPI) RebootForce(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// SleepUntilUp 起動するまで待機 +func (api *ServerAPI) SleepUntilUp(id int64, timeout time.Duration) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepUntilDown ダウンするまで待機 +func (api *ServerAPI) SleepUntilDown(id int64, timeout time.Duration) error { + handler := waitingForDownFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// ChangePlan サーバープラン変更(サーバーIDが変更となるため注意) +func (api *ServerAPI) ChangePlan(serverID int64, planID string) (*sacloud.Server, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/to/plan/%s", api.getResourceURL(), serverID, planID) + ) + + return api.request(func(res *sacloud.Response) error { + return api.baseAPI.request(method, uri, nil, res) + }) +} + +// FindDisk 指定サーバーに接続されているディスク一覧を取得 +func (api *ServerAPI) FindDisk(serverID int64) ([]sacloud.Disk, error) { + server, err := api.Read(serverID) + if err != nil { + return nil, err + } + return server.Disks, nil +} + +// InsertCDROM ISOイメージを挿入 +func (api *ServerAPI) InsertCDROM(serverID int64, cdromID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/cdrom", api.getResourceURL(), serverID) + ) + + req := &sacloud.Request{ + SakuraCloudResources: sacloud.SakuraCloudResources{ + CDROM: &sacloud.CDROM{Resource: &sacloud.Resource{ID: cdromID}}, + }, + } + + return api.modify(method, uri, req) +} + +// EjectCDROM ISOイメージを取り出し +func (api *ServerAPI) EjectCDROM(serverID int64, cdromID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/cdrom", api.getResourceURL(), serverID) + ) + + req := &sacloud.Request{ + SakuraCloudResources: sacloud.SakuraCloudResources{ + CDROM: &sacloud.CDROM{Resource: &sacloud.Resource{ID: cdromID}}, + }, + } + + return api.modify(method, uri, req) +} + +// NewKeyboardRequest キーボード入力リクエストパラメーター作成 +func (api *ServerAPI) NewKeyboardRequest() *sacloud.KeyboardRequest { + return &sacloud.KeyboardRequest{} +} + +// SendKey キーボード入力送信 +func (api *ServerAPI) SendKey(serverID int64, body *sacloud.KeyboardRequest) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/keyboard", api.getResourceURL(), serverID) + ) + + return api.modify(method, uri, body) +} + +// NewMouseRequest マウス入力リクエストパラメーター作成 +func (api *ServerAPI) NewMouseRequest() *sacloud.MouseRequest { + return &sacloud.MouseRequest{ + Buttons: &sacloud.MouseRequestButtons{}, + } +} + +// SendMouse マウス入力送信 +func (api *ServerAPI) SendMouse(serverID int64, mouseIndex string, body *sacloud.MouseRequest) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/mouse/%s", api.getResourceURL(), serverID, mouseIndex) + ) + + return api.modify(method, uri, body) +} + +// NewVNCSnapshotRequest VNCスナップショット取得リクエストパラメーター作成 +func (api *ServerAPI) NewVNCSnapshotRequest() *sacloud.VNCSnapshotRequest { + return &sacloud.VNCSnapshotRequest{} +} + +// GetVNCProxy VNCプロキシ情報取得 +func (api *ServerAPI) GetVNCProxy(serverID int64) (*sacloud.VNCProxyResponse, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/vnc/proxy", api.getResourceURL(), serverID) + res = &sacloud.VNCProxyResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res, nil +} + +// GetVNCSize VNC画面サイズ取得 +func (api *ServerAPI) GetVNCSize(serverID int64) (*sacloud.VNCSizeResponse, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/vnc/size", api.getResourceURL(), serverID) + res = &sacloud.VNCSizeResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res, nil +} + +// GetVNCSnapshot VNCスナップショット取得 +func (api *ServerAPI) GetVNCSnapshot(serverID int64, body *sacloud.VNCSnapshotRequest) (*sacloud.VNCSnapshotResponse, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/vnc/snapshot", api.getResourceURL(), serverID) + res = &sacloud.VNCSnapshotResponse{} + ) + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res, nil +} + +// Monitor アクティビティーモニター(CPU-TIME)取得 +func (api *ServerAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.monitor(id, body) +} diff --git a/vendor/github.com/sacloud/libsacloud/api/server_gen.go b/vendor/github.com/sacloud/libsacloud/api/server_gen.go new file mode 100644 index 000000000..e7e6fcae8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/server_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [ServerAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ServerAPI) Reset() *ServerAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ServerAPI) Offset(offset int) *ServerAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ServerAPI) Limit(limit int) *ServerAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ServerAPI) Include(key string) *ServerAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ServerAPI) Exclude(key string) *ServerAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ServerAPI) FilterBy(key string, value interface{}) *ServerAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ServerAPI) FilterMultiBy(key string, value interface{}) *ServerAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ServerAPI) WithNameLike(name string) *ServerAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *ServerAPI) WithTag(tag string) *ServerAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *ServerAPI) WithTags(tags []string) *ServerAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ServerAPI) WithSizeGib(size int) *ServerAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ServerAPI) WithSharedScope() *ServerAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ServerAPI) WithUserScope() *ServerAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ServerAPI) SortBy(key string, reverse bool) *ServerAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ServerAPI) SortByName(reverse bool) *ServerAPI { + api.sortByName(reverse) + return api +} + +// func (api *ServerAPI) SortBySize(reverse bool) *ServerAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ServerAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ServerAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ServerAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ServerAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ServerAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ServerAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ServerAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ServerAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *ServerAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *ServerAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *ServerAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ServerAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ServerAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ServerAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ServerAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ServerAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *ServerAPI) New() *sacloud.Server { + return &sacloud.Server{} +} + +// Create 新規作成 +func (api *ServerAPI) Create(value *sacloud.Server) (*sacloud.Server, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *ServerAPI) Read(id int64) (*sacloud.Server, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *ServerAPI) Update(id int64, value *sacloud.Server) (*sacloud.Server, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *ServerAPI) Delete(id int64) (*sacloud.Server, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *ServerAPI) setStateValue(setFunc func(*sacloud.Request)) *ServerAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ServerAPI) request(f func(*sacloud.Response) error) (*sacloud.Server, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Server, nil +} + +func (api *ServerAPI) createRequest(value *sacloud.Server) *sacloud.Request { + req := &sacloud.Request{} + req.Server = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/sim.go b/vendor/github.com/sacloud/libsacloud/api/sim.go new file mode 100644 index 000000000..e7be3d718 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/sim.go @@ -0,0 +1,219 @@ +package api + +import ( + "encoding/json" + "fmt" + + "github.com/sacloud/libsacloud/sacloud" +) + +// SearchSIMResponse SIM検索レスポンス +type SearchSIMResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // CommonServiceSIMItems SIMリスト + CommonServiceSIMItems []sacloud.SIM `json:"CommonServiceItems,omitempty"` +} + +type simRequest struct { + CommonServiceSIMItem *sacloud.SIM `json:"CommonServiceItem,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type simResponse struct { + *sacloud.ResultFlagValue + *sacloud.SIM `json:"CommonServiceItem,omitempty"` +} + +type simLogResponse struct { + Logs []sacloud.SIMLog `json:"logs,omitempty"` + IsOk bool `json:"is_ok,omitempty"` +} + +// SIMAPI SIM API +type SIMAPI struct { + *baseAPI +} + +// NewSIMAPI SIM API作成 +func NewSIMAPI(client *Client) *SIMAPI { + return &SIMAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "commonserviceitem" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Provider.Class", "sim") + return res + }, + }, + } +} + +// Find 検索 +func (api *SIMAPI) Find() (*SearchSIMResponse, error) { + + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchSIMResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *SIMAPI) request(f func(*simResponse) error) (*sacloud.SIM, error) { + res := &simResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.SIM, nil +} + +func (api *SIMAPI) createRequest(value *sacloud.SIM) *simRequest { + req := &simRequest{} + req.CommonServiceSIMItem = value + return req +} + +// Create 新規作成 +func (api *SIMAPI) Create(value *sacloud.SIM) (*sacloud.SIM, error) { + return api.request(func(res *simResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// New 新規作成用パラメーター作成 +func (api *SIMAPI) New(name, iccID, passcode string) *sacloud.SIM { + return sacloud.CreateNewSIM(name, iccID, passcode) +} + +// Read 読み取り +func (api *SIMAPI) Read(id int64) (*sacloud.SIM, error) { + return api.request(func(res *simResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *SIMAPI) Update(id int64, value *sacloud.SIM) (*sacloud.SIM, error) { + return api.request(func(res *simResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *SIMAPI) Delete(id int64) (*sacloud.SIM, error) { + return api.request(func(res *simResponse) error { + return api.delete(id, nil, res) + }) +} + +// Activate SIM有効化 +func (api *SIMAPI) Activate(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/sim/activate", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Deactivate SIM無効化 +func (api *SIMAPI) Deactivate(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/sim/deactivate", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// AssignIP SIMへのIP割り当て +func (api *SIMAPI) AssignIP(id int64, ip string) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/sim/ip", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]interface{}{ + "sim": map[string]interface{}{ + "ip": ip, + }, + }) +} + +// ClearIP SIMからのIP割り当て解除 +func (api *SIMAPI) ClearIP(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/sim/ip", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// IMEILock IMEIロック +func (api *SIMAPI) IMEILock(id int64, imei string) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/sim/imeilock", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]interface{}{ + "sim": map[string]interface{}{ + "imei": imei, + }, + }) +} + +// IMEIUnlock IMEIアンロック +func (api *SIMAPI) IMEIUnlock(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/sim/imeilock", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Logs セッションログ取得 +func (api *SIMAPI) Logs(id int64, body interface{}) ([]sacloud.SIMLog, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/sim/sessionlog", api.getResourceURL(), id) + ) + + res := &simLogResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Logs, nil +} + +// Monitor アクティビティーモニター(Up/Down link BPS)取得 +func (api *SIMAPI) Monitor(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/sim/metrics", api.getResourceURL(), id) + ) + res := &sacloud.ResourceMonitorResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Data, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/sim_gen.go b/vendor/github.com/sacloud/libsacloud/api/sim_gen.go new file mode 100644 index 000000000..2bb05cc05 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/sim_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [SIMAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *SIMAPI) Reset() *SIMAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *SIMAPI) Offset(offset int) *SIMAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *SIMAPI) Limit(limit int) *SIMAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *SIMAPI) Include(key string) *SIMAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *SIMAPI) Exclude(key string) *SIMAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *SIMAPI) FilterBy(key string, value interface{}) *SIMAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SIMAPI) FilterMultiBy(key string, value interface{}) *SIMAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *SIMAPI) WithNameLike(name string) *SIMAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *SIMAPI) WithTag(tag string) *SIMAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *SIMAPI) WithTags(tags []string) *SIMAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SIMAPI) WithSizeGib(size int) *SIMAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *SIMAPI) WithSharedScope() *SIMAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *SIMAPI) WithUserScope() *SIMAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *SIMAPI) SortBy(key string, reverse bool) *SIMAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *SIMAPI) SortByName(reverse bool) *SIMAPI { + api.sortByName(reverse) + return api +} + +// func (api *SIMAPI) SortBySize(reverse bool) *SIMAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *SIMAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *SIMAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *SIMAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *SIMAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *SIMAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *SIMAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SIMAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *SIMAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *SIMAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *SIMAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SIMAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *SIMAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *SIMAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *SIMAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *SIMAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *SIMAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *SIMAPI) New() *sacloud.SIM { +// return &sacloud.SIM{} +// } + +// func (api *SIMAPI) Create(value *sacloud.SIM) (*sacloud.SIM, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *SIMAPI) Read(id string) (*sacloud.SIM, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *SIMAPI) Update(id string, value *sacloud.SIM) (*sacloud.SIM, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *SIMAPI) Delete(id string) (*sacloud.SIM, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *SIMAPI) setStateValue(setFunc func(*sacloud.Request)) *SIMAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *SIMAPI) request(f func(*sacloud.Response) error) (*sacloud.SIM, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.SIM, nil +//} +// +//func (api *SIMAPI) createRequest(value *sacloud.SIM) *simRequest { +// req := &simRequest{} +// req.CommonServiceSIMItem = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go b/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go new file mode 100644 index 000000000..8e1a45b2a --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/simple_monitor.go @@ -0,0 +1,133 @@ +package api + +import ( + "encoding/json" + // "strings" + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +//HACK: さくらのAPI側仕様: CommonServiceItemsの内容によってJSONフォーマットが異なるため +// DNS/GSLB/シンプル監視それぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchSimpleMonitorResponse シンプル監視検索レスポンス +type SearchSimpleMonitorResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // SimpleMonitors シンプル監視 リスト + SimpleMonitors []sacloud.SimpleMonitor `json:"CommonServiceItems,omitempty"` +} + +type simpleMonitorRequest struct { + SimpleMonitor *sacloud.SimpleMonitor `json:"CommonServiceItem,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type simpleMonitorResponse struct { + *sacloud.ResultFlagValue + *sacloud.SimpleMonitor `json:"CommonServiceItem,omitempty"` +} + +// SimpleMonitorAPI シンプル監視API +type SimpleMonitorAPI struct { + *baseAPI +} + +// NewSimpleMonitorAPI シンプル監視API作成 +func NewSimpleMonitorAPI(client *Client) *SimpleMonitorAPI { + return &SimpleMonitorAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "commonserviceitem" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Provider.Class", "simplemon") + return res + }, + }, + } +} + +// Find 検索 +func (api *SimpleMonitorAPI) Find() (*SearchSimpleMonitorResponse, error) { + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchSimpleMonitorResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *SimpleMonitorAPI) request(f func(*simpleMonitorResponse) error) (*sacloud.SimpleMonitor, error) { + res := &simpleMonitorResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.SimpleMonitor, nil +} + +func (api *SimpleMonitorAPI) createRequest(value *sacloud.SimpleMonitor) *simpleMonitorResponse { + return &simpleMonitorResponse{SimpleMonitor: value} +} + +// New 新規作成用パラメーター作成 +func (api *SimpleMonitorAPI) New(target string) *sacloud.SimpleMonitor { + return sacloud.CreateNewSimpleMonitor(target) +} + +// Create 新規作成 +func (api *SimpleMonitorAPI) Create(value *sacloud.SimpleMonitor) (*sacloud.SimpleMonitor, error) { + return api.request(func(res *simpleMonitorResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *SimpleMonitorAPI) Read(id int64) (*sacloud.SimpleMonitor, error) { + return api.request(func(res *simpleMonitorResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *SimpleMonitorAPI) Update(id int64, value *sacloud.SimpleMonitor) (*sacloud.SimpleMonitor, error) { + return api.request(func(res *simpleMonitorResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *SimpleMonitorAPI) Delete(id int64) (*sacloud.SimpleMonitor, error) { + return api.request(func(res *simpleMonitorResponse) error { + return api.delete(id, nil, res) + }) +} + +// MonitorResponseTimeSec アクティビティーモニター(レスポンスタイム)取得 +func (api *SimpleMonitorAPI) MonitorResponseTimeSec(id int64, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/activity/responsetimesec/monitor", api.getResourceURL(), id) + ) + res := &sacloud.ResourceMonitorResponse{} + err := api.baseAPI.request(method, uri, body, res) + if err != nil { + return nil, err + } + return res.Data, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/simple_monitor_gen.go b/vendor/github.com/sacloud/libsacloud/api/simple_monitor_gen.go new file mode 100644 index 000000000..5c5279f53 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/simple_monitor_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [SimpleMonitorAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *SimpleMonitorAPI) Reset() *SimpleMonitorAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *SimpleMonitorAPI) Offset(offset int) *SimpleMonitorAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *SimpleMonitorAPI) Limit(limit int) *SimpleMonitorAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *SimpleMonitorAPI) Include(key string) *SimpleMonitorAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *SimpleMonitorAPI) Exclude(key string) *SimpleMonitorAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *SimpleMonitorAPI) FilterBy(key string, value interface{}) *SimpleMonitorAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SimpleMonitorAPI) FilterMultiBy(key string, value interface{}) *SimpleMonitorAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *SimpleMonitorAPI) WithNameLike(name string) *SimpleMonitorAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *SimpleMonitorAPI) WithTag(tag string) *SimpleMonitorAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *SimpleMonitorAPI) WithTags(tags []string) *SimpleMonitorAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SimpleMonitorAPI) WithSizeGib(size int) *SimpleMonitorAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *SimpleMonitorAPI) WithSharedScope() *SimpleMonitorAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *SimpleMonitorAPI) WithUserScope() *SimpleMonitorAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *SimpleMonitorAPI) SortBy(key string, reverse bool) *SimpleMonitorAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *SimpleMonitorAPI) SortByName(reverse bool) *SimpleMonitorAPI { + api.sortByName(reverse) + return api +} + +// func (api *SimpleMonitorAPI) SortBySize(reverse bool) *SimpleMonitorAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *SimpleMonitorAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *SimpleMonitorAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *SimpleMonitorAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *SimpleMonitorAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *SimpleMonitorAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *SimpleMonitorAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SimpleMonitorAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *SimpleMonitorAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *SimpleMonitorAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *SimpleMonitorAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SimpleMonitorAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *SimpleMonitorAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *SimpleMonitorAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *SimpleMonitorAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *SimpleMonitorAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *SimpleMonitorAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *SimpleMonitorAPI) New() *sacloud.SimpleMonitor { +// return &sacloud.SimpleMonitor{} +// } + +// func (api *SimpleMonitorAPI) Create(value *sacloud.SimpleMonitor) (*sacloud.SimpleMonitor, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *SimpleMonitorAPI) Read(id string) (*sacloud.SimpleMonitor, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *SimpleMonitorAPI) Update(id string, value *sacloud.SimpleMonitor) (*sacloud.SimpleMonitor, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *SimpleMonitorAPI) Delete(id string) (*sacloud.SimpleMonitor, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *SimpleMonitorAPI) setStateValue(setFunc func(*sacloud.Request)) *SimpleMonitorAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *SimpleMonitorAPI) request(f func(*sacloud.Response) error) (*sacloud.SimpleMonitor, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.SimpleMonitor, nil +//} +// +//func (api *SimpleMonitorAPI) createRequest(value *sacloud.SimpleMonitor) *sacloud.Request { +// req := &sacloud.Request{} +// req.SimpleMonitor = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/ssh_key.go b/vendor/github.com/sacloud/libsacloud/api/ssh_key.go new file mode 100644 index 000000000..631482862 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ssh_key.go @@ -0,0 +1,64 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +// SSHKeyAPI 公開鍵API +type SSHKeyAPI struct { + *baseAPI +} + +// NewSSHKeyAPI 公開鍵API作成 +func NewSSHKeyAPI(client *Client) *SSHKeyAPI { + return &SSHKeyAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "sshkey" + }, + }, + } +} + +// Generate 公開鍵の作成 +func (api *SSHKeyAPI) Generate(name string, passPhrase string, desc string) (*sacloud.SSHKeyGenerated, error) { + + var ( + method = "POST" + uri = fmt.Sprintf("%s/generate", api.getResourceURL()) + ) + + type genRequest struct { + Name string + GenerateFormat string + Description string + PassPhrase string + } + + type request struct { + SSHKey genRequest + } + type response struct { + *sacloud.ResultFlagValue + SSHKey *sacloud.SSHKeyGenerated + } + + body := &request{ + SSHKey: genRequest{ + Name: name, + GenerateFormat: "openssh", + PassPhrase: passPhrase, + Description: desc, + }, + } + + res := &response{} + + _, err := api.action(method, uri, body, res) + if err != nil { + return nil, fmt.Errorf("SSHKeyAPI: generate SSHKey is failed: %s", err) + } + return res.SSHKey, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/ssh_key_gen.go b/vendor/github.com/sacloud/libsacloud/api/ssh_key_gen.go new file mode 100644 index 000000000..84388f8ea --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/ssh_key_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [SSHKeyAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *SSHKeyAPI) Reset() *SSHKeyAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *SSHKeyAPI) Offset(offset int) *SSHKeyAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *SSHKeyAPI) Limit(limit int) *SSHKeyAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *SSHKeyAPI) Include(key string) *SSHKeyAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *SSHKeyAPI) Exclude(key string) *SSHKeyAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *SSHKeyAPI) FilterBy(key string, value interface{}) *SSHKeyAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SSHKeyAPI) FilterMultiBy(key string, value interface{}) *SSHKeyAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *SSHKeyAPI) WithNameLike(name string) *SSHKeyAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *SSHKeyAPI) WithTag(tag string) *SSHKeyAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *SSHKeyAPI) WithTags(tags []string) *SSHKeyAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SSHKeyAPI) WithSizeGib(size int) *SSHKeyAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *SSHKeyAPI) WithSharedScope() *SSHKeyAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *SSHKeyAPI) WithUserScope() *SSHKeyAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *SSHKeyAPI) SortBy(key string, reverse bool) *SSHKeyAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *SSHKeyAPI) SortByName(reverse bool) *SSHKeyAPI { + api.sortByName(reverse) + return api +} + +// func (api *SSHKeyAPI) SortBySize(reverse bool) *SSHKeyAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *SSHKeyAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *SSHKeyAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *SSHKeyAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *SSHKeyAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *SSHKeyAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *SSHKeyAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SSHKeyAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *SSHKeyAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *SSHKeyAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *SSHKeyAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SSHKeyAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *SSHKeyAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *SSHKeyAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *SSHKeyAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *SSHKeyAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *SSHKeyAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *SSHKeyAPI) New() *sacloud.SSHKey { + return &sacloud.SSHKey{} +} + +// Create 新規作成 +func (api *SSHKeyAPI) Create(value *sacloud.SSHKey) (*sacloud.SSHKey, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *SSHKeyAPI) Read(id int64) (*sacloud.SSHKey, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *SSHKeyAPI) Update(id int64, value *sacloud.SSHKey) (*sacloud.SSHKey, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *SSHKeyAPI) Delete(id int64) (*sacloud.SSHKey, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *SSHKeyAPI) setStateValue(setFunc func(*sacloud.Request)) *SSHKeyAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *SSHKeyAPI) request(f func(*sacloud.Response) error) (*sacloud.SSHKey, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.SSHKey, nil +} + +func (api *SSHKeyAPI) createRequest(value *sacloud.SSHKey) *sacloud.Request { + req := &sacloud.Request{} + req.SSHKey = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/subnet.go b/vendor/github.com/sacloud/libsacloud/api/subnet.go new file mode 100644 index 000000000..a56d84c29 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/subnet.go @@ -0,0 +1,18 @@ +package api + +// SubnetAPI サブネットAPI +type SubnetAPI struct { + *baseAPI +} + +// NewSubnetAPI サブネットAPI作成 +func NewSubnetAPI(client *Client) *SubnetAPI { + return &SubnetAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "subnet" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/subnet_gen.go b/vendor/github.com/sacloud/libsacloud/api/subnet_gen.go new file mode 100644 index 000000000..025a75e60 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/subnet_gen.go @@ -0,0 +1,229 @@ +package api + +/************************************************ + generated by IDE. for [SubnetAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *SubnetAPI) Reset() *SubnetAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *SubnetAPI) Offset(offset int) *SubnetAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *SubnetAPI) Limit(limit int) *SubnetAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *SubnetAPI) Include(key string) *SubnetAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *SubnetAPI) Exclude(key string) *SubnetAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *SubnetAPI) FilterBy(key string, value interface{}) *SubnetAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SubnetAPI) FilterMultiBy(key string, value interface{}) *SubnetAPI { + api.filterBy(key, value, true) + return api +} + +//func (api *SubnetAPI) WithNameLike(name string) *SubnetAPI { +// return api.FilterBy("Name", name) +//} +// +//func (api *SubnetAPI) WithTag(tag string) *SubnetAPI { +// return api.FilterBy("Tags.Name", tag) +//} +//func (api *SubnetAPI) WithTags(tags []string) *SubnetAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *SubnetAPI) WithSizeGib(size int) *SubnetAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *SubnetAPI) WithSharedScope() *SubnetAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *SubnetAPI) WithUserScope() *SubnetAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *SubnetAPI) SortBy(key string, reverse bool) *SubnetAPI { + api.sortBy(key, reverse) + return api +} + +//func (api *SubnetAPI) SortByName(reverse bool) *SubnetAPI { +// api.sortByName(reverse) +// return api +//} + +// func (api *SubnetAPI) SortBySize(reverse bool) *SubnetAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *SubnetAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *SubnetAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *SubnetAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *SubnetAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *SubnetAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *SubnetAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SubnetAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +//func (api *SubnetAPI) SetNameLike(name string) { +// api.FilterBy("Name", name) +//} +// +//func (api *SubnetAPI) SetTag(tag string) { +// api.FilterBy("Tags.Name", tag) +//} +//func (api *SubnetAPI) SetTags(tags []string) { +// api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *SubnetAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *SubnetAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *SubnetAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *SubnetAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +//func (api *SubnetAPI) SetSortByName(reverse bool) { +// api.sortByName(reverse) +//} + +// func (api *SubnetAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +//func (api *SubnetAPI) New() *sacloud.Subnet { +// return &sacloud.Subnet{} +//} + +//func (api *SubnetAPI) Create(value *sacloud.Subnet) (*sacloud.Subnet, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +//} + +// Read 読み取り +func (api *SubnetAPI) Read(id int64) (*sacloud.Subnet, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +//func (api *SubnetAPI) Update(id int64, value *sacloud.Subnet) (*sacloud.Subnet, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +//} +// +//func (api *SubnetAPI) Delete(id int64) (*sacloud.Subnet, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +//} + +/************************************************ + Inner functions +************************************************/ + +func (api *SubnetAPI) setStateValue(setFunc func(*sacloud.Request)) *SubnetAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *SubnetAPI) request(f func(*sacloud.Response) error) (*sacloud.Subnet, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Subnet, nil +} + +func (api *SubnetAPI) createRequest(value *sacloud.Subnet) *sacloud.Request { + req := &sacloud.Request{} + req.Subnet = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/switch.go b/vendor/github.com/sacloud/libsacloud/api/switch.go new file mode 100644 index 000000000..b646c10d8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/switch.go @@ -0,0 +1,55 @@ +package api + +import ( + "fmt" + "github.com/sacloud/libsacloud/sacloud" +) + +// SwitchAPI スイッチAPI +type SwitchAPI struct { + *baseAPI +} + +// NewSwitchAPI スイッチAPI作成 +func NewSwitchAPI(client *Client) *SwitchAPI { + return &SwitchAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "switch" + }, + }, + } +} + +// DisconnectFromBridge ブリッジとの切断 +func (api *SwitchAPI) DisconnectFromBridge(switchID int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/to/bridge", api.getResourceURL(), switchID) + ) + return api.modify(method, uri, nil) +} + +// ConnectToBridge ブリッジとの接続 +func (api *SwitchAPI) ConnectToBridge(switchID int64, bridgeID int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/to/bridge/%d", api.getResourceURL(), switchID, bridgeID) + ) + return api.modify(method, uri, nil) +} + +// GetServers スイッチに接続されているサーバー一覧取得 +func (api *SwitchAPI) GetServers(switchID int64) ([]sacloud.Server, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/server", api.getResourceURL(), switchID) + res = &sacloud.SearchResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res.Servers, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/switch_gen.go b/vendor/github.com/sacloud/libsacloud/api/switch_gen.go new file mode 100644 index 000000000..c7777155e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/switch_gen.go @@ -0,0 +1,243 @@ +package api + +/************************************************ + generated by IDE. for [SwitchAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *SwitchAPI) Reset() *SwitchAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *SwitchAPI) Offset(offset int) *SwitchAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *SwitchAPI) Limit(limit int) *SwitchAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *SwitchAPI) Include(key string) *SwitchAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *SwitchAPI) Exclude(key string) *SwitchAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *SwitchAPI) FilterBy(key string, value interface{}) *SwitchAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SwitchAPI) FilterMultiBy(key string, value interface{}) *SwitchAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *SwitchAPI) WithNameLike(name string) *SwitchAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *SwitchAPI) WithTag(tag string) *SwitchAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *SwitchAPI) WithTags(tags []string) *SwitchAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SwitchAPI) WithSizeGib(size int) *SwitchAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *SwitchAPI) WithSharedScope() *SwitchAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *SwitchAPI) WithUserScope() *SwitchAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *SwitchAPI) SortBy(key string, reverse bool) *SwitchAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *SwitchAPI) SortByName(reverse bool) *SwitchAPI { + api.sortByName(reverse) + return api +} + +// func (api *SwitchAPI) SortBySize(reverse bool) *SwitchAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *SwitchAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *SwitchAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *SwitchAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *SwitchAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *SwitchAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *SwitchAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *SwitchAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *SwitchAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *SwitchAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *SwitchAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *SwitchAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *SwitchAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *SwitchAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *SwitchAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *SwitchAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *SwitchAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// New 新規作成用パラメーター作成 +func (api *SwitchAPI) New() *sacloud.Switch { + return &sacloud.Switch{} +} + +// Create 新規作成 +func (api *SwitchAPI) Create(value *sacloud.Switch) (*sacloud.Switch, error) { + return api.request(func(res *sacloud.Response) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *SwitchAPI) Read(id int64) (*sacloud.Switch, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *SwitchAPI) Update(id int64, value *sacloud.Switch) (*sacloud.Switch, error) { + return api.request(func(res *sacloud.Response) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// Delete 削除 +func (api *SwitchAPI) Delete(id int64) (*sacloud.Switch, error) { + return api.request(func(res *sacloud.Response) error { + return api.delete(id, nil, res) + }) +} + +/************************************************ + Inner functions +************************************************/ + +func (api *SwitchAPI) setStateValue(setFunc func(*sacloud.Request)) *SwitchAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *SwitchAPI) request(f func(*sacloud.Response) error) (*sacloud.Switch, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Switch, nil +} + +func (api *SwitchAPI) createRequest(value *sacloud.Switch) *sacloud.Request { + req := &sacloud.Request{} + req.Switch = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/api/vpc_router.go b/vendor/github.com/sacloud/libsacloud/api/vpc_router.go new file mode 100644 index 000000000..43ad1513d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/vpc_router.go @@ -0,0 +1,437 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "time" +) + +//HACK: さくらのAPI側仕様: Applianceの内容によってJSONフォーマットが異なるため +// ロードバランサ/VPCルータそれぞれでリクエスト/レスポンスデータ型を定義する。 + +// SearchVPCRouterResponse VPCルーター検索レスポンス +type SearchVPCRouterResponse struct { + // Total 総件数 + Total int `json:",omitempty"` + // From ページング開始位置 + From int `json:",omitempty"` + // Count 件数 + Count int `json:",omitempty"` + // VPCRouters VPCルーター リスト + VPCRouters []sacloud.VPCRouter `json:"Appliances,omitempty"` +} + +type vpcRouterRequest struct { + VPCRouter *sacloud.VPCRouter `json:"Appliance,omitempty"` + From int `json:",omitempty"` + Count int `json:",omitempty"` + Sort []string `json:",omitempty"` + Filter map[string]interface{} `json:",omitempty"` + Exclude []string `json:",omitempty"` + Include []string `json:",omitempty"` +} + +type vpcRouterResponse struct { + *sacloud.ResultFlagValue + *sacloud.VPCRouter `json:"Appliance,omitempty"` + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} +} + +type vpcRouterStatusResponse struct { + *sacloud.ResultFlagValue + *sacloud.VPCRouterStatus `json:"Router"` + Success interface{} `json:",omitempty"` //HACK: さくらのAPI側仕様: 戻り値:Successがbool値へ変換できないためinterface{} +} + +type vpcRouterS2sConnInfoResponse struct { + *sacloud.ResultFlagValue + *sacloud.SiteToSiteConnectionInfo + Name string +} + +// VPCRouterAPI VPCルーターAPI +type VPCRouterAPI struct { + *baseAPI +} + +// NewVPCRouterAPI VPCルーターAPI作成 +func NewVPCRouterAPI(client *Client) *VPCRouterAPI { + return &VPCRouterAPI{ + &baseAPI{ + client: client, + FuncGetResourceURL: func() string { + return "appliance" + }, + FuncBaseSearchCondition: func() *sacloud.Request { + res := &sacloud.Request{} + res.AddFilter("Class", "vpcrouter") + return res + }, + }, + } +} + +// Find 検索 +func (api *VPCRouterAPI) Find() (*SearchVPCRouterResponse, error) { + data, err := api.client.newRequest("GET", api.getResourceURL(), api.getSearchState()) + if err != nil { + return nil, err + } + var res SearchVPCRouterResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +func (api *VPCRouterAPI) request(f func(*vpcRouterResponse) error) (*sacloud.VPCRouter, error) { + res := &vpcRouterResponse{} + err := f(res) + if err != nil { + return nil, err + } + return res.VPCRouter, nil +} + +func (api *VPCRouterAPI) createRequest(value *sacloud.VPCRouter) *vpcRouterResponse { + return &vpcRouterResponse{VPCRouter: value} +} + +// New 新規作成用パラメーター作成 +func (api *VPCRouterAPI) New() *sacloud.VPCRouter { + return sacloud.CreateNewVPCRouter() +} + +// Create 新規作成 +func (api *VPCRouterAPI) Create(value *sacloud.VPCRouter) (*sacloud.VPCRouter, error) { + return api.request(func(res *vpcRouterResponse) error { + return api.create(api.createRequest(value), res) + }) +} + +// Read 読み取り +func (api *VPCRouterAPI) Read(id int64) (*sacloud.VPCRouter, error) { + return api.request(func(res *vpcRouterResponse) error { + return api.read(id, nil, res) + }) +} + +// Update 更新 +func (api *VPCRouterAPI) Update(id int64, value *sacloud.VPCRouter) (*sacloud.VPCRouter, error) { + return api.request(func(res *vpcRouterResponse) error { + return api.update(id, api.createRequest(value), res) + }) +} + +// UpdateSetting 設定更新 +func (api *VPCRouterAPI) UpdateSetting(id int64, value *sacloud.VPCRouter) (*sacloud.VPCRouter, error) { + req := &sacloud.VPCRouter{ + // Settings + Settings: value.Settings, + } + return api.request(func(res *vpcRouterResponse) error { + return api.update(id, api.createRequest(req), res) + }) +} + +// Delete 削除 +func (api *VPCRouterAPI) Delete(id int64) (*sacloud.VPCRouter, error) { + return api.request(func(res *vpcRouterResponse) error { + return api.delete(id, nil, res) + }) +} + +// Config 設定変更の反映 +func (api *VPCRouterAPI) Config(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/config", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// ConnectToSwitch 指定のインデックス位置のNICをスイッチへ接続 +func (api *VPCRouterAPI) ConnectToSwitch(id int64, switchID int64, nicIndex int) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/interface/%d/to/switch/%d", api.getResourceURL(), id, nicIndex, switchID) + ) + return api.modify(method, uri, nil) +} + +// DisconnectFromSwitch 指定のインデックス位置のNICをスイッチから切断 +func (api *VPCRouterAPI) DisconnectFromSwitch(id int64, nicIndex int) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/interface/%d/to/switch", api.getResourceURL(), id, nicIndex) + ) + return api.modify(method, uri, nil) +} + +// IsUp 起動しているか判定 +func (api *VPCRouterAPI) IsUp(id int64) (bool, error) { + router, err := api.Read(id) + if err != nil { + return false, err + } + return router.Instance.IsUp(), nil +} + +// IsDown ダウンしているか判定 +func (api *VPCRouterAPI) IsDown(id int64) (bool, error) { + router, err := api.Read(id) + if err != nil { + return false, err + } + return router.Instance.IsDown(), nil +} + +// Boot 起動 +func (api *VPCRouterAPI) Boot(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + return api.modify(method, uri, nil) +} + +// Shutdown シャットダウン(graceful) +func (api *VPCRouterAPI) Shutdown(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// Stop シャットダウン(force) +func (api *VPCRouterAPI) Stop(id int64) (bool, error) { + var ( + method = "DELETE" + uri = fmt.Sprintf("%s/%d/power", api.getResourceURL(), id) + ) + + return api.modify(method, uri, map[string]bool{"Force": true}) +} + +// RebootForce 再起動 +func (api *VPCRouterAPI) RebootForce(id int64) (bool, error) { + var ( + method = "PUT" + uri = fmt.Sprintf("%s/%d/reset", api.getResourceURL(), id) + ) + + return api.modify(method, uri, nil) +} + +// SleepUntilUp 起動するまで待機 +func (api *VPCRouterAPI) SleepUntilUp(id int64, timeout time.Duration) error { + handler := waitingForUpFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepUntilDown ダウンするまで待機 +func (api *VPCRouterAPI) SleepUntilDown(id int64, timeout time.Duration) error { + handler := waitingForDownFunc(func() (hasUpDown, error) { + return api.Read(id) + }, 0) + return blockingPoll(handler, timeout) +} + +// SleepWhileCopying コピー終了まで待機 +// +// maxRetry: リクエストタイミングによって、コピー完了までの間に404エラーとなる場合がある。 +// 通常そのまま待てばコピー完了するため、404エラーが発生してもmaxRetryで指定した回数分は待機する。 +func (api *VPCRouterAPI) SleepWhileCopying(id int64, timeout time.Duration, maxRetry int) error { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return blockingPoll(handler, timeout) +} + +// AsyncSleepWhileCopying コピー終了まで待機(非同期) +func (api *VPCRouterAPI) AsyncSleepWhileCopying(id int64, timeout time.Duration, maxRetry int) (chan (interface{}), chan (interface{}), chan (error)) { + handler := waitingForAvailableFunc(func() (hasAvailable, error) { + return api.Read(id) + }, maxRetry) + return poll(handler, timeout) +} + +// AddStandardInterface スタンダードプランでのインターフェース追加 +func (api *VPCRouterAPI) AddStandardInterface(routerID int64, switchID int64, ipaddress string, maskLen int) (*sacloud.VPCRouter, error) { + return api.addInterface(routerID, switchID, &sacloud.VPCRouterInterface{ + IPAddress: []string{ipaddress}, + NetworkMaskLen: maskLen, + VirtualIPAddress: "", + }) +} + +// AddPremiumInterface プレミアムプランでのインターフェース追加 +func (api *VPCRouterAPI) AddPremiumInterface(routerID int64, switchID int64, ipaddresses []string, maskLen int, virtualIP string) (*sacloud.VPCRouter, error) { + return api.addInterface(routerID, switchID, &sacloud.VPCRouterInterface{ + IPAddress: ipaddresses, + NetworkMaskLen: maskLen, + VirtualIPAddress: virtualIP, + }) +} + +func (api *VPCRouterAPI) addInterface(routerID int64, switchID int64, routerNIC *sacloud.VPCRouterInterface) (*sacloud.VPCRouter, error) { + router, err := api.Read(routerID) + if err != nil { + return nil, err + } + req := &sacloud.VPCRouter{Settings: &sacloud.VPCRouterSettings{}} + + if router.Settings == nil { + req.Settings = &sacloud.VPCRouterSettings{ + Router: &sacloud.VPCRouterSetting{ + Interfaces: []*sacloud.VPCRouterInterface{nil}, + }, + } + } else { + req.Settings.Router = router.Settings.Router + } + + index := len(req.Settings.Router.Interfaces) // add to last + return api.addInterfaceAt(routerID, switchID, routerNIC, index) +} + +// AddStandardInterfaceAt スタンダードプランでの指定位置へのインターフェース追加 +func (api *VPCRouterAPI) AddStandardInterfaceAt(routerID int64, switchID int64, ipaddress string, maskLen int, index int) (*sacloud.VPCRouter, error) { + return api.addInterfaceAt(routerID, switchID, &sacloud.VPCRouterInterface{ + IPAddress: []string{ipaddress}, + NetworkMaskLen: maskLen, + VirtualIPAddress: "", + }, index) +} + +// AddPremiumInterfaceAt プレミアムプランでの指定位置へのインターフェース追加 +func (api *VPCRouterAPI) AddPremiumInterfaceAt(routerID int64, switchID int64, ipaddresses []string, maskLen int, virtualIP string, index int) (*sacloud.VPCRouter, error) { + return api.addInterfaceAt(routerID, switchID, &sacloud.VPCRouterInterface{ + IPAddress: ipaddresses, + NetworkMaskLen: maskLen, + VirtualIPAddress: virtualIP, + }, index) +} + +func (api *VPCRouterAPI) addInterfaceAt(routerID int64, switchID int64, routerNIC *sacloud.VPCRouterInterface, index int) (*sacloud.VPCRouter, error) { + router, err := api.Read(routerID) + if err != nil { + return nil, err + } + + req := &sacloud.VPCRouter{Settings: &sacloud.VPCRouterSettings{}} + + if router.Settings == nil { + req.Settings = &sacloud.VPCRouterSettings{ + Router: &sacloud.VPCRouterSetting{ + Interfaces: []*sacloud.VPCRouterInterface{nil}, + }, + } + } else { + req.Settings.Router = router.Settings.Router + } + + //connect to switch + _, err = api.ConnectToSwitch(routerID, switchID, index) + if err != nil { + return nil, err + } + + for i := 0; i < index; i++ { + if len(req.Settings.Router.Interfaces) < index { + req.Settings.Router.Interfaces = append(req.Settings.Router.Interfaces, nil) + } + } + + if len(req.Settings.Router.Interfaces) < index+1 { + req.Settings.Router.Interfaces = append(req.Settings.Router.Interfaces, routerNIC) + } else { + req.Settings.Router.Interfaces[index] = routerNIC + } + + res, err := api.UpdateSetting(routerID, req) + if err != nil { + return nil, err + } + + return res, nil + +} + +// DeleteInterfaceAt 指定位置のインターフェース削除 +func (api *VPCRouterAPI) DeleteInterfaceAt(routerID int64, index int) (*sacloud.VPCRouter, error) { + router, err := api.Read(routerID) + if err != nil { + return nil, err + } + + req := &sacloud.VPCRouter{Settings: &sacloud.VPCRouterSettings{}} + + if router.Settings == nil { + req.Settings = &sacloud.VPCRouterSettings{ + // Router + Router: &sacloud.VPCRouterSetting{ + // Interfaces + Interfaces: []*sacloud.VPCRouterInterface{nil}, + }, + } + } else { + req.Settings.Router = router.Settings.Router + } + + //disconnect to switch + _, err = api.DisconnectFromSwitch(routerID, index) + if err != nil { + return nil, err + } + + if index < len(req.Settings.Router.Interfaces) { + req.Settings.Router.Interfaces[index] = nil + } + + res, err := api.UpdateSetting(routerID, req) + if err != nil { + return nil, err + } + + return res, nil + +} + +// MonitorBy 指定位置のインターフェースのアクティビティーモニター取得 +func (api *VPCRouterAPI) MonitorBy(id int64, nicIndex int, body *sacloud.ResourceMonitorRequest) (*sacloud.MonitorValues, error) { + return api.baseAPI.applianceMonitorBy(id, "interface", nicIndex, body) +} + +// Status ログなどのステータス情報 取得 +func (api *VPCRouterAPI) Status(id int64) (*sacloud.VPCRouterStatus, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/status", api.getResourceURL(), id) + res = &vpcRouterStatusResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res.VPCRouterStatus, nil +} + +// SiteToSiteConnectionDetails サイト間VPN接続情報を取得 +func (api *VPCRouterAPI) SiteToSiteConnectionDetails(id int64) (*sacloud.SiteToSiteConnectionInfo, error) { + var ( + method = "GET" + uri = fmt.Sprintf("%s/%d/vpcrouter/sitetosite/connectiondetails", api.getResourceURL(), id) + res = &vpcRouterS2sConnInfoResponse{} + ) + err := api.baseAPI.request(method, uri, nil, res) + if err != nil { + return nil, err + } + return res.SiteToSiteConnectionInfo, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/vpc_router_gen.go b/vendor/github.com/sacloud/libsacloud/api/vpc_router_gen.go new file mode 100644 index 000000000..a913e189d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/vpc_router_gen.go @@ -0,0 +1,238 @@ +package api + +/************************************************ + generated by IDE. for [VPCRouterAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *VPCRouterAPI) Reset() *VPCRouterAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *VPCRouterAPI) Offset(offset int) *VPCRouterAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *VPCRouterAPI) Limit(limit int) *VPCRouterAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *VPCRouterAPI) Include(key string) *VPCRouterAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *VPCRouterAPI) Exclude(key string) *VPCRouterAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *VPCRouterAPI) FilterBy(key string, value interface{}) *VPCRouterAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *VPCRouterAPI) FilterMultiBy(key string, value interface{}) *VPCRouterAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *VPCRouterAPI) WithNameLike(name string) *VPCRouterAPI { + return api.FilterBy("Name", name) +} + +// WithTag タグ条件 +func (api *VPCRouterAPI) WithTag(tag string) *VPCRouterAPI { + return api.FilterBy("Tags.Name", tag) +} + +// WithTags タグ(複数)条件 +func (api *VPCRouterAPI) WithTags(tags []string) *VPCRouterAPI { + return api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *VPCRouterAPI) WithSizeGib(size int) *VPCRouterAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *VPCRouterAPI) WithSharedScope() *VPCRouterAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *VPCRouterAPI) WithUserScope() *VPCRouterAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *VPCRouterAPI) SortBy(key string, reverse bool) *VPCRouterAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *VPCRouterAPI) SortByName(reverse bool) *VPCRouterAPI { + api.sortByName(reverse) + return api +} + +// func (api *VPCRouterAPI) SortBySize(reverse bool) *VPCRouterAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *VPCRouterAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *VPCRouterAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *VPCRouterAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *VPCRouterAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *VPCRouterAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *VPCRouterAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *VPCRouterAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *VPCRouterAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// SetTag タグ条件 +func (api *VPCRouterAPI) SetTag(tag string) { + api.FilterBy("Tags.Name", tag) +} + +// SetTags タグ(複数)条件 +func (api *VPCRouterAPI) SetTags(tags []string) { + api.FilterBy("Tags.Name", []interface{}{tags}) +} + +// func (api *VPCRouterAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *VPCRouterAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *VPCRouterAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *VPCRouterAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *VPCRouterAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *VPCRouterAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *VPCRouterAPI) New() *sacloud.VPCRouter { +// return &sacloud.VPCRouter{} +// } + +// func (api *VPCRouterAPI) Create(value *sacloud.VPCRouter) (*sacloud.VPCRouter, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// func (api *VPCRouterAPI) Read(id string) (*sacloud.VPCRouter, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.read(id, nil, res) +// }) +// } + +// func (api *VPCRouterAPI) Update(id string, value *sacloud.VPCRouter) (*sacloud.VPCRouter, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *VPCRouterAPI) Delete(id string) (*sacloud.VPCRouter, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *VPCRouterAPI) setStateValue(setFunc func(*sacloud.Request)) *VPCRouterAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +//func (api *VPCRouterAPI) request(f func(*sacloud.Response) error) (*sacloud.VPCRouter, error) { +// res := &sacloud.Response{} +// err := f(res) +// if err != nil { +// return nil, err +// } +// return res.VPCRouter, nil +//} +// +//func (api *VPCRouterAPI) createRequest(value *sacloud.VPCRouter) *sacloud.Request { +// req := &sacloud.Request{} +// req.VPCRouter = value +// return req +//} diff --git a/vendor/github.com/sacloud/libsacloud/api/webaccel.go b/vendor/github.com/sacloud/libsacloud/api/webaccel.go new file mode 100644 index 000000000..e3b924b24 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/webaccel.go @@ -0,0 +1,114 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "strings" +) + +// WebAccelAPI ウェブアクセラレータAPI +type WebAccelAPI struct { + *baseAPI +} + +// NewWebAccelAPI ウェブアクセラレータAPI +func NewWebAccelAPI(client *Client) *WebAccelAPI { + return &WebAccelAPI{ + &baseAPI{ + client: client, + apiRootSuffix: sakuraWebAccelAPIRootSuffix, + FuncGetResourceURL: func() string { + return "" + }, + }, + } +} + +// WebAccelDeleteCacheResponse ウェブアクセラレータ キャッシュ削除レスポンス +type WebAccelDeleteCacheResponse struct { + *sacloud.ResultFlagValue + Results []*sacloud.DeleteCacheResult +} + +// Read サイト情報取得 +func (api *WebAccelAPI) Read(id string) (*sacloud.WebAccelSite, error) { + + uri := fmt.Sprintf("%s/site/%s", api.getResourceURL(), id) + + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + var res struct { + Site sacloud.WebAccelSite + } + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res.Site, nil +} + +// ReadCertificate 証明書 参照 +func (api *WebAccelAPI) ReadCertificate(id string) (*sacloud.WebAccelCertResponseBody, error) { + uri := fmt.Sprintf("%s/site/%s/certificate", api.getResourceURL(), id) + + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + var res sacloud.WebAccelCertResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return res.Certificate, nil +} + +// UpdateCertificate 証明書 更新 +func (api *WebAccelAPI) UpdateCertificate(id string, request *sacloud.WebAccelCertRequest) (*sacloud.WebAccelCertResponse, error) { + uri := fmt.Sprintf("%s/site/%s/certificate", api.getResourceURL(), id) + + if request.CertificateChain != "" { + request.CertificateChain = strings.TrimRight(request.CertificateChain, "\n") + } + if request.Key != "" { + request.Key = strings.TrimRight(request.Key, "\n") + } + + data, err := api.client.newRequest("PUT", uri, map[string]interface{}{ + "Certificate": request, + }) + if err != nil { + return nil, err + } + + var res sacloud.WebAccelCertResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} + +// DeleteCache キャッシュ削除 +func (api *WebAccelAPI) DeleteCache(urls ...string) (*WebAccelDeleteCacheResponse, error) { + + type request struct { + // URL + URL []string + } + + uri := fmt.Sprintf("%s/deletecache", api.getResourceURL()) + + data, err := api.client.newRequest("POST", uri, &request{URL: urls}) + if err != nil { + return nil, err + } + + var res WebAccelDeleteCacheResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + return &res, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/webaccel_search.go b/vendor/github.com/sacloud/libsacloud/api/webaccel_search.go new file mode 100644 index 000000000..fc8884b64 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/webaccel_search.go @@ -0,0 +1,85 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/sacloud/libsacloud/sacloud" + "net/url" + "strings" +) + +// Reset 検索条件のリセット +func (api *WebAccelAPI) Reset() *WebAccelAPI { + api.SetEmpty() + return api +} + +// SetEmpty 検索条件のリセット +func (api *WebAccelAPI) SetEmpty() { + api.reset() +} + +// FilterBy 指定キーでのフィルター +func (api *WebAccelAPI) FilterBy(key string, value interface{}) *WebAccelAPI { + api.filterBy(key, value, false) + return api +} + +// WithNameLike 名称条件 +func (api *WebAccelAPI) WithNameLike(name string) *WebAccelAPI { + return api.FilterBy("Name", name) +} + +// SetFilterBy 指定キーでのフィルター +func (api *WebAccelAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetNameLike 名称条件 +func (api *WebAccelAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +// Find サイト一覧取得 +func (api *WebAccelAPI) Find() (*sacloud.SearchResponse, error) { + + uri := fmt.Sprintf("%s/site", api.getResourceURL()) + + data, err := api.client.newRequest("GET", uri, nil) + if err != nil { + return nil, err + } + + var res sacloud.SearchResponse + if err := json.Unmarshal(data, &res); err != nil { + return nil, err + } + + // handle filter(API側がFilterに対応していないためここでフィルタリング) + for key, filter := range api.getSearchState().Filter { + if key != "Name" { + continue + } + strNames, ok := filter.(string) + if !ok { + continue + } + + names := strings.Split(strNames, " ") + filtered := []sacloud.WebAccelSite{} + for _, site := range res.WebAccelSites { + for _, name := range names { + + u, _ := url.Parse(name) + + if strings.Contains(site.Name, u.Path) { + filtered = append(filtered, site) + break + } + } + } + res.WebAccelSites = filtered + } + + return &res, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/api/zone.go b/vendor/github.com/sacloud/libsacloud/api/zone.go new file mode 100644 index 000000000..214de1e05 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/zone.go @@ -0,0 +1,19 @@ +package api + +// ZoneAPI ゾーンAPI +type ZoneAPI struct { + *baseAPI +} + +// NewZoneAPI ゾーンAPI作成 +func NewZoneAPI(client *Client) *ZoneAPI { + return &ZoneAPI{ + &baseAPI{ + client: client, + // FuncGetResourceURL + FuncGetResourceURL: func() string { + return "zone" + }, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/api/zone_gen.go b/vendor/github.com/sacloud/libsacloud/api/zone_gen.go new file mode 100644 index 000000000..dfaabeace --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/api/zone_gen.go @@ -0,0 +1,239 @@ +package api + +/************************************************ + generated by IDE. for [ZoneAPI] +************************************************/ + +import ( + "github.com/sacloud/libsacloud/sacloud" +) + +/************************************************ + To support fluent interface for Find() +************************************************/ + +// Reset 検索条件のリセット +func (api *ZoneAPI) Reset() *ZoneAPI { + api.reset() + return api +} + +// Offset オフセット +func (api *ZoneAPI) Offset(offset int) *ZoneAPI { + api.offset(offset) + return api +} + +// Limit リミット +func (api *ZoneAPI) Limit(limit int) *ZoneAPI { + api.limit(limit) + return api +} + +// Include 取得する項目 +func (api *ZoneAPI) Include(key string) *ZoneAPI { + api.include(key) + return api +} + +// Exclude 除外する項目 +func (api *ZoneAPI) Exclude(key string) *ZoneAPI { + api.exclude(key) + return api +} + +// FilterBy 指定キーでのフィルター +func (api *ZoneAPI) FilterBy(key string, value interface{}) *ZoneAPI { + api.filterBy(key, value, false) + return api +} + +// FilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ZoneAPI) FilterMultiBy(key string, value interface{}) *ZoneAPI { + api.filterBy(key, value, true) + return api +} + +// WithNameLike 名称条件 +func (api *ZoneAPI) WithNameLike(name string) *ZoneAPI { + return api.FilterBy("Name", name) +} + +//// WithTag タグ条件 +//func (api *ZoneAPI) WithTag(tag string) *ZoneAPI { +// return api.FilterBy("Tags.Name", tag) +//} +// +//// WithTags タグ(複数)条件 +//func (api *ZoneAPI) WithTags(tags []string) *ZoneAPI { +// return api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *ZoneAPI) WithSizeGib(size int) *ZoneAPI { +// api.FilterBy("SizeMB", size*1024) +// return api +// } + +// func (api *ZoneAPI) WithSharedScope() *ZoneAPI { +// api.FilterBy("Scope", "shared") +// return api +// } + +// func (api *ZoneAPI) WithUserScope() *ZoneAPI { +// api.FilterBy("Scope", "user") +// return api +// } + +// SortBy 指定キーでのソート +func (api *ZoneAPI) SortBy(key string, reverse bool) *ZoneAPI { + api.sortBy(key, reverse) + return api +} + +// SortByName 名称でのソート +func (api *ZoneAPI) SortByName(reverse bool) *ZoneAPI { + api.sortByName(reverse) + return api +} + +// func (api *ZoneAPI) SortBySize(reverse bool) *ZoneAPI { +// api.sortBy("SizeMB", reverse) +// return api +// } + +/************************************************ + To support Setxxx interface for Find() +************************************************/ + +// SetEmpty 検索条件のリセット +func (api *ZoneAPI) SetEmpty() { + api.reset() +} + +// SetOffset オフセット +func (api *ZoneAPI) SetOffset(offset int) { + api.offset(offset) +} + +// SetLimit リミット +func (api *ZoneAPI) SetLimit(limit int) { + api.limit(limit) +} + +// SetInclude 取得する項目 +func (api *ZoneAPI) SetInclude(key string) { + api.include(key) +} + +// SetExclude 除外する項目 +func (api *ZoneAPI) SetExclude(key string) { + api.exclude(key) +} + +// SetFilterBy 指定キーでのフィルター +func (api *ZoneAPI) SetFilterBy(key string, value interface{}) { + api.filterBy(key, value, false) +} + +// SetFilterMultiBy 任意項目でのフィルタ(完全一致 OR条件) +func (api *ZoneAPI) SetFilterMultiBy(key string, value interface{}) { + api.filterBy(key, value, true) +} + +// SetNameLike 名称条件 +func (api *ZoneAPI) SetNameLike(name string) { + api.FilterBy("Name", name) +} + +//// SetTag タグ条件 +//func (api *ZoneAPI) SetTag(tag string) { +// api.FilterBy("Tags.Name", tag) +//} +// +//// SetTags タグ(複数)条件 +//func (api *ZoneAPI) SetTags(tags []string) { +// api.FilterBy("Tags.Name", []interface{}{tags}) +//} + +// func (api *ZoneAPI) SetSizeGib(size int) { +// api.FilterBy("SizeMB", size*1024) +// } + +// func (api *ZoneAPI) SetSharedScope() { +// api.FilterBy("Scope", "shared") +// } + +// func (api *ZoneAPI) SetUserScope() { +// api.FilterBy("Scope", "user") +// } + +// SetSortBy 指定キーでのソート +func (api *ZoneAPI) SetSortBy(key string, reverse bool) { + api.sortBy(key, reverse) +} + +// SetSortByName 名称でのソート +func (api *ZoneAPI) SetSortByName(reverse bool) { + api.sortByName(reverse) +} + +// func (api *ZoneAPI) SetSortBySize(reverse bool) { +// api.sortBy("SizeMB", reverse) +// } + +/************************************************ + To support CRUD(Create/Read/Update/Delete) +************************************************/ + +// func (api *ZoneAPI) New() *sacloud.Zone { +// return &sacloud.Zone{} +// } + +// func (api *ZoneAPI) Create(value *sacloud.Zone) (*sacloud.Zone, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.create(api.createRequest(value), res) +// }) +// } + +// Read 読み取り +func (api *ZoneAPI) Read(id int64) (*sacloud.Zone, error) { + return api.request(func(res *sacloud.Response) error { + return api.read(id, nil, res) + }) +} + +// func (api *ZoneAPI) Update(id string, value *sacloud.Zone) (*sacloud.Zone, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.update(id, api.createRequest(value), res) +// }) +// } + +// func (api *ZoneAPI) Delete(id string) (*sacloud.Zone, error) { +// return api.request(func(res *sacloud.Response) error { +// return api.delete(id, nil, res) +// }) +// } + +/************************************************ + Inner functions +************************************************/ + +func (api *ZoneAPI) setStateValue(setFunc func(*sacloud.Request)) *ZoneAPI { + api.baseAPI.setStateValue(setFunc) + return api +} + +func (api *ZoneAPI) request(f func(*sacloud.Response) error) (*sacloud.Zone, error) { + res := &sacloud.Response{} + err := f(res) + if err != nil { + return nil, err + } + return res.Zone, nil +} + +func (api *ZoneAPI) createRequest(value *sacloud.Zone) *sacloud.Request { + req := &sacloud.Request{} + req.Zone = value + return req +} diff --git a/vendor/github.com/sacloud/libsacloud/libsacloud.go b/vendor/github.com/sacloud/libsacloud/libsacloud.go new file mode 100644 index 000000000..1981b5029 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/libsacloud.go @@ -0,0 +1,5 @@ +// Package libsacloud is the SAKURA CLOUD API v1.1 Client for Go. +package libsacloud + +// Version バージョン +const Version = "1.0.0-rc5" diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/account.go b/vendor/github.com/sacloud/libsacloud/sacloud/account.go new file mode 100644 index 000000000..8ece8f448 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/account.go @@ -0,0 +1,9 @@ +package sacloud + +// Account さくらのクラウド アカウント +type Account struct { + *Resource + propName // 名称 + Class string `json:",omitempty"` // リソースクラス + Code string `json:",omitempty"` // アカウントコード +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/appliance.go b/vendor/github.com/sacloud/libsacloud/sacloud/appliance.go new file mode 100644 index 000000000..892439260 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/appliance.go @@ -0,0 +1,57 @@ +package sacloud + +// Appliance アプライアンス基底クラス +type Appliance struct { + *Resource // ID + propAvailability // 有功状態 + propName // 名称 + propDescription // 説明 + propPlanID // プラン + propServiceClass // サービスクラス + propInstance // インスタンス + propSwitch // スイッチ + propInterfaces // インターフェース配列 + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + Class string `json:",omitempty"` // リソースクラス + SettingHash string `json:",omitempty"` // 設定ハッシュ値 + +} + +//HACK Appliance:Zone.IDがRoute/LoadBalancerの場合でデータ型が異なるため +//それぞれのstruct定義でZoneだけ上書きした構造体を定義して使う + +// ApplianceRemarkBase アプライアンス Remark 基底クラス +type ApplianceRemarkBase struct { + Servers []interface{} // 配下のサーバー群 + + Switch *ApplianceRemarkSwitch `json:",omitempty"` // 接続先スイッチ + VRRP *ApplianceRemarkVRRP `json:",omitempty"` // VRRP + Network *ApplianceRemarkNetwork `json:",omitempty"` // ネットワーク + + //Zone *Resource `json:",omitempty"` + //Plan *Resource +} + +//type ApplianceServer struct { +// IPAddress string `json:",omitempty"` +//} + +// ApplianceRemarkSwitch スイッチ +type ApplianceRemarkSwitch struct { + ID string `json:",omitempty"` // リソースID + propScope // スコープ +} + +// ApplianceRemarkVRRP VRRP +type ApplianceRemarkVRRP struct { + VRID int // VRID +} + +// ApplianceRemarkNetwork ネットワーク +type ApplianceRemarkNetwork struct { + NetworkMaskLen int `json:",omitempty"` // ネットワークマスク長 + DefaultRoute string // デフォルトルート + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/archive.go b/vendor/github.com/sacloud/libsacloud/sacloud/archive.go new file mode 100644 index 000000000..3b21a4911 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/archive.go @@ -0,0 +1,27 @@ +package sacloud + +// AllowArchiveSizes 作成できるアーカイブのサイズ +func AllowArchiveSizes() []string { + return []string{"20", "40", "60", "80", "100", "250", "500", "750", "1024"} +} + +// Archive アーカイブ +type Archive struct { + *Resource // ID + propAvailability // 有功状態 + propName // 名称 + propDescription // 説明 + propSizeMB // サイズ(MB単位) + propMigratedMB // コピー済みデータサイズ(MB単位) + propScope // スコープ + propCopySource // コピー元情報 + propServiceClass // サービスクラス + propPlanID // プランID + propJobStatus // マイグレーションジョブステータス + propOriginalArchiveID // オリジナルアーカイブID + propStorage // ストレージ + propBundleInfo // バンドル情報 + propTags // タグ + propIcon // アイコン + propCreatedAt // 作成日時 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/auth_status.go b/vendor/github.com/sacloud/libsacloud/sacloud/auth_status.go new file mode 100644 index 000000000..d1a562695 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/auth_status.go @@ -0,0 +1,79 @@ +package sacloud + +// AuthStatus 現在の認証状態 +type AuthStatus struct { + Account *Account // アカウント + Member *Member // 会員情報 + AuthClass EAuthClass `json:",omitempty"` // 認証クラス + AuthMethod EAuthMethod `json:",omitempty"` // 認証方法 + ExternalPermission string `json:",omitempty"` // 他サービスへのアクセス権 + IsAPIKey bool `json:",omitempty"` // APIキーでのアクセスフラグ + OperationPenalty string `json:",omitempty"` // オペレーションペナルティ + Permission EPermission `json:",omitempty"` // 権限 + IsOk bool `json:"is_ok,omitempty"` // 結果 + + // RESTFilter [unknown type] `json:",omitempty"` + // User [unknown type] `json:",omitempty"` + +} + +// -------------------------------------------------------- + +// EAuthClass 認証種別 +type EAuthClass string + +var ( + // EAuthClassAccount アカウント認証 + EAuthClassAccount = EAuthClass("account") +) + +// -------------------------------------------------------- + +// EAuthMethod 認証方法 +type EAuthMethod string + +var ( + // EAuthMethodAPIKey APIキー認証 + EAuthMethodAPIKey = EAuthMethod("apikey") +) + +// -------------------------------------------------------- + +// EExternalPermission 他サービスへのアクセス権 +type EExternalPermission string + +var ( + // EExternalPermissionBill 請求情報 + EExternalPermissionBill = EExternalPermission("bill") + // EExternalPermissionCDN ウェブアクセラレータ + EExternalPermissionCDN = EExternalPermission("cdn") +) + +// -------------------------------------------------------- + +// EOperationPenalty ペナルティ +type EOperationPenalty string + +var ( + // EOperationPenaltyNone ペナルティなし + EOperationPenaltyNone = EOperationPenalty("none") +) + +// -------------------------------------------------------- + +// EPermission アクセスレベル +type EPermission string + +var ( + // EPermissionCreate 作成・削除権限 + EPermissionCreate = EPermission("create") + + // EPermissionArrange 設定変更権限 + EPermissionArrange = EPermission("arrange") + + // EPermissionPower 電源操作権限 + EPermissionPower = EPermission("power") + + // EPermissionView リソース閲覧権限 + EPermissionView = EPermission("view") +) diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/auto_backup.go b/vendor/github.com/sacloud/libsacloud/sacloud/auto_backup.go new file mode 100644 index 000000000..d626fe821 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/auto_backup.go @@ -0,0 +1,88 @@ +package sacloud + +import ( + "encoding/json" + "fmt" +) + +// AutoBackup 自動バックアップ(CommonServiceItem) +type AutoBackup struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + + Status *AutoBackupStatus `json:",omitempty"` // ステータス + Provider *AutoBackupProvider `json:",omitempty"` // プロバイダ + Settings *AutoBackupSettings `json:",omitempty"` // 設定 + +} + +// AutoBackupSettings 自動バックアップ設定 +type AutoBackupSettings struct { + AccountID json.Number `json:"AccountId,omitempty"` // アカウントID + DiskID string `json:"DiskId,omitempty"` // ディスクID + ZoneID int64 `json:"ZoneId,omitempty"` // ゾーンID + ZoneName string `json:",omitempty"` // ゾーン名称 + Autobackup *AutoBackupRecordSets `json:",omitempty"` // 自動バックアップ定義 + +} + +// AutoBackupStatus 自動バックアップステータス +type AutoBackupStatus struct { + AccountID json.Number `json:"AccountId,omitempty"` // アカウントID + DiskID string `json:"DiskId,omitempty"` // ディスクID + ZoneID int64 `json:"ZoneId,omitempty"` // ゾーンID + ZoneName string `json:",omitempty"` // ゾーン名称 +} + +// AutoBackupProvider 自動バックアッププロバイダ +type AutoBackupProvider struct { + Class string `json:",omitempty"` // クラス +} + +// CreateNewAutoBackup 自動バックアップ 作成(CommonServiceItem) +func CreateNewAutoBackup(backupName string, diskID int64) *AutoBackup { + return &AutoBackup{ + Resource: &Resource{}, + propName: propName{Name: backupName}, + Status: &AutoBackupStatus{ + DiskID: fmt.Sprintf("%d", diskID), + }, + Provider: &AutoBackupProvider{ + Class: "autobackup", + }, + Settings: &AutoBackupSettings{ + Autobackup: &AutoBackupRecordSets{ + BackupSpanType: "weekdays", + }, + }, + } +} + +// AllowAutoBackupWeekdays 自動バックアップ実行曜日リスト +func AllowAutoBackupWeekdays() []string { + return []string{"mon", "tue", "wed", "thu", "fri", "sat", "sun"} +} + +// AutoBackupRecordSets 自動バックアップ定義 +type AutoBackupRecordSets struct { + BackupSpanType string // バックアップ間隔タイプ + BackupSpanWeekdays []string // バックアップ実施曜日 + MaximumNumberOfArchives int // 世代数 + +} + +// SetBackupSpanWeekdays バックアップ実行曜日設定 +func (a *AutoBackup) SetBackupSpanWeekdays(weekdays []string) { + a.Settings.Autobackup.BackupSpanWeekdays = weekdays +} + +// SetBackupMaximumNumberOfArchives 世代数設定 +func (a *AutoBackup) SetBackupMaximumNumberOfArchives(max int) { + a.Settings.Autobackup.MaximumNumberOfArchives = max +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/bill.go b/vendor/github.com/sacloud/libsacloud/sacloud/bill.go new file mode 100644 index 000000000..45143acfb --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/bill.go @@ -0,0 +1,32 @@ +package sacloud + +import "time" + +// Bill 請求情報 +type Bill struct { + Amount int64 `json:",omitempty"` // 金額 + BillID int64 `json:",omitempty"` // 請求ID + Date *time.Time `json:",omitempty"` // 請求日 + MemberID string `json:",omitempty"` // 会員ID + Paid bool `json:",omitempty"` // 支払済フラグ + PayLimit *time.Time `json:",omitempty"` // 支払い期限 + PaymentClassID int `json:",omitempty"` // 支払いクラスID + +} + +// BillDetail 支払い明細情報 +type BillDetail struct { + Amount int64 `json:",omitempty"` // 金額 + ContractID int64 `json:",omitempty"` // 契約ID + Description string `json:",omitempty"` // 説明 + Index int `json:",omitempty"` // インデックス + ServiceClassID int64 `json:",omitempty"` // サービスクラスID + Usage int64 `json:",omitempty"` // 秒数 + Zone string `json:",omitempty"` // ゾーン + ContractEndAt *time.Time `json:",omitempty"` // 契約終了日時 +} + +// IsContractEnded 支払済か判定 +func (d *BillDetail) IsContractEnded(t time.Time) bool { + return d.ContractEndAt != nil && d.ContractEndAt.Before(t) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/bridge.go b/vendor/github.com/sacloud/libsacloud/sacloud/bridge.go new file mode 100644 index 000000000..b9a22d8ae --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/bridge.go @@ -0,0 +1,30 @@ +package sacloud + +import ( + "encoding/json" +) + +// Bridge ブリッジ +type Bridge struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propRegion // リージョン + propCreatedAt // 作成日時 + + Info *struct { // インフォ + Switches []*struct { // 接続スイッチリスト + *Switch // スイッチ + ID json.Number `json:",omitempty"` // (HACK) ID + } + } + + SwitchInZone *struct { // ゾーン内接続スイッチ + *Resource // ID + propScope // スコープ + Name string `json:",omitempty"` // 名称 + ServerCount int `json:",omitempty"` // 接続サーバー数 + ApplianceCount int `json:",omitempty"` // 接続アプライアンス数 + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/cdrom.go b/vendor/github.com/sacloud/libsacloud/sacloud/cdrom.go new file mode 100644 index 000000000..dc80768ed --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/cdrom.go @@ -0,0 +1,17 @@ +package sacloud + +// CDROM ISOイメージ(CDROM) +type CDROM struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propAvailability // 有功状態 + propSizeMB // サイズ(MB単位) + propScope // スコープ + propServiceClass // サービスクラス + propStorageClass // ストレージクラス + propStorage // ストレージ + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go b/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go new file mode 100644 index 000000000..35e88afe9 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/common_types.go @@ -0,0 +1,326 @@ +package sacloud + +import ( + "fmt" + "strconv" + "time" +) + +// Resource IDを持つ、さくらのクラウド上のリソース +type Resource struct { + ID int64 // ID +} + +// ResourceIDHolder ID保持インターフェース +type ResourceIDHolder interface { + SetID(int64) + GetID() int64 +} + +// EmptyID 空ID +const EmptyID int64 = 0 + +// NewResource 新規リソース作成 +func NewResource(id int64) *Resource { + return &Resource{ID: id} +} + +// NewResourceByStringID ID文字列からリソース作成 +func NewResourceByStringID(id string) *Resource { + intID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + panic(err) + } + return &Resource{ID: intID} +} + +// SetID ID 設定 +func (n *Resource) SetID(id int64) { + n.ID = id +} + +// GetID ID 取得 +func (n *Resource) GetID() int64 { + if n == nil { + return -1 + } + return n.ID +} + +// GetStrID 文字列でID取得 +func (n *Resource) GetStrID() string { + if n == nil { + return "" + } + return fmt.Sprintf("%d", n.ID) +} + +// EAvailability 有効状態 +type EAvailability string + +var ( + // EAAvailable 有効 + EAAvailable = EAvailability("available") + // EAUploading アップロード中 + EAUploading = EAvailability("uploading") + // EAFailed 失敗 + EAFailed = EAvailability("failed") + // EAMigrating マイグレーション中 + EAMigrating = EAvailability("migrating") +) + +// IsAvailable 有効状態が"有効"か判定 +func (e EAvailability) IsAvailable() bool { + return e == EAAvailable +} + +// IsUploading 有効状態が"アップロード中"か判定 +func (e EAvailability) IsUploading() bool { + return e == EAUploading +} + +// IsFailed 有効状態が"失敗"か判定 +func (e EAvailability) IsFailed() bool { + return e == EAFailed +} + +// IsMigrating 有効状態が"マイグレーション中"か判定 +func (e EAvailability) IsMigrating() bool { + return e == EAMigrating +} + +// EInterfaceDriver インターフェースドライバ +type EInterfaceDriver string + +var ( + // InterfaceDriverVirtIO virtio + InterfaceDriverVirtIO = EInterfaceDriver("virtio") + // InterfaceDriverE1000 e1000 + InterfaceDriverE1000 = EInterfaceDriver("e1000") +) + +// EServerInstanceStatus サーバーインスタンスステータス +type EServerInstanceStatus struct { + Status string `json:",omitempty"` // 現在のステータス + BeforeStatus string `json:",omitempty"` // 前のステータス +} + +// IsUp インスタンスが起動しているか判定 +func (e *EServerInstanceStatus) IsUp() bool { + return e.Status == "up" +} + +// IsDown インスタンスがダウンしているか確認 +func (e *EServerInstanceStatus) IsDown() bool { + return e.Status == "down" +} + +// GetStatus ステータス 取得 +func (e *EServerInstanceStatus) GetStatus() string { + return e.Status +} + +// GetBeforeStatus 以前のステータス 取得 +func (e *EServerInstanceStatus) GetBeforeStatus() string { + return e.BeforeStatus +} + +// EScope スコープ +type EScope string + +var ( + // ESCopeShared sharedスコープ + ESCopeShared = EScope("shared") + // ESCopeUser userスコープ + ESCopeUser = EScope("user") +) + +// EDiskConnection ディスク接続方法 +type EDiskConnection string + +// SakuraCloudResources さくらのクラウド上のリソース種別一覧 +type SakuraCloudResources struct { + Server *Server `json:",omitempty"` // サーバー + Disk *Disk `json:",omitempty"` // ディスク + Note *Note `json:",omitempty"` // スタートアップスクリプト + Archive *Archive `json:",omitempty"` // アーカイブ + PacketFilter *PacketFilter `json:",omitempty"` // パケットフィルタ + PrivateHost *PrivateHost `json:",omitempty"` // 専有ホスト + Bridge *Bridge `json:",omitempty"` // ブリッジ + Icon *Icon `json:",omitempty"` // アイコン + Image *Image `json:",omitempty"` // 画像 + Interface *Interface `json:",omitempty"` // インターフェース + Internet *Internet `json:",omitempty"` // ルーター + IPAddress *IPAddress `json:",omitempty"` // IPv4アドレス + IPv6Addr *IPv6Addr `json:",omitempty"` // IPv6アドレス + IPv6Net *IPv6Net `json:",omitempty"` // IPv6ネットワーク + License *License `json:",omitempty"` // ライセンス + Switch *Switch `json:",omitempty"` // スイッチ + CDROM *CDROM `json:",omitempty"` // ISOイメージ + SSHKey *SSHKey `json:",omitempty"` // 公開鍵 + Subnet *Subnet `json:",omitempty"` // IPv4ネットワーク + DiskPlan *ProductDisk `json:",omitempty"` // ディスクプラン + InternetPlan *ProductInternet `json:",omitempty"` // ルータープラン + LicenseInfo *ProductLicense `json:",omitempty"` // ライセンス情報 + ServerPlan *ProductServer `json:",omitempty"` // サーバープラン + PrivateHostPlan *ProductPrivateHost `json:",omitempty"` // 専有ホストプラン + Region *Region `json:",omitempty"` // リージョン + Zone *Zone `json:",omitempty"` // ゾーン + FTPServer *FTPServer `json:",omitempty"` // FTPサーバー情報 + WebAccelSite *WebAccelSite `json:"Site,omitempty"` // ウェブアクセラレータ サイト + //REMARK: CommonServiceItemとApplianceはapiパッケージにて別途定義 +} + +// SakuraCloudResourceList さくらのクラウド上のリソース種別一覧(複数形) +type SakuraCloudResourceList struct { + Servers []Server `json:",omitempty"` // サーバー + Disks []Disk `json:",omitempty"` // ディスク + Notes []Note `json:",omitempty"` // スタートアップスクリプト + Archives []Archive `json:",omitempty"` // アーカイブ + PacketFilters []PacketFilter `json:",omitempty"` // パケットフィルタ + PrivateHosts []PrivateHost `json:",omitempty"` // 専有ホスト + Bridges []Bridge `json:",omitempty"` // ブリッジ + Icons []Icon `json:",omitempty"` // アイコン + Interfaces []Interface `json:",omitempty"` // インターフェース + Internet []Internet `json:",omitempty"` // ルーター + IPAddress []IPAddress `json:",omitempty"` // IPv4アドレス + IPv6Addrs []IPv6Addr `json:",omitempty"` // IPv6アドレス + IPv6Nets []IPv6Net `json:",omitempty"` // IPv6ネットワーク + Licenses []License `json:",omitempty"` // ライセンス + Switches []Switch `json:",omitempty"` // スイッチ + CDROMs []CDROM `json:",omitempty"` // ISOイメージ + SSHKeys []SSHKey `json:",omitempty"` // 公開鍵 + Subnets []Subnet `json:",omitempty"` // IPv4ネットワーク + DiskPlans []ProductDisk `json:",omitempty"` // ディスクプラン + InternetPlans []ProductInternet `json:",omitempty"` // ルータープラン + LicenseInfo []ProductLicense `json:",omitempty"` // ライセンス情報 + ServerPlans []ProductServer `json:",omitempty"` // サーバープラン + PrivateHostPlans []ProductPrivateHost `json:",omitempty"` // 専有ホストプラン + Regions []Region `json:",omitempty"` // リージョン + Zones []Zone `json:",omitempty"` // ゾーン + ServiceClasses []PublicPrice `json:",omitempty"` // サービスクラス(価格情報) + WebAccelSites []WebAccelSite `json:"Sites,omitempty"` // ウェブアクセラレータ サイト + + //REMARK:CommonServiceItemとApplianceはapiパッケージにて別途定義 +} + +// Request APIリクエスト型 +type Request struct { + SakuraCloudResources // さくらのクラウドリソース + From int `json:",omitempty"` // ページング FROM + Count int `json:",omitempty"` // 取得件数 + Sort []string `json:",omitempty"` // ソート + Filter map[string]interface{} `json:",omitempty"` // フィルタ + Exclude []string `json:",omitempty"` // 除外する項目 + Include []string `json:",omitempty"` // 取得する項目 + +} + +// AddFilter フィルタの追加 +func (r *Request) AddFilter(key string, value interface{}) *Request { + if r.Filter == nil { + r.Filter = map[string]interface{}{} + } + r.Filter[key] = value + return r +} + +// AddSort ソートの追加 +func (r *Request) AddSort(keyName string) *Request { + if r.Sort == nil { + r.Sort = []string{} + } + r.Sort = append(r.Sort, keyName) + return r +} + +// AddExclude 除外対象の追加 +func (r *Request) AddExclude(keyName string) *Request { + if r.Exclude == nil { + r.Exclude = []string{} + } + r.Exclude = append(r.Exclude, keyName) + return r +} + +// AddInclude 選択対象の追加 +func (r *Request) AddInclude(keyName string) *Request { + if r.Include == nil { + r.Include = []string{} + } + r.Include = append(r.Include, keyName) + return r +} + +// ResultFlagValue レスポンス値でのフラグ項目 +type ResultFlagValue struct { + IsOk bool `json:"is_ok,omitempty"` // is_ok項目 + Success bool `json:",omitempty"` // success項目 +} + +// SearchResponse 検索レスポンス +type SearchResponse struct { + Total int `json:",omitempty"` // トータル件数 + From int `json:",omitempty"` // ページング開始ページ + Count int `json:",omitempty"` // 件数 + ResponsedAt *time.Time `json:",omitempty"` // 応答日時 + *SakuraCloudResourceList // さくらのクラウドリソース(複数形) +} + +// Response レスポンス型 +type Response struct { + *ResultFlagValue // フラグ値 + *SakuraCloudResources // さくらのクラウドリソース(単数形) +} + +// ResultErrorValue レスポンスエラー型 +type ResultErrorValue struct { + IsFatal bool `json:"is_fatal,omitempty"` // IsFatal + Serial string `json:"serial,omitempty"` // Serial + Status string `json:"status,omitempty"` // Status + ErrorCode string `json:"error_code,omitempty"` // ErrorCode + ErrorMessage string `json:"error_msg,omitempty"` // ErrorMessage + +} + +// MigrationJobStatus マイグレーションジョブステータス +type MigrationJobStatus struct { + Status string `json:",omitempty"` // ステータス + + Delays *struct { // Delays + Start *struct { // 開始 + Max int `json:",omitempty"` // 最大 + Min int `json:",omitempty"` // 最小 + } `json:",omitempty"` + + Finish *struct { // 終了 + Max int `json:",omitempty"` // 最大 + Min int `json:",omitempty"` // 最小 + } `json:",omitempty"` + } +} + +var ( + // TagGroupA サーバをグループ化し起動ホストを分離します(グループA) + TagGroupA = "@group=a" + // TagGroupB サーバをグループ化し起動ホストを分離します(グループB) + TagGroupB = "@group=b" + // TagGroupC サーバをグループ化し起動ホストを分離します(グループC) + TagGroupC = "@group=b" + // TagGroupD サーバをグループ化し起動ホストを分離します(グループD) + TagGroupD = "@group=b" + + // TagAutoReboot サーバ停止時に自動起動します + TagAutoReboot = "@auto-reboot" + + // TagKeyboardUS リモートスクリーン画面でUSキーボード入力します + TagKeyboardUS = "@keyboard-us" + + // TagBootCDROM 優先ブートデバイスをCD-ROMに設定します + TagBootCDROM = "@boot-cdrom" + // TagBootNetwork 優先ブートデバイスをPXE bootに設定します + TagBootNetwork = "@boot-network" +) + +// DatetimeLayout さくらのクラウドAPIで利用される日付型のレイアウト(RFC3339) +var DatetimeLayout = "2006-01-02T15:04:05-07:00" diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/database.go b/vendor/github.com/sacloud/libsacloud/sacloud/database.go new file mode 100644 index 000000000..447f9cd94 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/database.go @@ -0,0 +1,435 @@ +package sacloud + +import ( + "encoding/json" + "strings" +) + +// Database データベース(appliance) +type Database struct { + *Appliance // アプライアンス共通属性 + + Remark *DatabaseRemark `json:",omitempty"` // リマーク + Settings *DatabaseSettings `json:",omitempty"` // データベース設定 +} + +// DatabaseRemark データベースリマーク +type DatabaseRemark struct { + *ApplianceRemarkBase + propPlanID // プランID + DBConf *DatabaseCommonRemarks // コンフィグ + Network *DatabaseRemarkNetwork // ネットワーク + SourceAppliance *Resource // クローン元DB + Zone struct { // ゾーン + ID json.Number `json:",omitempty"` // ゾーンID + } +} + +// DatabaseRemarkNetwork ネットワーク +type DatabaseRemarkNetwork struct { + NetworkMaskLen int `json:",omitempty"` // ネットワークマスク長 + DefaultRoute string `json:",omitempty"` // デフォルトルート +} + +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (s *DatabaseRemarkNetwork) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `[]` { + return nil + } + + tmp := &struct { + // NetworkMaskLen + NetworkMaskLen int `json:",omitempty"` + // DefaultRoute + DefaultRoute string `json:",omitempty"` + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + s.NetworkMaskLen = tmp.NetworkMaskLen + s.DefaultRoute = tmp.DefaultRoute + return nil +} + +// DatabaseCommonRemarks リマークリスト +type DatabaseCommonRemarks struct { + Common *DatabaseCommonRemark // Common +} + +// DatabaseCommonRemark リマーク +type DatabaseCommonRemark struct { + DatabaseName string `json:",omitempty"` // 名称 + DatabaseRevision string `json:",omitempty"` // リビジョン + DatabaseTitle string `json:",omitempty"` // タイトル + DatabaseVersion string `json:",omitempty"` // バージョン + ReplicaPassword string `json:",omitempty"` // レプリケーションパスワード + ReplicaUser string `json:",omitempty"` // レプリケーションユーザー +} + +// DatabaseSettings データベース設定リスト +type DatabaseSettings struct { + DBConf *DatabaseSetting `json:",omitempty"` // コンフィグ +} + +// DatabaseSetting データベース設定 +type DatabaseSetting struct { + Backup *DatabaseBackupSetting `json:",omitempty"` // バックアップ設定 + Common *DatabaseCommonSetting `json:",oitempty"` // 共通設定 +} + +// DatabaseServer データベースサーバー情報 +type DatabaseServer struct { + IPAddress string `json:",omitempty"` // IPアドレス + Port string `json:",omitempty"` // ポート + Enabled string `json:",omitempty"` // 有効/無効 + Status string `json:",omitempty"` // ステータス + ActiveConn string `json:",omitempty"` // アクティブコネクション +} + +// DatabasePlan プラン +type DatabasePlan int + +var ( + // DatabasePlanMini ミニプラン(後方互換用) + DatabasePlanMini = DatabasePlan(10) + // DatabasePlan10G 10Gプラン + DatabasePlan10G = DatabasePlan(10) + // DatabasePlan30G 30Gプラン + DatabasePlan30G = DatabasePlan(30) + // DatabasePlan90G 90Gプラン + DatabasePlan90G = DatabasePlan(90) + // DatabasePlan240G 240Gプラン + DatabasePlan240G = DatabasePlan(240) + // DatabasePlan500G 500Gプラン + DatabasePlan500G = DatabasePlan(500) + // DatabasePlan1T 1Tプラン + DatabasePlan1T = DatabasePlan(1000) +) + +// AllowDatabasePlans 指定可能なデータベースプラン +func AllowDatabasePlans() []int { + return []int{ + int(DatabasePlan10G), + int(DatabasePlan30G), + int(DatabasePlan90G), + int(DatabasePlan240G), + int(DatabasePlan500G), + int(DatabasePlan1T), + } +} + +// DatabaseBackupSetting バックアップ設定 +type DatabaseBackupSetting struct { + Rotate int `json:",omitempty"` // ローテーション世代数 + Time string `json:",omitempty"` // 開始時刻 +} + +// DatabaseCommonSetting 共通設定 +type DatabaseCommonSetting struct { + DefaultUser string `json:",omitempty"` // ユーザー名 + UserPassword string `json:",omitempty"` // ユーザーパスワード + WebUI interface{} `json:",omitempty"` // WebUIのIPアドレス or FQDN + ServicePort string // ポート番号 + SourceNetwork SourceNetwork // 接続許可ネットワーク +} + +// SourceNetwork 接続許可ネットワーク +type SourceNetwork []string + +// UnmarshalJSON JSONアンマーシャル(配列と文字列が混在するためここで対応) +func (s *SourceNetwork) UnmarshalJSON(data []byte) error { + // SourceNetworkが未設定の場合、APIレスポンスが""となるため回避する + if string(data) == `""` { + return nil + } + + tmp := []string{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + source := SourceNetwork(tmp) + *s = source + return nil +} + +// MarshalJSON JSONマーシャル(配列と文字列が混在するためここで対応) +func (s *SourceNetwork) MarshalJSON() ([]byte, error) { + if s == nil { + return []byte(""), nil + } + + list := []string(*s) + if len(list) == 0 || (len(list) == 1 && list[0] == "") { + return []byte(`""`), nil + } + + return json.Marshal(list) +} + +// CreateDatabaseValue データベース作成用パラメータ +type CreateDatabaseValue struct { + Plan DatabasePlan // プラン + AdminPassword string // 管理者パスワード + DefaultUser string // ユーザー名 + UserPassword string // パスワード + SourceNetwork []string // 接続許可ネットワーク + ServicePort string // ポート + // BackupRotate int // バックアップ世代数 + BackupTime string // バックアップ開始時間 + SwitchID string // 接続先スイッチ + IPAddress1 string // IPアドレス1 + MaskLen int // ネットワークマスク長 + DefaultRoute string // デフォルトルート + Name string // 名称 + Description string // 説明 + Tags []string // タグ + Icon *Resource // アイコン + WebUI bool // WebUI有効 + DatabaseName string // データベース名 + DatabaseRevision string // リビジョン + DatabaseTitle string // データベースタイトル + DatabaseVersion string // データベースバージョン + ReplicaUser string // ReplicaUser レプリケーションユーザー + SourceAppliance *Resource // クローン元DB + //ReplicaPassword string // in current API version , setted admin password +} + +// NewCreatePostgreSQLDatabaseValue PostgreSQL作成用パラメーター +func NewCreatePostgreSQLDatabaseValue() *CreateDatabaseValue { + return &CreateDatabaseValue{ + DatabaseName: "postgres", + DatabaseVersion: "10", + } +} + +// NewCreateMariaDBDatabaseValue MariaDB作成用パラメーター +func NewCreateMariaDBDatabaseValue() *CreateDatabaseValue { + return &CreateDatabaseValue{ + DatabaseName: "MariaDB", + DatabaseVersion: "10.2", + } +} + +// NewCloneDatabaseValue クローンDB作成用パラメータ +func NewCloneDatabaseValue(db *Database) *CreateDatabaseValue { + return &CreateDatabaseValue{ + DatabaseName: db.Remark.DBConf.Common.DatabaseName, + DatabaseVersion: db.Remark.DBConf.Common.DatabaseVersion, + SourceAppliance: NewResource(db.ID), + } +} + +// CreateNewDatabase データベース作成 +func CreateNewDatabase(values *CreateDatabaseValue) *Database { + + db := &Database{ + // Appliance + Appliance: &Appliance{ + // Class + Class: "database", + // Name + propName: propName{Name: values.Name}, + // Description + propDescription: propDescription{Description: values.Description}, + // TagsType + propTags: propTags{ + // Tags + Tags: values.Tags, + }, + // Icon + propIcon: propIcon{ + &Icon{ + // Resource + Resource: values.Icon, + }, + }, + // Plan + //propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + }, + // Remark + Remark: &DatabaseRemark{ + // ApplianceRemarkBase + ApplianceRemarkBase: &ApplianceRemarkBase{ + // Servers + Servers: []interface{}{""}, + }, + // DBConf + DBConf: &DatabaseCommonRemarks{ + // Common + Common: &DatabaseCommonRemark{ + // DatabaseName + DatabaseName: values.DatabaseName, + // DatabaseRevision + DatabaseRevision: values.DatabaseRevision, + // DatabaseTitle + DatabaseTitle: values.DatabaseTitle, + // DatabaseVersion + DatabaseVersion: values.DatabaseVersion, + // ReplicaUser + // ReplicaUser: values.ReplicaUser, + // ReplicaPassword + // ReplicaPassword: values.AdminPassword, + }, + }, + // Plan + propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + SourceAppliance: values.SourceAppliance, + }, + // Settings + Settings: &DatabaseSettings{ + // DBConf + DBConf: &DatabaseSetting{ + // Backup + Backup: &DatabaseBackupSetting{ + // Rotate + // Rotate: values.BackupRotate, + Rotate: 8, + // Time + Time: values.BackupTime, + }, + // Common + Common: &DatabaseCommonSetting{ + // DefaultUser + DefaultUser: values.DefaultUser, + // UserPassword + UserPassword: values.UserPassword, + // SourceNetwork + SourceNetwork: SourceNetwork(values.SourceNetwork), + // ServicePort + ServicePort: values.ServicePort, + }, + }, + }, + } + + db.Remark.Switch = &ApplianceRemarkSwitch{ + // ID + ID: values.SwitchID, + } + db.Remark.Network = &DatabaseRemarkNetwork{ + // NetworkMaskLen + NetworkMaskLen: values.MaskLen, + // DefaultRoute + DefaultRoute: values.DefaultRoute, + } + + db.Remark.Servers = []interface{}{ + map[string]interface{}{"IPAddress": values.IPAddress1}, + } + + if values.WebUI { + db.Settings.DBConf.Common.WebUI = values.WebUI + } + + return db +} + +// CloneNewDatabase データベース作成 +func CloneNewDatabase(values *CreateDatabaseValue) *Database { + db := &Database{ + // Appliance + Appliance: &Appliance{ + // Class + Class: "database", + // Name + propName: propName{Name: values.Name}, + // Description + propDescription: propDescription{Description: values.Description}, + // TagsType + propTags: propTags{ + // Tags + Tags: values.Tags, + }, + // Icon + propIcon: propIcon{ + &Icon{ + // Resource + Resource: values.Icon, + }, + }, + // Plan + //propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + }, + // Remark + Remark: &DatabaseRemark{ + // ApplianceRemarkBase + ApplianceRemarkBase: &ApplianceRemarkBase{ + // Servers + Servers: []interface{}{""}, + }, + // DBConf + DBConf: &DatabaseCommonRemarks{ + // Common + Common: &DatabaseCommonRemark{ + DatabaseName: values.DatabaseName, + DatabaseVersion: values.DatabaseVersion, + }, + }, + // Plan + propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + SourceAppliance: values.SourceAppliance, + }, + // Settings + Settings: &DatabaseSettings{ + // DBConf + DBConf: &DatabaseSetting{ + // Backup + Backup: &DatabaseBackupSetting{ + // Rotate + // Rotate: values.BackupRotate, + Rotate: 8, + // Time + Time: values.BackupTime, + }, + // Common + Common: &DatabaseCommonSetting{ + // SourceNetwork + SourceNetwork: SourceNetwork(values.SourceNetwork), + // ServicePort + ServicePort: values.ServicePort, + }, + }, + }, + } + + db.Remark.Switch = &ApplianceRemarkSwitch{ + // ID + ID: values.SwitchID, + } + db.Remark.Network = &DatabaseRemarkNetwork{ + // NetworkMaskLen + NetworkMaskLen: values.MaskLen, + // DefaultRoute + DefaultRoute: values.DefaultRoute, + } + + db.Remark.Servers = []interface{}{ + map[string]interface{}{"IPAddress": values.IPAddress1}, + } + + if values.WebUI { + db.Settings.DBConf.Common.WebUI = values.WebUI + } + + return db +} + +// AddSourceNetwork 接続許可ネットワーク 追加 +func (s *Database) AddSourceNetwork(nw string) { + res := []string(s.Settings.DBConf.Common.SourceNetwork) + res = append(res, nw) + s.Settings.DBConf.Common.SourceNetwork = SourceNetwork(res) +} + +// DeleteSourceNetwork 接続許可ネットワーク 削除 +func (s *Database) DeleteSourceNetwork(nw string) { + res := []string{} + for _, s := range s.Settings.DBConf.Common.SourceNetwork { + if s != nw { + res = append(res, s) + } + } + s.Settings.DBConf.Common.SourceNetwork = SourceNetwork(res) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/database_status.go b/vendor/github.com/sacloud/libsacloud/sacloud/database_status.go new file mode 100644 index 000000000..26ed1ef36 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/database_status.go @@ -0,0 +1,128 @@ +package sacloud + +import ( + "encoding/json" + "strconv" + "strings" + "time" +) + +// DatabaseStatus データベース ステータス +type DatabaseStatus struct { + *EServerInstanceStatus + DBConf *DatabaseStatusDBConf `json:",omitempty"` +} + +// DatabaseStatusDBConf データベース設定 +type DatabaseStatusDBConf struct { + Version *DatabaseStatusVersion `json:"version,omitempty"` + Log []*DatabaseLog `json:"log,omitempty"` + Backup *DatabaseBackupInfo `json:"backup,omitempty"` +} + +// DatabaseStatusVersion データベース設定バージョン情報 +type DatabaseStatusVersion struct { + LastModified string `json:"lastmodified,omitempty"` + CommitHash string `json:"commithash,omitempty"` + Status string `json:"status,omitempty"` + Tag string `json:"tag,omitempty"` + Expire string `json:"expire,omitempty"` +} + +// DatabaseLog データベースログ +type DatabaseLog struct { + Name string `json:"name,omitempty"` + Data string `json:"data,omitempty"` +} + +// IsSystemdLog systemcltのログか判定 +func (l *DatabaseLog) IsSystemdLog() bool { + return l.Name == "systemctl" +} + +// Logs ログボディ取得 +func (l *DatabaseLog) Logs() []string { + return strings.Split(l.Data, "\n") +} + +// ID ログのID取得 +func (l *DatabaseLog) ID() string { + return l.Name +} + +// DatabaseBackupInfo データベースバックアップ情報 +type DatabaseBackupInfo struct { + History []*DatabaseBackupHistory `json:"history,omitempty"` +} + +// DatabaseBackupHistory データベースバックアップ履歴情報 +type DatabaseBackupHistory struct { + CreatedAt time.Time `json:"createdat,omitempty"` + Availability string `json:"availability,omitempty"` + RecoveredAt *time.Time `json:"recoveredat,omitempty"` + Size int64 `json:"size,omitempty"` +} + +// ID バックアップ履歴のID取得 +func (h *DatabaseBackupHistory) ID() string { + return h.CreatedAt.Format(time.RFC3339) +} + +// FormatCreatedAt 指定のレイアウトで作成日時を文字列化 +func (h *DatabaseBackupHistory) FormatCreatedAt(layout string) string { + return h.CreatedAt.Format(layout) +} + +// FormatRecoveredAt 指定のレイアウトで復元日時を文字列化 +// +// 復元日時がnilの場合は空の文字列を返す +func (h *DatabaseBackupHistory) FormatRecoveredAt(layout string) string { + if h.RecoveredAt == nil { + return "" + } + return h.RecoveredAt.Format(layout) +} + +// UnmarshalJSON JSON復号処理 +func (h *DatabaseBackupHistory) UnmarshalJSON(data []byte) error { + + var tmpMap = map[string]interface{}{} + if err := json.Unmarshal(data, &tmpMap); err != nil { + return err + } + + if recoveredAt, ok := tmpMap["recoveredat"]; ok { + if strRecoveredAt, ok := recoveredAt.(string); ok { + if _, err := time.Parse(time.RFC3339, strRecoveredAt); err != nil { + tmpMap["recoveredat"] = nil + } + } + } + + data, err := json.Marshal(tmpMap) + if err != nil { + return err + } + + tmp := &struct { + CreatedAt time.Time `json:"createdat,omitempty"` + Availability string `json:"availability,omitempty"` + RecoveredAt *time.Time `json:"recoveredat,omitempty"` + Size string `json:"size,omitempty"` + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + h.CreatedAt = tmp.CreatedAt + h.Availability = tmp.Availability + h.RecoveredAt = tmp.RecoveredAt + s, err := strconv.ParseInt(tmp.Size, 10, 64) + if err == nil { + h.Size = s + } else { + return err + } + + return nil +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/delete_cache_result.go b/vendor/github.com/sacloud/libsacloud/sacloud/delete_cache_result.go new file mode 100644 index 000000000..aa0cea463 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/delete_cache_result.go @@ -0,0 +1,8 @@ +package sacloud + +// DeleteCacheResult ウェブアクセラレータ キャッシュ削除APIレスポンス +type DeleteCacheResult struct { + URL string `json:",omitempty"` // URL + Status int `json:",omitempty"` // ステータス + Result string `json:",omitempty"` // 結果 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/disk.go b/vendor/github.com/sacloud/libsacloud/sacloud/disk.go new file mode 100644 index 000000000..9deb36ef4 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/disk.go @@ -0,0 +1,202 @@ +package sacloud + +import "fmt" + +// Disk ディスク +type Disk struct { + *Resource // ID + propAvailability // 有功状態 + propName // 名称 + propDescription // 説明 + propSizeMB // サイズ(MB単位) + propMigratedMB // コピー済みデータサイズ(MB単位) + propCopySource // コピー元情報 + propJobStatus // マイグレーションジョブステータス + propBundleInfo // バンドル情報 + propServer // サーバー + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propPlanID // プランID + propDiskConnection // ディスク接続情報 + propDistantFrom // ストレージ隔離対象ディスク + + ReinstallCount int `json:",omitempty"` // 再インストール回数 + + Storage struct { // ストレージ + *Resource // ID + Name string `json:",omitempty"` // 名称 + DiskPlan ProductDisk `json:",ommitempty"` // ディスクプラン + MountIndex int64 `json:",omitempty"` // マウント順 + Class string `json:",omitempty"` // クラス + } +} + +// DiskPlanID ディスクプランID +type DiskPlanID int64 + +const ( + // DiskPlanHDDID HDDプランID + DiskPlanHDDID = DiskPlanID(2) + // DiskPlanSSDID SSDプランID + DiskPlanSSDID = DiskPlanID(4) + // DiskConnectionVirtio 準仮想化モード(virtio) + DiskConnectionVirtio EDiskConnection = "virtio" + // DiskConnectionIDE IDE + DiskConnectionIDE EDiskConnection = "ide" +) + +var ( + // DiskPlanHDD HDDプラン + DiskPlanHDD = &Resource{ID: int64(DiskPlanHDDID)} + // DiskPlanSSD SSDプラン + DiskPlanSSD = &Resource{ID: int64(DiskPlanSSDID)} +) + +// ToResource ディスクプランIDからリソースへの変換 +func (d DiskPlanID) ToResource() *Resource { + return &Resource{ID: int64(d)} +} + +// CreateNewDisk ディスクの作成 +func CreateNewDisk() *Disk { + return &Disk{ + propPlanID: propPlanID{Plan: DiskPlanSSD}, + propDiskConnection: propDiskConnection{Connection: DiskConnectionVirtio}, + propSizeMB: propSizeMB{SizeMB: 20480}, + } +} + +// SetDiskPlan プラン文字列(ssd or sdd)からプラン設定 +func (d *Disk) SetDiskPlan(strPlan string) { + switch strPlan { + case "ssd": + d.Plan = DiskPlanSSD + case "hdd": + d.Plan = DiskPlanHDD + default: + panic(fmt.Errorf("Invalid plan:%s", strPlan)) + } +} + +// SetDiskPlanToHDD HDDプラン 設定 +func (d *Disk) SetDiskPlanToHDD() { + d.Plan = DiskPlanHDD +} + +// SetDiskPlanToSSD SSDプラン 設定 +func (d *Disk) SetDiskPlanToSSD() { + d.Plan = DiskPlanSSD +} + +// DiskEditValue ディスクの修正用パラメータ +// +// 設定を行う項目のみ値をセットする。値のセットにはセッターを利用すること。 +type DiskEditValue struct { + Password *string `json:",omitempty"` // パスワード + SSHKey *SSHKey `json:",omitempty"` // 公開鍵(単体) + SSHKeys []*SSHKey `json:",omitempty"` // 公開鍵(複数) + DisablePWAuth *bool `json:",omitempty"` // パスワード認証無効化フラグ + HostName *string `json:",omitempty"` // ホスト名 + Notes []*Resource `json:",omitempty"` // スタートアップスクリプト + UserIPAddress *string `json:",omitempty"` // IPアドレス + UserSubnet *struct { // サブネット情報 + DefaultRoute string `json:",omitempty"` // デフォルトルート + NetworkMaskLen string `json:",omitempty"` // ネットワークマスク長 + } `json:",omitempty"` +} + +// SetHostName ホスト名 設定 +func (d *DiskEditValue) SetHostName(value string) { + d.HostName = &value +} + +// SetPassword パスワード 設定 +func (d *DiskEditValue) SetPassword(value string) { + d.Password = &value +} + +// AddSSHKeys 公開鍵 設定 +func (d *DiskEditValue) AddSSHKeys(keyID string) { + if d.SSHKeys == nil { + d.SSHKeys = []*SSHKey{} + } + d.SSHKeys = append(d.SSHKeys, &SSHKey{Resource: NewResourceByStringID(keyID)}) +} + +// SetSSHKeys 公開鍵 設定 +func (d *DiskEditValue) SetSSHKeys(keyIDs []string) { + if d.SSHKeys == nil { + d.SSHKeys = []*SSHKey{} + } + for _, keyID := range keyIDs { + d.SSHKeys = append(d.SSHKeys, &SSHKey{Resource: NewResourceByStringID(keyID)}) + } +} + +// AddSSHKeyByString 公開鍵(文字列) 追加 +func (d *DiskEditValue) AddSSHKeyByString(key string) { + if d.SSHKeys == nil { + d.SSHKeys = []*SSHKey{} + } + d.SSHKeys = append(d.SSHKeys, &SSHKey{PublicKey: key}) +} + +// SetSSHKeyByString 公開鍵(文字列) 設定 +func (d *DiskEditValue) SetSSHKeyByString(keys []string) { + if d.SSHKeys == nil { + d.SSHKeys = []*SSHKey{} + } + for _, key := range keys { + d.SSHKeys = append(d.SSHKeys, &SSHKey{PublicKey: key}) + } +} + +// SetDisablePWAuth パスワード認証無効化フラグ 設定 +func (d *DiskEditValue) SetDisablePWAuth(disable bool) { + d.DisablePWAuth = &disable +} + +// SetNotes スタートアップスクリプト 設定 +func (d *DiskEditValue) SetNotes(noteIDs []string) { + d.Notes = []*Resource{} + for _, noteID := range noteIDs { + d.Notes = append(d.Notes, NewResourceByStringID(noteID)) + } + +} + +// AddNote スタートアップスクリプト 追加 +func (d *DiskEditValue) AddNote(noteID string) { + if d.Notes == nil { + d.Notes = []*Resource{} + } + d.Notes = append(d.Notes, NewResourceByStringID(noteID)) +} + +// SetUserIPAddress IPアドレス 設定 +func (d *DiskEditValue) SetUserIPAddress(ip string) { + d.UserIPAddress = &ip +} + +// SetDefaultRoute デフォルトルート 設定 +func (d *DiskEditValue) SetDefaultRoute(route string) { + if d.UserSubnet == nil { + d.UserSubnet = &struct { + DefaultRoute string `json:",omitempty"` + NetworkMaskLen string `json:",omitempty"` + }{} + } + d.UserSubnet.DefaultRoute = route +} + +// SetNetworkMaskLen ネットワークマスク長 設定 +func (d *DiskEditValue) SetNetworkMaskLen(length string) { + if d.UserSubnet == nil { + d.UserSubnet = &struct { + DefaultRoute string `json:",omitempty"` + NetworkMaskLen string `json:",omitempty"` + }{} + } + d.UserSubnet.NetworkMaskLen = length +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/dns.go b/vendor/github.com/sacloud/libsacloud/sacloud/dns.go new file mode 100644 index 000000000..edfbbc63e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/dns.go @@ -0,0 +1,189 @@ +package sacloud + +import ( + "fmt" + "strings" +) + +// DNS DNS(CommonServiceItem) +type DNS struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + + Status DNSStatus `json:",omitempty"` // ステータス + Provider DNSProvider `json:",omitempty"` // プロバイダ + Settings DNSSettings `json:",omitempty"` // 設定 + +} + +// DNSSettings DNS設定リスト +type DNSSettings struct { + DNS DNSRecordSets `json:",omitempty"` // DNSレコード設定リスト +} + +// DNSStatus DNSステータス +type DNSStatus struct { + Zone string `json:",omitempty"` // 対象ゾーン + NS []string `json:",omitempty"` // ネームサーバーリスト +} + +// DNSProvider プロバイダ +type DNSProvider struct { + Class string `json:",omitempty"` // クラス +} + +// CreateNewDNS DNS作成 +func CreateNewDNS(zoneName string) *DNS { + return &DNS{ + Resource: &Resource{}, + propName: propName{Name: zoneName}, + Status: DNSStatus{ + Zone: zoneName, + }, + Provider: DNSProvider{ + Class: "dns", + }, + Settings: DNSSettings{ + DNS: DNSRecordSets{}, + }, + } +} + +// AllowDNSTypes DNSレコード種別リスト +func AllowDNSTypes() []string { + return []string{"A", "AAAA", "CNAME", "NS", "MX", "TXT", "SRV"} +} + +// SetZone DNSゾーン名 設定 +func (d *DNS) SetZone(zone string) { + d.Name = zone + d.Status.Zone = zone +} + +// HasDNSRecord DNSレコード設定を保持しているか判定 +func (d *DNS) HasDNSRecord() bool { + return len(d.Settings.DNS.ResourceRecordSets) > 0 +} + +// CreateNewRecord DNSレコード作成(汎用) +func (d *DNS) CreateNewRecord(name string, rtype string, rdata string, ttl int) *DNSRecordSet { + return &DNSRecordSet{ + // Name + Name: name, + // Type + Type: rtype, + // RData + RData: rdata, + // TTL + TTL: ttl, + } +} + +// CreateNewMXRecord DNSレコード作成(MXレコード) +func (d *DNS) CreateNewMXRecord(name string, rdata string, ttl int, priority int) *DNSRecordSet { + if rdata != "" && !strings.HasSuffix(rdata, ".") { + rdata = rdata + "." + } + return &DNSRecordSet{ + // Name + Name: name, + // Type + Type: "MX", + // RData + RData: fmt.Sprintf("%d %s", priority, rdata), + // TTL + TTL: ttl, + } +} + +// CreateNewSRVRecord DNSレコード作成(SRVレコード) +func (d *DNS) CreateNewSRVRecord(name string, rdata string, ttl int, priority int, weight int, port int) *DNSRecordSet { + return &DNSRecordSet{ + // Name + Name: name, + // Type + Type: "SRV", + // RData + RData: fmt.Sprintf("%d %d %d %s", priority, weight, port, rdata), + // TTL + TTL: ttl, + } +} + +// AddRecord レコードの追加 +func (d *DNS) AddRecord(record *DNSRecordSet) { + var recordSet = d.Settings.DNS.ResourceRecordSets + var isExist = false + for i := range recordSet { + if recordSet[i].Name == record.Name && recordSet[i].Type == record.Type && recordSet[i].RData == record.RData { + d.Settings.DNS.ResourceRecordSets[i].TTL = record.TTL + isExist = true + } + } + + if !isExist { + d.Settings.DNS.ResourceRecordSets = append(d.Settings.DNS.ResourceRecordSets, *record) + } + +} + +// ClearRecords レコード クリア +func (d *DNS) ClearRecords() { + d.Settings.DNS = DNSRecordSets{} +} + +// DNSRecordSets DNSレコード設定リスト +type DNSRecordSets struct { + // ResourceRecordSets DNSレコード設定リスト + ResourceRecordSets []DNSRecordSet +} + +// AddDNSRecordSet ホスト名とIPアドレスにてAレコードを登録 +func (d *DNSRecordSets) AddDNSRecordSet(name string, ip string) { + var record DNSRecordSet + var isExist = false + for i := range d.ResourceRecordSets { + if d.ResourceRecordSets[i].Name == name && d.ResourceRecordSets[i].Type == "A" { + d.ResourceRecordSets[i].RData = ip + isExist = true + } + } + + if !isExist { + record = DNSRecordSet{ + // Name + Name: name, + // Type + Type: "A", + // RData + RData: ip, + } + d.ResourceRecordSets = append(d.ResourceRecordSets, record) + } +} + +// DeleteDNSRecordSet ホスト名とIPアドレスにてAレコードを削除する +func (d *DNSRecordSets) DeleteDNSRecordSet(name string, ip string) { + res := []DNSRecordSet{} + for i := range d.ResourceRecordSets { + if d.ResourceRecordSets[i].Name != name || d.ResourceRecordSets[i].Type != "A" || d.ResourceRecordSets[i].RData != ip { + res = append(res, d.ResourceRecordSets[i]) + } + } + + d.ResourceRecordSets = res +} + +// DNSRecordSet DNSレコード設定 +type DNSRecordSet struct { + Name string `json:",omitempty"` // ホスト名 + Type string `json:",omitempty"` // レコードタイプ + RData string `json:",omitempty"` // レコードデータ + TTL int `json:",omitempty"` // TTL +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/doc.go b/vendor/github.com/sacloud/libsacloud/sacloud/doc.go new file mode 100644 index 000000000..2cb4fdcfc --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/doc.go @@ -0,0 +1,3 @@ +// Package sacloud is define models for SakuraCloud. +// +package sacloud diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/feed.go b/vendor/github.com/sacloud/libsacloud/sacloud/feed.go new file mode 100644 index 000000000..f4c918404 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/feed.go @@ -0,0 +1,36 @@ +package sacloud + +import ( + "strconv" + "time" +) + +// NewsFeed メンテナンス/障害情報お知らせ +type NewsFeed struct { + StrDate string `json:"date,omitempty"` + Description string `json:"desc,omitempty"` + StrEventStart string `json:"event_start,omitempty"` + StrEventEnd string `json:"event_end,omitempty"` + Title string `json:"title,omitempty"` + URL string `json:"url,omitempty"` +} + +// Date 対象日時 +func (f *NewsFeed) Date() time.Time { + return f.parseTime(f.StrDate) +} + +// EventStart 掲載開始日時 +func (f *NewsFeed) EventStart() time.Time { + return f.parseTime(f.StrEventStart) +} + +// EventEnd 掲載終了日時 +func (f *NewsFeed) EventEnd() time.Time { + return f.parseTime(f.StrEventEnd) +} + +func (f *NewsFeed) parseTime(sec string) time.Time { + s, _ := strconv.ParseInt(sec, 10, 64) + return time.Unix(s, 0) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ftp_server.go b/vendor/github.com/sacloud/libsacloud/sacloud/ftp_server.go new file mode 100644 index 000000000..7c56bb683 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ftp_server.go @@ -0,0 +1,15 @@ +package sacloud + +// FTPServer FTPサーバー接続情報 +type FTPServer struct { + HostName string `json:",omitempty"` // FTPサーバーホスト名 + IPAddress string `json:",omitempty"` // FTPサーバー IPアドレス + User string `json:",omitempty"` // 接続ユーザー名 + Password string `json:",omitempty"` // パスワード + +} + +// FTPOpenRequest FTP接続オープンリクエスト +type FTPOpenRequest struct { + ChangePassword bool // パスワード変更フラグ +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/gslb.go b/vendor/github.com/sacloud/libsacloud/sacloud/gslb.go new file mode 100644 index 000000000..41f7bd02b --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/gslb.go @@ -0,0 +1,216 @@ +package sacloud + +import "fmt" + +// GSLB GSLB(CommonServiceItem) +type GSLB struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + + Status GSLBStatus `json:",omitempty"` // ステータス + Provider GSLBProvider `json:",omitempty"` // プロバイダ + Settings GSLBSettings `json:",omitempty"` // GSLB設定 + +} + +// GSLBSettings GSLB設定 +type GSLBSettings struct { + GSLB GSLBRecordSets `json:",omitempty"` // GSLB GSLBエントリー +} + +// GSLBStatus GSLBステータス +type GSLBStatus struct { + FQDN string `json:",omitempty"` // GSLBのFQDN +} + +// GSLBProvider プロバイダ +type GSLBProvider struct { + Class string `json:",omitempty"` // クラス +} + +// CreateNewGSLB GSLB作成 +func CreateNewGSLB(gslbName string) *GSLB { + return &GSLB{ + Resource: &Resource{}, + propName: propName{Name: gslbName}, + Provider: GSLBProvider{ + Class: "gslb", + }, + Settings: GSLBSettings{ + GSLB: GSLBRecordSets{ + DelayLoop: 10, + HealthCheck: defaultGSLBHealthCheck, + Weighted: "True", + Servers: []GSLBServer{}, + }, + }, + } + +} + +// AllowGSLBHealthCheckProtocol GSLB監視プロトコルリスト +func AllowGSLBHealthCheckProtocol() []string { + return []string{"http", "https", "ping", "tcp"} +} + +// HasGSLBServer GSLB配下にサーバーを保持しているか判定 +func (g *GSLB) HasGSLBServer() bool { + return len(g.Settings.GSLB.Servers) > 0 +} + +// CreateGSLBServer GSLB配下のサーバーを作成 +func (g *GSLB) CreateGSLBServer(ip string) *GSLBServer { + return &GSLBServer{ + IPAddress: ip, + Enabled: "True", + Weight: "1", + } +} + +// AddGSLBServer GSLB配下にサーバーを追加 +func (g *GSLB) AddGSLBServer(server *GSLBServer) { + var isExist = false + for i := range g.Settings.GSLB.Servers { + if g.Settings.GSLB.Servers[i].IPAddress == server.IPAddress { + g.Settings.GSLB.Servers[i].Enabled = server.Enabled + g.Settings.GSLB.Servers[i].Weight = server.Weight + isExist = true + } + } + + if !isExist { + g.Settings.GSLB.Servers = append(g.Settings.GSLB.Servers, *server) + } +} + +// ClearGSLBServer GSLB配下のサーバーをクリア +func (g *GSLB) ClearGSLBServer() { + g.Settings.GSLB.Servers = []GSLBServer{} +} + +// GSLBRecordSets GSLBエントリー +type GSLBRecordSets struct { + DelayLoop int `json:",omitempty"` // 監視間隔 + HealthCheck GSLBHealthCheck `json:",omitempty"` // ヘルスチェック + Weighted string `json:",omitempty"` // ウェイト + SorryServer string `json:",omitempty"` // ソーリーサーバー + Servers []GSLBServer // サーバー +} + +// AddServer GSLB配下のサーバーを追加 +func (g *GSLBRecordSets) AddServer(ip string) { + var record GSLBServer + var isExist = false + for i := range g.Servers { + if g.Servers[i].IPAddress == ip { + isExist = true + } + } + + if !isExist { + record = GSLBServer{ + IPAddress: ip, + Enabled: "True", + Weight: "1", + } + g.Servers = append(g.Servers, record) + } +} + +// DeleteServer GSLB配下のサーバーを削除 +func (g *GSLBRecordSets) DeleteServer(ip string) { + res := []GSLBServer{} + for i := range g.Servers { + if g.Servers[i].IPAddress != ip { + res = append(res, g.Servers[i]) + } + } + + g.Servers = res +} + +// GSLBServer GSLB配下のサーバー +type GSLBServer struct { + IPAddress string `json:",omitempty"` // IPアドレス + Enabled string `json:",omitempty"` // 有効/無効 + Weight string `json:",omitempty"` // ウェイト + +} + +// GSLBHealthCheck ヘルスチェック +type GSLBHealthCheck struct { + Protocol string `json:",omitempty"` // プロトコル + Host string `json:",omitempty"` // 対象ホスト + Path string `json:",omitempty"` // HTTP/HTTPSの場合のリクエストパス + Status string `json:",omitempty"` // 期待するステータスコード + Port string `json:",omitempty"` // ポート番号 +} + +var defaultGSLBHealthCheck = GSLBHealthCheck{ + Protocol: "http", + Host: "", + Path: "/", + Status: "200", +} + +// SetHTTPHealthCheck HTTPヘルスチェック 設定 +func (g *GSLB) SetHTTPHealthCheck(hostHeader string, path string, responseCode int) { + g.Settings.GSLB.HealthCheck.Protocol = "http" + g.Settings.GSLB.HealthCheck.Host = hostHeader + g.Settings.GSLB.HealthCheck.Path = path + g.Settings.GSLB.HealthCheck.Status = fmt.Sprintf("%d", responseCode) + g.Settings.GSLB.HealthCheck.Port = "" + +} + +// SetHTTPSHealthCheck HTTPSヘルスチェック 設定 +func (g *GSLB) SetHTTPSHealthCheck(hostHeader string, path string, responseCode int) { + g.Settings.GSLB.HealthCheck.Protocol = "https" + g.Settings.GSLB.HealthCheck.Host = hostHeader + g.Settings.GSLB.HealthCheck.Path = path + g.Settings.GSLB.HealthCheck.Status = fmt.Sprintf("%d", responseCode) + g.Settings.GSLB.HealthCheck.Port = "" +} + +// SetPingHealthCheck Pingヘルスチェック 設定 +func (g *GSLB) SetPingHealthCheck() { + g.Settings.GSLB.HealthCheck.Protocol = "ping" + g.Settings.GSLB.HealthCheck.Host = "" + g.Settings.GSLB.HealthCheck.Path = "" + g.Settings.GSLB.HealthCheck.Status = "" + g.Settings.GSLB.HealthCheck.Port = "" +} + +// SetTCPHealthCheck TCPヘルスチェック 設定 +func (g *GSLB) SetTCPHealthCheck(port int) { + g.Settings.GSLB.HealthCheck.Protocol = "tcp" + g.Settings.GSLB.HealthCheck.Host = "" + g.Settings.GSLB.HealthCheck.Path = "" + g.Settings.GSLB.HealthCheck.Status = "" + g.Settings.GSLB.HealthCheck.Port = fmt.Sprintf("%d", port) +} + +// SetDelayLoop 監視間隔秒数 設定 +func (g *GSLB) SetDelayLoop(delayLoop int) { + g.Settings.GSLB.DelayLoop = delayLoop +} + +// SetWeightedEnable 重み付け応答 有効/無効 設定 +func (g *GSLB) SetWeightedEnable(enable bool) { + v := "True" + if !enable { + v = "False" + } + g.Settings.GSLB.Weighted = v +} + +// SetSorryServer ソーリーサーバ 設定 +func (g *GSLB) SetSorryServer(server string) { + g.Settings.GSLB.SorryServer = server +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/host.go b/vendor/github.com/sacloud/libsacloud/sacloud/host.go new file mode 100644 index 000000000..f32f12cb5 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/host.go @@ -0,0 +1,6 @@ +package sacloud + +// Host さくらのクラウド ホスト(物理) +type Host struct { + propName // 名称 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/icon.go b/vendor/github.com/sacloud/libsacloud/sacloud/icon.go new file mode 100644 index 000000000..ebdffd914 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/icon.go @@ -0,0 +1,33 @@ +package sacloud + +// Icon アイコン +type Icon struct { + *Resource // ID + propAvailability // 有功状態 + propName // 名称 + propScope // スコープ + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + + URL string `json:",omitempty"` // アイコンURL + Image string `json:",omitempty"` // 画像データBase64文字列(Sizeパラメータ指定時 or 画像アップロード時に利用) +} + +// Image 画像データBASE64文字列 +type Image string + +// GetURL アイコン画像URL取得 +func (icon *Icon) GetURL() string { + return icon.URL +} + +// GetImage アイコン画像データ(base64)取得 +func (icon *Icon) GetImage() string { + return icon.Image +} + +// SetImage アイコン画像データ(base64)設定 +func (icon *Icon) SetImage(image string) { + icon.Image = image +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/instance.go b/vendor/github.com/sacloud/libsacloud/sacloud/instance.go new file mode 100644 index 000000000..701496a71 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/instance.go @@ -0,0 +1,35 @@ +package sacloud + +import "time" + +// Instance インスタンス +type Instance struct { + *EServerInstanceStatus // ステータス + Server Resource `json:",omitempty"` // サーバー + StatusChangedAt *time.Time `json:",omitempty"` // ステータス変更日時 + MigrationProgress string `json:",omitempty"` // コピージョブ進捗状態 + MigrationSchedule string `json:",omitempty"` // コピージョブスケジュール + IsMigrating bool `json:",omitempty"` // コピージョブ実施中フラグ + MigrationAllowed string `json:",omitempty"` // コピージョブ許可 + ModifiedAt *time.Time `json:",omitempty"` // 変更日時 + CDROM *CDROM `json:",omitempty"` // ISOイメージ + CDROMStorage *Storage `json:",omitempty"` // ISOイメージストレージ + + Host struct { // Host + Name string `json:",omitempty"` // ホスト名 + InfoURL string `json:",omitempty"` // インフォURL + Class string `json:",omitempty"` // クラス + Version int `json:",omitempty"` // バージョン + SystemVersion string `json:",omitempty"` // システムバージョン + } `json:",omitempty"` +} + +// HasInfoURL Host.InfoURLに値があるか +func (i *Instance) HasInfoURL() bool { + return i != nil && i.Host.InfoURL != "" +} + +// MaintenanceScheduled メンテナンス予定の有無 +func (i *Instance) MaintenanceScheduled() bool { + return i.HasInfoURL() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/interface.go b/vendor/github.com/sacloud/libsacloud/sacloud/interface.go new file mode 100644 index 000000000..557a3444e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/interface.go @@ -0,0 +1,43 @@ +package sacloud + +// Interface インターフェース(NIC) +type Interface struct { + *Resource // ID + propServer // サーバー + propSwitch // スイッチ + MACAddress string `json:",omitempty"` // MACアドレス + IPAddress string `json:",omitempty"` // IPアドレス + UserIPAddress string `json:",omitempty"` // ユーザー指定IPアドレス + HostName string `json:",omitempty"` // ホスト名 + PacketFilter *PacketFilter `json:",omitempty"` // 適用パケットフィルタ +} + +// GetMACAddress MACアドレス 取得 +func (i *Interface) GetMACAddress() string { + return i.MACAddress +} + +//GetIPAddress IPアドレス 取得 +func (i *Interface) GetIPAddress() string { + return i.IPAddress +} + +// SetUserIPAddress ユーザー指定IPアドレス 設定 +func (i *Interface) SetUserIPAddress(ip string) { + i.UserIPAddress = ip +} + +//GetUserIPAddress ユーザー指定IPアドレス 取得 +func (i *Interface) GetUserIPAddress() string { + return i.UserIPAddress +} + +// GetHostName ホスト名 取得 +func (i *Interface) GetHostName() string { + return i.HostName +} + +// GetPacketFilter 適用パケットフィルタ 取得 +func (i *Interface) GetPacketFilter() *PacketFilter { + return i.PacketFilter +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/internet.go b/vendor/github.com/sacloud/libsacloud/sacloud/internet.go new file mode 100644 index 000000000..ef7758809 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/internet.go @@ -0,0 +1,50 @@ +package sacloud + +// Internet ルーター +type Internet struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propScope // スコープ + propServiceClass // サービスクラス + propSwitch // 接続先スイッチ + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + + BandWidthMbps int `json:",omitempty"` // 帯域 + NetworkMaskLen int `json:",omitempty"` // ネットワークマスク長 + + //TODO Zone(API側起因のデータ型不一致のため) + // ZoneType +} + +// GetBandWidthMbps 帯域幅 取得 +func (i *Internet) GetBandWidthMbps() int { + return i.BandWidthMbps +} + +// SetBandWidthMbps 帯域幅 設定 +func (i *Internet) SetBandWidthMbps(v int) { + i.BandWidthMbps = v +} + +// GetNetworkMaskLen ネットワークマスク長 取得 +func (i *Internet) GetNetworkMaskLen() int { + return i.NetworkMaskLen +} + +// SetNetworkMaskLen ネットワークマスク長 設定 +func (i *Internet) SetNetworkMaskLen(v int) { + i.NetworkMaskLen = v +} + +// AllowInternetBandWidth 設定可能な帯域幅の値リスト +func AllowInternetBandWidth() []int { + return []int{100, 250, 500, 1000, 1500, 2000, 2500, 3000} +} + +// AllowInternetNetworkMaskLen 設定可能なネットワークマスク長の値リスト +func AllowInternetNetworkMaskLen() []int { + return []int{26, 27, 28} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ipaddress.go b/vendor/github.com/sacloud/libsacloud/sacloud/ipaddress.go new file mode 100644 index 000000000..0b08a4cef --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ipaddress.go @@ -0,0 +1,10 @@ +package sacloud + +// IPAddress IPアドレス(IPv4) +type IPAddress struct { + HostName string `json:",omitempty"` // HostName ホスト名 + IPAddress string `json:",omitempty"` // IPAddress IPv4アドレス + Interface *Interface `json:",omitempty"` // Interface インターフェース + Subnet *Subnet `json:",omitempty"` // Subnet IPv4サブネット + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ipv6addr.go b/vendor/github.com/sacloud/libsacloud/sacloud/ipv6addr.go new file mode 100644 index 000000000..13533fdc2 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ipv6addr.go @@ -0,0 +1,35 @@ +package sacloud + +// IPv6Addr IPアドレス(IPv6) +type IPv6Addr struct { + HostName string `json:",omitempty"` // ホスト名 + IPv6Addr string `json:",omitempty"` // IPv6アドレス + Interface *Interface `json:",omitempty"` // インターフェース + IPv6Net *IPv6Net `json:",omitempty"` // IPv6サブネット + +} + +// GetIPv6NetID IPv6アドレスが所属するIPv6NetのIDを取得 +func (a *IPv6Addr) GetIPv6NetID() int64 { + if a.IPv6Net != nil { + return a.IPv6Net.ID + } + return 0 +} + +// GetInternetID IPv6アドレスを所有するルータ+スイッチ(Internet)のIDを取得 +func (a *IPv6Addr) GetInternetID() int64 { + if a.IPv6Net != nil && a.IPv6Net.Switch != nil && a.IPv6Net.Switch.Internet != nil { + return a.IPv6Net.Switch.Internet.ID + } + return 0 +} + +// CreateNewIPv6Addr IPv6アドレス作成 +func CreateNewIPv6Addr() *IPv6Addr { + return &IPv6Addr{ + IPv6Net: &IPv6Net{ + Resource: &Resource{}, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ipv6net.go b/vendor/github.com/sacloud/libsacloud/sacloud/ipv6net.go new file mode 100644 index 000000000..dc86cd594 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ipv6net.go @@ -0,0 +1,18 @@ +package sacloud + +// IPv6Net IPv6ネットワーク(サブネット) +type IPv6Net struct { + *Resource // ID + propScope // スコープ + propServiceClass // サービスクラス + propCreatedAt // 作成日時 + + IPv6Prefix string `json:",omitempty"` // IPv6プレフィックス + IPv6PrefixLen int `json:",omitempty"` // IPv6プレフィックス長 + IPv6PrefixTail string `json:",omitempty"` // IPv6プレフィックス末尾 + IPv6Table *Resource `json:",omitempty"` // IPv6テーブル + NamedIPv6AddrCount int `json:",omitempty"` // 名前付きIPv6アドレス数 + ServiceID int64 `json:",omitempty"` // サービスID + Switch *Switch `json:",omitempty"` // 接続先スイッチ + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/license.go b/vendor/github.com/sacloud/libsacloud/sacloud/license.go new file mode 100644 index 000000000..472422aee --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/license.go @@ -0,0 +1,27 @@ +package sacloud + +// License ライセンス +type License struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + + LicenseInfo *ProductLicense `json:",omitempty"` // ライセンス情報 +} + +// GetLicenseInfo ライセンス情報 取得 +func (l *License) GetLicenseInfo() *ProductLicense { + return l.LicenseInfo +} + +// SetLicenseInfo ライセンス情報 設定 +func (l *License) SetLicenseInfo(license *ProductLicense) { + l.LicenseInfo = license +} + +// SetLicenseInfoByID ライセンス情報 設定 +func (l *License) SetLicenseInfoByID(id int64) { + l.LicenseInfo = &ProductLicense{Resource: NewResource(id)} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go b/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go new file mode 100644 index 000000000..5401fe85f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/loadbalancer.go @@ -0,0 +1,181 @@ +package sacloud + +// LoadBalancer ロードバランサー +type LoadBalancer struct { + *Appliance // アプライアンス共通属性 + + Remark *LoadBalancerRemark `json:",omitempty"` // リマーク + Settings *LoadBalancerSettings `json:",omitempty"` // ロードバランサー設定 +} + +// LoadBalancerRemark リマーク +type LoadBalancerRemark struct { + *ApplianceRemarkBase + // TODO Zone + //Zone *Resource +} + +// LoadBalancerSettings ロードバランサー設定リスト +type LoadBalancerSettings struct { + LoadBalancer []*LoadBalancerSetting `json:",omitempty"` // ロードバランサー設定リスト +} + +// LoadBalancerSetting ロードバランサー仮想IP設定 +type LoadBalancerSetting struct { + VirtualIPAddress string `json:",omitempty"` // 仮想IPアドレス + Port string `json:",omitempty"` // ポート番号 + DelayLoop string `json:",omitempty"` // 監視間隔 + SorryServer string `json:",omitempty"` // ソーリーサーバー + Servers []*LoadBalancerServer `json:",omitempty"` // 仮想IP配下の実サーバー +} + +// LoadBalancerServer 仮想IP設定配下のサーバー +type LoadBalancerServer struct { + IPAddress string `json:",omitempty"` // IPアドレス + Port string `json:",omitempty"` // ポート番号 + HealthCheck *LoadBalancerHealthCheck `json:",omitempty"` // ヘルスチェック + Enabled string `json:",omitempty"` // 有効/無効 + Status string `json:",omitempty"` // ステータス + ActiveConn string `json:",omitempty"` // アクティブなコネクション +} + +// LoadBalancerHealthCheck ヘルスチェック +type LoadBalancerHealthCheck struct { + Protocol string `json:",omitempty"` // プロトコル + Path string `json:",omitempty"` // HTTP/HTTPSの場合のリクエストパス + Status string `json:",omitempty"` // HTTP/HTTPSの場合の期待するレスポンスコード +} + +// LoadBalancerPlan ロードバランサープラン +type LoadBalancerPlan int + +var ( + // LoadBalancerPlanStandard スタンダードプラン + LoadBalancerPlanStandard = LoadBalancerPlan(1) + // LoadBalancerPlanPremium プレミアムプラン + LoadBalancerPlanPremium = LoadBalancerPlan(2) +) + +// CreateLoadBalancerValue ロードバランサー作成用パラメーター +type CreateLoadBalancerValue struct { + SwitchID string // 接続先スイッチID + VRID int // VRID + Plan LoadBalancerPlan // プラン + IPAddress1 string // IPアドレス + MaskLen int // ネットワークマスク長 + DefaultRoute string // デフォルトルート + Name string // 名称 + Description string // 説明 + Tags []string // タグ + Icon *Resource // アイコン +} + +// CreateDoubleLoadBalancerValue ロードバランサー(冗長化あり)作成用パラメーター +type CreateDoubleLoadBalancerValue struct { + *CreateLoadBalancerValue + IPAddress2 string // IPアドレス2 +} + +// AllowLoadBalancerHealthCheckProtocol ロードバランサーでのヘルスチェック対応プロトコルリスト +func AllowLoadBalancerHealthCheckProtocol() []string { + return []string{"http", "https", "ping", "tcp"} +} + +// CreateNewLoadBalancerSingle ロードバランサー作成(冗長化なし) +func CreateNewLoadBalancerSingle(values *CreateLoadBalancerValue, settings []*LoadBalancerSetting) (*LoadBalancer, error) { + + lb := &LoadBalancer{ + Appliance: &Appliance{ + Class: "loadbalancer", + propName: propName{Name: values.Name}, + propDescription: propDescription{Description: values.Description}, + propTags: propTags{Tags: values.Tags}, + propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + propIcon: propIcon{ + &Icon{ + Resource: values.Icon, + }, + }, + }, + Remark: &LoadBalancerRemark{ + ApplianceRemarkBase: &ApplianceRemarkBase{ + Switch: &ApplianceRemarkSwitch{ + ID: values.SwitchID, + }, + VRRP: &ApplianceRemarkVRRP{ + VRID: values.VRID, + }, + Network: &ApplianceRemarkNetwork{ + NetworkMaskLen: values.MaskLen, + DefaultRoute: values.DefaultRoute, + }, + Servers: []interface{}{ + map[string]string{"IPAddress": values.IPAddress1}, + }, + }, + }, + } + + for _, s := range settings { + lb.AddLoadBalancerSetting(s) + } + + return lb, nil +} + +// CreateNewLoadBalancerDouble ロードバランサー(冗長化あり)作成 +func CreateNewLoadBalancerDouble(values *CreateDoubleLoadBalancerValue, settings []*LoadBalancerSetting) (*LoadBalancer, error) { + lb, err := CreateNewLoadBalancerSingle(values.CreateLoadBalancerValue, settings) + if err != nil { + return nil, err + } + lb.Remark.Servers = append(lb.Remark.Servers, map[string]string{"IPAddress": values.IPAddress2}) + return lb, nil +} + +// AddLoadBalancerSetting ロードバランサー仮想IP設定追加 +// +// ロードバランサー設定は仮想IPアドレス単位で保持しています。 +// 仮想IPを増やす場合にこのメソッドを利用します。 +func (l *LoadBalancer) AddLoadBalancerSetting(setting *LoadBalancerSetting) { + if l.Settings == nil { + l.Settings = &LoadBalancerSettings{} + } + if l.Settings.LoadBalancer == nil { + l.Settings.LoadBalancer = []*LoadBalancerSetting{} + } + l.Settings.LoadBalancer = append(l.Settings.LoadBalancer, setting) +} + +// DeleteLoadBalancerSetting ロードバランサー仮想IP設定の削除 +func (l *LoadBalancer) DeleteLoadBalancerSetting(vip string, port string) { + res := []*LoadBalancerSetting{} + for _, l := range l.Settings.LoadBalancer { + if l.VirtualIPAddress != vip || l.Port != port { + res = append(res, l) + } + } + + l.Settings.LoadBalancer = res +} + +// AddServer 仮想IP設定配下へ実サーバーを追加 +func (s *LoadBalancerSetting) AddServer(server *LoadBalancerServer) { + if s.Servers == nil { + s.Servers = []*LoadBalancerServer{} + } + s.Servers = append(s.Servers, server) +} + +// DeleteServer 仮想IP設定配下の実サーバーを削除 +func (s *LoadBalancerSetting) DeleteServer(ip string, port string) { + res := []*LoadBalancerServer{} + for _, server := range s.Servers { + if server.IPAddress != ip || server.Port != port { + res = append(res, server) + } + } + + s.Servers = res + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/member.go b/vendor/github.com/sacloud/libsacloud/sacloud/member.go new file mode 100644 index 000000000..af6c540db --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/member.go @@ -0,0 +1,9 @@ +package sacloud + +// Member 会員情報 +type Member struct { + Class string `json:",omitempty"` // クラス + Code string `json:",omitempty"` // 会員コード + + // Errors [unknown type] `json:",omitempty"` +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go b/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go new file mode 100644 index 000000000..6e98d635e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/mobile_gateway.go @@ -0,0 +1,209 @@ +package sacloud + +import ( + "encoding/json" + "fmt" + "strings" +) + +// MobileGateway モバイルゲートウェイ +type MobileGateway struct { + *Appliance // アプライアンス共通属性 + + Remark *MobileGatewayRemark `json:",omitempty"` // リマーク + Settings *MobileGatewaySettings `json:",omitempty"` // モバイルゲートウェイ設定 +} + +// MobileGatewayRemark リマーク +type MobileGatewayRemark struct { + *ApplianceRemarkBase + // TODO Zone + //Zone *Resource +} + +// MobileGatewaySettings モバイルゲートウェイ設定 +type MobileGatewaySettings struct { + MobileGateway *MobileGatewaySetting `json:",omitempty"` // モバイルゲートウェイ設定リスト +} + +// MobileGatewaySetting モバイルゲートウェイ設定 +type MobileGatewaySetting struct { + InternetConnection *MGWInternetConnection `json:",omitempty"` // インターネット接続 + Interfaces []*MGWInterface `json:",omitempty"` // インターフェース + StaticRoutes []*MGWStaticRoute `json:",omitempty"` // スタティックルート +} + +// MGWInternetConnection インターネット接続 +type MGWInternetConnection struct { + Enabled string `json:",omitempty"` +} + +// MGWInterface インターフェース +type MGWInterface struct { + IPAddress []string `json:",omitempty"` + NetworkMaskLen int `json:",omitempty"` +} + +// MGWStaticRoute スタティックルート +type MGWStaticRoute struct { + Prefix string `json:",omitempty"` + NextHop string `json:",omitempty"` +} + +// MobileGatewayPlan モバイルゲートウェイプラン +type MobileGatewayPlan int + +var ( + // MobileGatewayPlanStandard スタンダードプラン // TODO 正式名称不明なため暫定の名前 + MobileGatewayPlanStandard = MobileGatewayPlan(1) +) + +// CreateMobileGatewayValue モバイルゲートウェイ作成用パラメーター +type CreateMobileGatewayValue struct { + Name string // 名称 + Description string // 説明 + Tags []string // タグ + IconID int64 // アイコン +} + +// CreateNewMobileGateway モバイルゲートウェイ作成 +func CreateNewMobileGateway(values *CreateMobileGatewayValue, setting *MobileGatewaySetting) (*MobileGateway, error) { + + lb := &MobileGateway{ + Appliance: &Appliance{ + Class: "mobilegateway", + propName: propName{Name: values.Name}, + propDescription: propDescription{Description: values.Description}, + propTags: propTags{Tags: values.Tags}, + propPlanID: propPlanID{Plan: &Resource{ID: int64(MobileGatewayPlanStandard)}}, + propIcon: propIcon{ + &Icon{ + Resource: NewResource(values.IconID), + }, + }, + }, + Remark: &MobileGatewayRemark{ + ApplianceRemarkBase: &ApplianceRemarkBase{ + Switch: &ApplianceRemarkSwitch{ + propScope: propScope{ + Scope: "shared", + }, + }, + Servers: []interface{}{ + nil, + }, + }, + }, + Settings: &MobileGatewaySettings{ + MobileGateway: setting, + }, + } + + return lb, nil +} + +// SetPrivateInterface プライベート側NICの接続 +func (m *MobileGateway) SetPrivateInterface(ip string, nwMaskLen int) { + if len(m.Settings.MobileGateway.Interfaces) > 1 { + m.Settings.MobileGateway.Interfaces[1].IPAddress = []string{ip} + m.Settings.MobileGateway.Interfaces[1].NetworkMaskLen = nwMaskLen + } else { + nic := &MGWInterface{ + IPAddress: []string{ip}, + NetworkMaskLen: nwMaskLen, + } + m.Settings.MobileGateway.Interfaces = append(m.Settings.MobileGateway.Interfaces, nic) + } +} + +// ClearPrivateInterface プライベート側NICの切断 +func (m *MobileGateway) ClearPrivateInterface() { + m.Settings.MobileGateway.Interfaces = []*MGWInterface{nil} +} + +// NewMobileGatewayResolver DNS登録用パラメータ作成 +func NewMobileGatewayResolver(dns1, dns2 string) *MobileGatewayResolver { + return &MobileGatewayResolver{ + SimGroup: &MobileGatewaySIMGroup{ + DNS1: dns1, + DNS2: dns2, + }, + } +} + +// MobileGatewayResolver DNS登録用パラメータ +type MobileGatewayResolver struct { + SimGroup *MobileGatewaySIMGroup `json:"sim_group,omitempty"` +} + +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (m *MobileGatewaySIMGroup) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `[]` { + return nil + } + + tmp := &struct { + DNS1 string `json:"dns_1,omitempty"` + DNS2 string `json:"dns_2,omitempty"` + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + m.DNS1 = tmp.DNS1 + m.DNS2 = tmp.DNS2 + return nil +} + +// MobileGatewaySIMGroup DNS登録用SIMグループ値 +type MobileGatewaySIMGroup struct { + DNS1 string `json:"dns_1,omitempty"` + DNS2 string `json:"dns_2,omitempty"` +} + +// MobileGatewaySIMRoute SIルート +type MobileGatewaySIMRoute struct { + ICCID string `json:"iccid,omitempty"` + Prefix string `json:"prefix,omitempty"` + ResourceID string `json:"resource_id,omitempty"` +} + +// MobileGatewaySIMRoutes SIMルート一覧 +type MobileGatewaySIMRoutes struct { + SIMRoutes []*MobileGatewaySIMRoute `json:"sim_routes"` +} + +// AddSIMRoute SIMルート追加 +func (m *MobileGatewaySIMRoutes) AddSIMRoute(simID int64, prefix string) bool { + var exists bool + for _, route := range m.SIMRoutes { + if route.ResourceID == fmt.Sprintf("%d", simID) && route.Prefix == prefix { + exists = true + break + } + } + if !exists { + m.SIMRoutes = append(m.SIMRoutes, &MobileGatewaySIMRoute{ + ResourceID: fmt.Sprintf("%d", simID), + Prefix: prefix, + }) + } + return !exists +} + +// DeleteSIMRoute SIMルート削除 +func (m *MobileGatewaySIMRoutes) DeleteSIMRoute(simID int64, prefix string) bool { + routes := []*MobileGatewaySIMRoute{} // nolint (JSONヘのMarshal時に要素が0の場合にNULLではなく[]とするため) + var exists bool + + for _, route := range m.SIMRoutes { + if route.ResourceID == fmt.Sprintf("%d", simID) && route.Prefix == prefix { + exists = true + } else { + routes = append(routes, route) + } + } + m.SIMRoutes = routes + return exists +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go b/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go new file mode 100644 index 000000000..f4ecc061c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/monitor.go @@ -0,0 +1,305 @@ +package sacloud + +import ( + "encoding/json" + "math" + "strings" + "time" +) + +// MonitorValue アクティビティモニター +type MonitorValue struct { + CPUTime *float64 `json:"CPU-TIME,omitempty"` // CPU時間 + Write *float64 `json:",omitempty"` // ディスク書き込み + Read *float64 `json:",omitempty"` // ディスク読み取り + Receive *float64 `json:",omitempty"` // パケット受信 + Send *float64 `json:",omitempty"` // パケット送信 + In *float64 `json:",omitempty"` // パケット受信 + Out *float64 `json:",omitempty"` // パケット送信 + TotalMemorySize *float64 `json:"Total-Memory-Size,omitempty"` // 総メモリサイズ + UsedMemorySize *float64 `json:"Used-Memory-Size,omitempty"` // 使用済みメモリサイズ + TotalDisk1Size *float64 `json:"Total-Disk1-Size,omitempty"` // 総ディスクサイズ + UsedDisk1Size *float64 `json:"Used-Disk1-Size,omitempty"` // 使用済みディスクサイズ + TotalDisk2Size *float64 `json:"Total-Disk2-Size,omitempty"` // 総ディスクサイズ + UsedDisk2Size *float64 `json:"Used-Disk2-Size,omitempty"` // 使用済みディスクサイズ + FreeDiskSize *float64 `json:"Free-Disk-Size,omitempty"` // 空きディスクサイズ(NFS) + ResponseTimeSec *float64 `json:"responsetimesec,omitempty"` // レスポンスタイム(シンプル監視) + UplinkBPS *float64 `json:"UplinkBps,omitempty"` // 上り方向トラフィック + DownlinkBPS *float64 `json:"DownlinkBps"` // 下り方向トラフィック +} + +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (m *MonitorValue) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `[]` { + return nil + } + + tmp := &struct { + CPUTime *float64 `json:"CPU-TIME,omitempty"` + Write *float64 `json:",omitempty"` + Read *float64 `json:",omitempty"` + Receive *float64 `json:",omitempty"` + Send *float64 `json:",omitempty"` + In *float64 `json:",omitempty"` + Out *float64 `json:",omitempty"` + TotalMemorySize *float64 `json:"Total-Memory-Size,omitempty"` + UsedMemorySize *float64 `json:"Used-Memory-Size,omitempty"` + TotalDisk1Size *float64 `json:"Total-Disk1-Size,omitempty"` + UsedDisk1Size *float64 `json:"Used-Disk1-Size,omitempty"` + TotalDisk2Size *float64 `json:"Total-Disk2-Size,omitempty"` + UsedDisk2Size *float64 `json:"Used-Disk2-Size,omitempty"` + FreeDiskSize *float64 `json:"Free-Disk-Size,omitempty"` + ResponseTimeSec *float64 `json:"responsetimesec,omitempty"` + UplinkBPS *float64 `json:"UplinkBps,omitempty"` + DownlinkBPS *float64 `json:"DownlinkBps"` + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + m.CPUTime = tmp.CPUTime + m.Write = tmp.Write + m.Read = tmp.Read + m.Receive = tmp.Receive + m.Send = tmp.Send + m.In = tmp.In + m.Out = tmp.Out + m.TotalMemorySize = tmp.TotalMemorySize + m.UsedMemorySize = tmp.UsedMemorySize + m.TotalDisk1Size = tmp.TotalDisk1Size + m.UsedDisk1Size = tmp.UsedDisk1Size + m.TotalDisk2Size = tmp.TotalDisk2Size + m.UsedDisk2Size = tmp.UsedDisk2Size + m.FreeDiskSize = tmp.FreeDiskSize + m.ResponseTimeSec = tmp.ResponseTimeSec + m.UplinkBPS = tmp.UplinkBPS + m.DownlinkBPS = tmp.DownlinkBPS + + return nil +} + +// ResourceMonitorRequest アクティビティモニター取得リクエスト +type ResourceMonitorRequest struct { + Start *time.Time `json:",omitempty"` // 取得開始時間 + End *time.Time `json:",omitempty"` // 取得終了時間 +} + +// NewResourceMonitorRequest アクティビティモニター取得リクエスト作成 +func NewResourceMonitorRequest(start *time.Time, end *time.Time) *ResourceMonitorRequest { + res := &ResourceMonitorRequest{} + if start != nil { + t := start.Truncate(time.Second) + res.Start = &t + } + if end != nil { + t := end.Truncate(time.Second) + res.End = &t + } + return res +} + +// ResourceMonitorResponse アクティビティモニターレスポンス +type ResourceMonitorResponse struct { + Data *MonitorValues `json:",omitempty"` // メトリクス +} + +// MonitorSummaryData メトリクスサマリー +type MonitorSummaryData struct { + Max float64 // 最大値 + Min float64 // 最小値 + Avg float64 // 平均値 + Count float64 // データ個数 + +} + +// MonitorSummary アクティビティーモニター サマリー +type MonitorSummary struct { + CPU *MonitorSummaryData // CPU時間サマリー + Disk *struct { // ディスク利用サマリー + Write *MonitorSummaryData // ディスク書き込みサマリー + Read *MonitorSummaryData // ディスク読み取りサマリー + } + Interface *struct { // NIC送受信サマリー + Receive *MonitorSummaryData // 受信パケットサマリー + Send *MonitorSummaryData // 送信パケットサマリー + } +} + +// MonitorValues メトリクス リスト +type MonitorValues map[string]*MonitorValue + +// FlatMonitorValue フラット化したメトリクス +type FlatMonitorValue struct { + Time time.Time // 対象時刻 + Value float64 // 値 +} + +// Calc サマリー計算 +func (m *MonitorValues) Calc() *MonitorSummary { + + res := &MonitorSummary{} + res.CPU = m.calcBy(func(v *MonitorValue) *float64 { return v.CPUTime }) + res.Disk = &struct { + Write *MonitorSummaryData + Read *MonitorSummaryData + }{ + Write: m.calcBy(func(v *MonitorValue) *float64 { return v.Write }), + Read: m.calcBy(func(v *MonitorValue) *float64 { return v.Read }), + } + res.Interface = &struct { + Receive *MonitorSummaryData + Send *MonitorSummaryData + }{ + Receive: m.calcBy(func(v *MonitorValue) *float64 { return v.Receive }), + Send: m.calcBy(func(v *MonitorValue) *float64 { return v.Send }), + } + + return res +} + +func (m *MonitorValues) calcBy(f func(m *MonitorValue) *float64) *MonitorSummaryData { + res := &MonitorSummaryData{} + var sum float64 + for _, data := range map[string]*MonitorValue(*m) { + value := f(data) + if value != nil { + res.Count++ + res.Min = math.Min(res.Min, *value) + res.Max = math.Max(res.Max, *value) + sum += *value + } + } + if sum > 0 && res.Count > 0 { + res.Avg = sum / res.Count + } + + return res +} + +// FlattenCPUTimeValue フラット化 CPU時間 +func (m *MonitorValues) FlattenCPUTimeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.CPUTime }) +} + +// FlattenDiskWriteValue フラット化 ディスク書き込み +func (m *MonitorValues) FlattenDiskWriteValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.Write }) +} + +// FlattenDiskReadValue フラット化 ディスク読み取り +func (m *MonitorValues) FlattenDiskReadValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.Read }) +} + +// FlattenPacketSendValue フラット化 パケット送信 +func (m *MonitorValues) FlattenPacketSendValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.Send }) +} + +// FlattenPacketReceiveValue フラット化 パケット受信 +func (m *MonitorValues) FlattenPacketReceiveValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.Receive }) +} + +// FlattenInternetInValue フラット化 パケット受信 +func (m *MonitorValues) FlattenInternetInValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.In }) +} + +// FlattenInternetOutValue フラット化 パケット送信 +func (m *MonitorValues) FlattenInternetOutValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.Out }) +} + +// FlattenTotalMemorySizeValue フラット化 総メモリサイズ +func (m *MonitorValues) FlattenTotalMemorySizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.TotalMemorySize }) +} + +// FlattenUsedMemorySizeValue フラット化 使用済みメモリサイズ +func (m *MonitorValues) FlattenUsedMemorySizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedMemorySize }) +} + +// FlattenTotalDisk1SizeValue フラット化 総ディスクサイズ +func (m *MonitorValues) FlattenTotalDisk1SizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.TotalDisk1Size }) +} + +// FlattenUsedDisk1SizeValue フラット化 使用済みディスクサイズ +func (m *MonitorValues) FlattenUsedDisk1SizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedDisk1Size }) +} + +// FlattenTotalDisk2SizeValue フラット化 総ディスクサイズ +func (m *MonitorValues) FlattenTotalDisk2SizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.TotalDisk2Size }) +} + +// FlattenUsedDisk2SizeValue フラット化 使用済みディスクサイズ +func (m *MonitorValues) FlattenUsedDisk2SizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.UsedDisk2Size }) +} + +// FlattenFreeDiskSizeValue フラット化 空きディスクサイズ(NFS) +func (m *MonitorValues) FlattenFreeDiskSizeValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.FreeDiskSize }) +} + +// FlattenResponseTimeSecValue フラット化 レスポンスタイム(シンプル監視) +func (m *MonitorValues) FlattenResponseTimeSecValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.ResponseTimeSec }) +} + +// FlattenUplinkBPSValue フラット化 上り方向トラフィック(セキュアモバイルSIM) +func (m *MonitorValues) FlattenUplinkBPSValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.UplinkBPS }) +} + +// FlattenDownlinkBPSValue フラット化 下り方向トライフィック(セキュアモバイルSIM) +func (m *MonitorValues) FlattenDownlinkBPSValue() ([]FlatMonitorValue, error) { + return m.flattenValue(func(v *MonitorValue) *float64 { return v.DownlinkBPS }) +} + +func (m *MonitorValues) flattenValue(f func(*MonitorValue) *float64) ([]FlatMonitorValue, error) { + var res []FlatMonitorValue + + for k, v := range map[string]*MonitorValue(*m) { + if f(v) == nil { + continue + } + time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601 + if err != nil { + return res, err + } + res = append(res, FlatMonitorValue{ + // Time + Time: time, + // Value + Value: *f(v), + }) + } + return res, nil +} + +// HasValue 取得したアクティビティーモニターに有効値が含まれるか判定 +func (m *MonitorValue) HasValue() bool { + values := []*float64{ + m.CPUTime, + m.Read, m.Receive, + m.Send, m.Write, + m.In, m.Out, + m.TotalMemorySize, m.UsedMemorySize, + m.TotalDisk1Size, m.UsedDisk1Size, + m.TotalDisk2Size, m.UsedDisk2Size, + m.FreeDiskSize, m.ResponseTimeSec, + m.UplinkBPS, m.DownlinkBPS, + } + for _, v := range values { + if v != nil { + return true + } + } + return false +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go b/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go new file mode 100644 index 000000000..7d91a48ec --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/nfs.go @@ -0,0 +1,110 @@ +package sacloud + +// NFS NFS +type NFS struct { + *Appliance // アプライアンス共通属性 + + Remark *NFSRemark `json:",omitempty"` // リマーク + Settings *NFSSettings `json:",omitempty"` // NFS設定 +} + +// NFSRemark リマーク +type NFSRemark struct { + *ApplianceRemarkBase + propPlanID + // TODO Zone + //Zone *Resource + //SourceAppliance *Resource // クローン元DB +} + +// NFSSettings NFS設定リスト +type NFSSettings struct { +} + +// NFSPlan NFSプラン +type NFSPlan int + +var ( + // NFSPlan100G 100Gプラン + NFSPlan100G = NFSPlan(100) + // NFSPlan500G 500Gプラン + NFSPlan500G = NFSPlan(500) + // NFSPlan1T 1T(1024GB)プラン + NFSPlan1T = NFSPlan(1024 * 1) + // NFSPlan2T 2T(2048GB)プラン + NFSPlan2T = NFSPlan(1024 * 2) + // NFSPlan4T 4T(4096GB)プラン + NFSPlan4T = NFSPlan(1024 * 4) +) + +// AllowNFSPlans 指定可能なNFSプラン +func AllowNFSPlans() []int { + return []int{ + int(NFSPlan100G), + int(NFSPlan500G), + int(NFSPlan1T), + int(NFSPlan2T), + int(NFSPlan4T), + } +} + +// CreateNFSValue NFS作成用パラメーター +type CreateNFSValue struct { + SwitchID string // 接続先スイッチID + Plan NFSPlan // プラン + IPAddress string // IPアドレス + MaskLen int // ネットワークマスク長 + DefaultRoute string // デフォルトルート + Name string // 名称 + Description string // 説明 + Tags []string // タグ + Icon *Resource // アイコン + SourceAppliance *Resource // クローン元NFS +} + +// NewCreateNFSValue NFS作成用パラメーター +func NewCreateNFSValue() *CreateNFSValue { + return &CreateNFSValue{ + Plan: NFSPlan100G, + } +} + +// NewNFS NFS作成(冗長化なし) +func NewNFS(values *CreateNFSValue) *NFS { + + if int(values.Plan) == 0 { + values.Plan = NFSPlan100G + } + + return &NFS{ + Appliance: &Appliance{ + Class: "nfs", + propName: propName{Name: values.Name}, + propDescription: propDescription{Description: values.Description}, + propTags: propTags{Tags: values.Tags}, + propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + propIcon: propIcon{ + &Icon{ + Resource: values.Icon, + }, + }, + }, + Remark: &NFSRemark{ + ApplianceRemarkBase: &ApplianceRemarkBase{ + Switch: &ApplianceRemarkSwitch{ + ID: values.SwitchID, + }, + Network: &ApplianceRemarkNetwork{ + NetworkMaskLen: values.MaskLen, + DefaultRoute: values.DefaultRoute, + }, + Servers: []interface{}{ + map[string]string{"IPAddress": values.IPAddress}, + }, + }, + propPlanID: propPlanID{Plan: &Resource{ID: int64(values.Plan)}}, + //SourceAppliance: values.SourceAppliance, + }, + } + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/note.go b/vendor/github.com/sacloud/libsacloud/sacloud/note.go new file mode 100644 index 000000000..d50addba7 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/note.go @@ -0,0 +1,16 @@ +package sacloud + +// Note スタートアップスクリプト +type Note struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propAvailability // 有功状態 + propScope // スコープ + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + propNoteClass // クラス + Content string // スクリプト本体 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go new file mode 100644 index 000000000..a53fd1b2c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archive_ostype.go @@ -0,0 +1,127 @@ +// Package ostype is define OS type of SakuraCloud public archive +package ostype + +//go:generate stringer -type=ArchiveOSTypes + +// ArchiveOSTypes パブリックアーカイブOS種別 +type ArchiveOSTypes int + +const ( + // CentOS OS種別:CentOS + CentOS ArchiveOSTypes = iota + // CentOS6 OS種別:CentOS6 + CentOS6 + // Ubuntu OS種別:Ubuntu + Ubuntu + // Debian OS種別:Debian + Debian + // VyOS OS種別:VyOS + VyOS + // CoreOS OS種別:CoreOS + CoreOS + // RancherOS OS種別:RancherOS + RancherOS + // Kusanagi OS種別:Kusanagi(CentOS) + Kusanagi + // SophosUTM OS種別:Sophos UTM + SophosUTM + // FreeBSD OS種別:FreeBSD + FreeBSD + // Windows2012 OS種別:Windows Server 2012 R2 Datacenter Edition + Windows2012 + // Windows2012RDS OS種別:Windows Server 2012 R2 for RDS + Windows2012RDS + // Windows2012RDSOffice OS種別:Windows Server 2012 R2 for RDS(Office) + Windows2012RDSOffice + // Windows2016 OS種別:Windows Server 2016 Datacenter Edition + Windows2016 + // Windows2016RDS OS種別:Windows Server 2016 RDS + Windows2016RDS + // Windows2016RDSOffice OS種別:Windows Server 2016 RDS(Office) + Windows2016RDSOffice + // Windows2016SQLServerWeb OS種別:Windows Server 2016 SQLServer(Web) + Windows2016SQLServerWeb + // Windows2016SQLServerStandard OS種別:Windows Server 2016 SQLServer(Standard) + Windows2016SQLServerStandard + // Windows2016SQLServerStandardAll OS種別:Windows Server 2016 SQLServer(Standard) + RDS + Office + Windows2016SQLServerStandardAll + // Custom OS種別:カスタム + Custom +) + +// OSTypeShortNames OSTypeとして利用できる文字列のリスト +var OSTypeShortNames = []string{ + "centos", "centos6", "ubuntu", "debian", "vyos", "coreos", + "rancheros", "kusanagi", "sophos-utm", "freebsd", + "windows2012", "windows2012-rds", "windows2012-rds-office", + "windows2016", "windows2016-rds", "windows2016-rds-office", + "windows2016-sql-web", "windows2016-sql-standard", "windows2016-sql-standard-all", +} + +// IsWindows Windowsか +func (o ArchiveOSTypes) IsWindows() bool { + switch o { + case Windows2012, Windows2012RDS, Windows2012RDSOffice, + Windows2016, Windows2016RDS, Windows2016RDSOffice, + Windows2016SQLServerWeb, Windows2016SQLServerStandard, Windows2016SQLServerStandardAll: + return true + default: + return false + } +} + +// IsSupportDiskEdit ディスクの修正機能をフルサポートしているか(Windowsは一部サポートのためfalseを返す) +func (o ArchiveOSTypes) IsSupportDiskEdit() bool { + switch o { + case CentOS, CentOS6, Ubuntu, Debian, VyOS, CoreOS, RancherOS, Kusanagi, FreeBSD: + return true + default: + return false + } +} + +// StrToOSType 文字列からArchiveOSTypesへの変換 +func StrToOSType(osType string) ArchiveOSTypes { + switch osType { + case "centos": + return CentOS + case "centos6": + return CentOS6 + case "ubuntu": + return Ubuntu + case "debian": + return Debian + case "vyos": + return VyOS + case "coreos": + return CoreOS + case "rancheros": + return RancherOS + case "kusanagi": + return Kusanagi + case "sophos-utm": + return SophosUTM + case "freebsd": + return FreeBSD + case "windows2012": + return Windows2012 + case "windows2012-rds": + return Windows2012RDS + case "windows2012-rds-office": + return Windows2012RDSOffice + case "windows2016": + return Windows2016 + case "windows2016-rds": + return Windows2016RDS + case "windows2016-rds-office": + return Windows2016RDSOffice + case "windows2016-sql-web": + return Windows2016SQLServerWeb + case "windows2016-sql-standard": + return Windows2016SQLServerStandard + case "windows2016-sql-standard-all": + return Windows2016SQLServerStandardAll + default: + return Custom + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go new file mode 100644 index 000000000..74656b860 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ostype/archiveostypes_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=ArchiveOSTypes"; DO NOT EDIT. + +package ostype + +import "strconv" + +const _ArchiveOSTypes_name = "CentOSCentOS6UbuntuDebianVyOSCoreOSRancherOSKusanagiSophosUTMFreeBSDWindows2012Windows2012RDSWindows2012RDSOfficeWindows2016Windows2016RDSWindows2016RDSOfficeWindows2016SQLServerWebWindows2016SQLServerStandardWindows2016SQLServerStandardAllCustom" + +var _ArchiveOSTypes_index = [...]uint8{0, 6, 13, 19, 25, 29, 35, 44, 52, 61, 68, 79, 93, 113, 124, 138, 158, 181, 209, 240, 246} + +func (i ArchiveOSTypes) String() string { + if i < 0 || i >= ArchiveOSTypes(len(_ArchiveOSTypes_index)-1) { + return "ArchiveOSTypes(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ArchiveOSTypes_name[_ArchiveOSTypes_index[i]:_ArchiveOSTypes_index[i+1]] +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/packet_filter.go b/vendor/github.com/sacloud/libsacloud/sacloud/packet_filter.go new file mode 100644 index 000000000..7a32f558e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/packet_filter.go @@ -0,0 +1,248 @@ +package sacloud + +import ( + "crypto/md5" + "encoding/hex" + "fmt" +) + +// PacketFilter パケットフィルタ +type PacketFilter struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + + Expression []*PacketFilterExpression // ルール + Notice string `json:",omitempty"` // Notice + + //HACK API呼び出しルートにより数字/文字列が混在する + // PackerFilterのCREATE時は文字列、以外は数値となる。現状利用しないためコメントとしておく + // RequiredHostVersion int `json:",omitempty"` + +} + +// AllowPacketFilterProtocol パケットフィルタが対応するプロトコルリスト +func AllowPacketFilterProtocol() []string { + return []string{"tcp", "udp", "icmp", "fragment", "ip"} +} + +// PacketFilterExpression フィルタリングルール +type PacketFilterExpression struct { + Protocol string `json:",omitempty"` // Protocol プロトコル + Action string `json:",omitempty"` // Action 許可/拒否 + + SourceNetwork string // SourceNetwork 送信元ネットワーク + SourcePort string // SourcePort 送信元ポート + DestinationPort string // DestinationPort 宛先ポート + + propDescription // 説明 +} + +// Hash 値からハッシュ値を生成 +func (e *PacketFilterExpression) Hash() string { + str := fmt.Sprintf("%v", e) + h := md5.New() + h.Write([]byte(str)) + return hex.EncodeToString(h.Sum(nil)) +} + +// CreateNewPacketFilter パケットフィルタ作成 +func CreateNewPacketFilter() *PacketFilter { + return &PacketFilter{ + // Expression + Expression: []*PacketFilterExpression{}, + } +} + +// ClearRules ルールのクリア +func (p *PacketFilter) ClearRules() { + p.Expression = []*PacketFilterExpression{} +} + +// FindByHash 指定のハッシュ値を持つPacketFilterExpressionを検索する +func (p *PacketFilter) FindByHash(hash string) *PacketFilterExpression { + + for _, e := range p.Expression { + h := e.Hash() + if h == hash { + return e + } + } + return nil +} + +// AddTCPRule TCPルール追加 +func (p *PacketFilter) AddTCPRule(sourceNetwork string, sourcePort string, destPort string, description string, isAllow bool) (*PacketFilterExpression, error) { + exp := p.createTCPRule(sourceNetwork, sourcePort, destPort, description, isAllow) + p.Expression = append(p.Expression, exp) + return exp, nil +} + +// AddTCPRuleAt TCPルール追加 +func (p *PacketFilter) AddTCPRuleAt(sourceNetwork string, sourcePort string, destPort string, description string, isAllow bool, index int) (*PacketFilterExpression, error) { + + exp := p.createTCPRule(sourceNetwork, sourcePort, destPort, description, isAllow) + p.addRuleAt(exp, index) + return exp, nil +} + +func (p *PacketFilter) createTCPRule(sourceNetwork string, sourcePort string, destPort string, description string, isAllow bool) *PacketFilterExpression { + + return &PacketFilterExpression{ + Protocol: "tcp", + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationPort: destPort, + Action: p.getActionString(isAllow), + propDescription: propDescription{Description: description}, + } +} + +// AddUDPRule UDPルール追加 +func (p *PacketFilter) AddUDPRule(sourceNetwork string, sourcePort string, destPort string, description string, isAllow bool) (*PacketFilterExpression, error) { + exp := p.createUDPRule(sourceNetwork, sourcePort, destPort, description, isAllow) + p.Expression = append(p.Expression, exp) + return exp, nil +} + +// AddUDPRuleAt UDPルール追加 +func (p *PacketFilter) AddUDPRuleAt(sourceNetwork string, sourcePort string, destPort string, description string, isAllow bool, index int) (*PacketFilterExpression, error) { + exp := p.createUDPRule(sourceNetwork, sourcePort, destPort, description, isAllow) + p.addRuleAt(exp, index) + return exp, nil +} + +func (p *PacketFilter) createUDPRule(sourceNetwork string, sourcePort string, destPort string, description string, isAllow bool) *PacketFilterExpression { + + return &PacketFilterExpression{ + Protocol: "udp", + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationPort: destPort, + Action: p.getActionString(isAllow), + propDescription: propDescription{Description: description}, + } +} + +// AddICMPRule ICMPルール追加 +func (p *PacketFilter) AddICMPRule(sourceNetwork string, description string, isAllow bool) (*PacketFilterExpression, error) { + + exp := p.createICMPRule(sourceNetwork, description, isAllow) + p.Expression = append(p.Expression, exp) + return exp, nil +} + +// AddICMPRuleAt ICMPルール追加 +func (p *PacketFilter) AddICMPRuleAt(sourceNetwork string, description string, isAllow bool, index int) (*PacketFilterExpression, error) { + + exp := p.createICMPRule(sourceNetwork, description, isAllow) + p.addRuleAt(exp, index) + return exp, nil +} + +func (p *PacketFilter) createICMPRule(sourceNetwork string, description string, isAllow bool) *PacketFilterExpression { + + return &PacketFilterExpression{ + Protocol: "icmp", + SourceNetwork: sourceNetwork, + Action: p.getActionString(isAllow), + propDescription: propDescription{Description: description}, + } +} + +// AddFragmentRule フラグメントルール追加 +func (p *PacketFilter) AddFragmentRule(sourceNetwork string, description string, isAllow bool) (*PacketFilterExpression, error) { + + exp := p.createFragmentRule(sourceNetwork, description, isAllow) + p.Expression = append(p.Expression, exp) + return exp, nil +} + +// AddFragmentRuleAt フラグメントルール追加 +func (p *PacketFilter) AddFragmentRuleAt(sourceNetwork string, description string, isAllow bool, index int) (*PacketFilterExpression, error) { + + exp := p.createFragmentRule(sourceNetwork, description, isAllow) + p.addRuleAt(exp, index) + return exp, nil +} + +func (p *PacketFilter) createFragmentRule(sourceNetwork string, description string, isAllow bool) *PacketFilterExpression { + + return &PacketFilterExpression{ + Protocol: "fragment", + SourceNetwork: sourceNetwork, + Action: p.getActionString(isAllow), + propDescription: propDescription{Description: description}, + } +} + +// AddIPRule IPルール追加 +func (p *PacketFilter) AddIPRule(sourceNetwork string, description string, isAllow bool) (*PacketFilterExpression, error) { + + exp := p.createIPRule(sourceNetwork, description, isAllow) + p.Expression = append(p.Expression, exp) + return exp, nil +} + +// AddIPRuleAt IPルール追加 +func (p *PacketFilter) AddIPRuleAt(sourceNetwork string, description string, isAllow bool, index int) (*PacketFilterExpression, error) { + + exp := p.createIPRule(sourceNetwork, description, isAllow) + p.addRuleAt(exp, index) + return exp, nil +} + +func (p *PacketFilter) createIPRule(sourceNetwork string, description string, isAllow bool) *PacketFilterExpression { + + return &PacketFilterExpression{ + Protocol: "ip", + SourceNetwork: sourceNetwork, + Action: p.getActionString(isAllow), + propDescription: propDescription{Description: description}, + } + +} + +// RemoveRuleAt 指定インデックス(0開始)位置のルールを除去 +func (p *PacketFilter) RemoveRuleAt(index int) { + if index >= 0 && index < len(p.Expression) { + p.Expression = append(p.Expression[:index], p.Expression[index+1:]...) + } +} + +// RemoveRuleByHash 指定のハッシュ値を持つルールを削除する +func (p *PacketFilter) RemoveRuleByHash(hash string) { + dest := []*PacketFilterExpression{} + for _, e := range p.Expression { + if hash != e.Hash() { + dest = append(dest, e) + } + } + p.Expression = dest +} + +func (p *PacketFilter) addRuleAt(rule *PacketFilterExpression, index int) { + if len(p.Expression) == 0 && index == 0 { + p.Expression = []*PacketFilterExpression{rule} + return + } + + if !(index < len(p.Expression)) { + index = len(p.Expression) + } + + // Grow the slice by one element. + p.Expression = append(p.Expression, nil) + // Use copy to move the upper part of the slice out of the way and open a hole. + copy(p.Expression[index+1:], p.Expression[index:]) + // Store the new value. + p.Expression[index] = rule +} + +func (p PacketFilter) getActionString(isAllow bool) string { + action := "deny" + if isAllow { + action = "allow" + } + return action +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/private_host.go b/vendor/github.com/sacloud/libsacloud/sacloud/private_host.go new file mode 100644 index 000000000..ff2487eea --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/private_host.go @@ -0,0 +1,18 @@ +package sacloud + +// PrivateHost 専有ホスト +type PrivateHost struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + + propPrivateHostPlan // 専有ホストプラン + propHost // ホスト(物理) + propAssignedCPU // 割当済みCPUコア数 + propAssignedMemoryMB // 割当済みメモリ(MB) + + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/product_disk.go b/vendor/github.com/sacloud/libsacloud/sacloud/product_disk.go new file mode 100644 index 000000000..a52cff8e2 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/product_disk.go @@ -0,0 +1,20 @@ +package sacloud + +// ProductDisk ディスクプラン +type ProductDisk struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propStorageClass // ストレージクラス + propAvailability // 有功状態 + + Size []struct { // サイズ + propAvailability // 有功状態 + propSizeMB // サイズ(MB単位) + propServiceClass // サービスクラス + + DisplaySize int `json:",omitempty"` // 表示サイズ + DisplaySuffix string `json:",omitempty"` // 表示サフィックス + + } `json:",omitempty"` +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/product_internet.go b/vendor/github.com/sacloud/libsacloud/sacloud/product_internet.go new file mode 100644 index 000000000..cf87d8819 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/product_internet.go @@ -0,0 +1,12 @@ +package sacloud + +// ProductInternet ルータープラン +type ProductInternet struct { + *Resource // ID + propName // 名称 + propAvailability // 有功状態 + propServiceClass // サービスクラス + + BandWidthMbps int `json:",omitempty"` // 帯域幅 + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/product_license.go b/vendor/github.com/sacloud/libsacloud/sacloud/product_license.go new file mode 100644 index 000000000..1a0a52ad9 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/product_license.go @@ -0,0 +1,11 @@ +package sacloud + +// ProductLicense ライセンスプラン +type ProductLicense struct { + *Resource // ID + propName // 名称 + propServiceClass // サービスクラス + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + TermsOfUse string `json:",omitempty"` // 利用規約 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/product_privatehost.go b/vendor/github.com/sacloud/libsacloud/sacloud/product_privatehost.go new file mode 100644 index 000000000..a08ce56ac --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/product_privatehost.go @@ -0,0 +1,13 @@ +package sacloud + +// ProductPrivateHost 専有ホストプラン +type ProductPrivateHost struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propAvailability // 有功状態 + propCPU // CPUコア数 + propMemoryMB // メモリサイズ(MB単位) + propClass // クラス + propServiceClass // サービスクラス +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/product_server.go b/vendor/github.com/sacloud/libsacloud/sacloud/product_server.go new file mode 100644 index 000000000..9427ec0dd --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/product_server.go @@ -0,0 +1,12 @@ +package sacloud + +// ProductServer サーバープラン +type ProductServer struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propAvailability // 有功状態 + propCPU // CPUコア数 + propMemoryMB // メモリサイズ(MB単位) + propServiceClass // サービスクラス +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_cpu.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_cpu.go new file mode 100644 index 000000000..3f528fdaa --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_cpu.go @@ -0,0 +1,11 @@ +package sacloud + +// propAssignedCPU CPUコア数内包型 +type propAssignedCPU struct { + AssignedCPU int `json:",omitempty"` // CPUコア数 +} + +// GetAssignedCPU CPUコア数 取得 +func (p *propAssignedCPU) GetAssignedCPU() int { + return p.AssignedCPU +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_memory.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_memory.go new file mode 100644 index 000000000..796fbcf46 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_assigned_memory.go @@ -0,0 +1,19 @@ +package sacloud + +// propAssignedMemoryMB サイズ(MB)内包型 +type propAssignedMemoryMB struct { + AssignedMemoryMB int `json:",omitempty"` // サイズ(MB単位) +} + +// GetAssignedMemoryMB サイズ(MB単位) 取得 +func (p *propAssignedMemoryMB) GetAssignedMemoryMB() int { + return p.AssignedMemoryMB +} + +// GetAssignedMemoryGB サイズ(GB単位) 取得 +func (p *propAssignedMemoryMB) GetAssignedMemoryGB() int { + if p.AssignedMemoryMB <= 0 { + return 0 + } + return p.AssignedMemoryMB / 1024 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_availability.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_availability.go new file mode 100644 index 000000000..2380a76c6 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_availability.go @@ -0,0 +1,26 @@ +package sacloud + +// propAvailability 有効状態内包型 +type propAvailability struct { + Availability EAvailability `json:",omitempty"` // 有効状態 +} + +// IsAvailable 有効状態が"有効"か判定 +func (p *propAvailability) IsAvailable() bool { + return p.Availability.IsAvailable() +} + +// IsUploading 有効状態が"アップロード中"か判定 +func (p *propAvailability) IsUploading() bool { + return p.Availability.IsUploading() +} + +// IsFailed 有効状態が"失敗"か判定 +func (p *propAvailability) IsFailed() bool { + return p.Availability.IsFailed() +} + +// IsMigrating 有効状態が"マイグレーション中"か判定 +func (p *propAvailability) IsMigrating() bool { + return p.Availability.IsMigrating() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_bundle_info.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_bundle_info.go new file mode 100644 index 000000000..0ff047f73 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_bundle_info.go @@ -0,0 +1,27 @@ +package sacloud + +import "strings" + +// BundleInfo バンドル情報 +type BundleInfo struct { + HostClass string `json:",omitempty"` + ServiceClass string `json:",omitempty"` +} + +// propBundleInfo バンドル情報内包型 +type propBundleInfo struct { + BundleInfo *BundleInfo `json:",omitempty"` // バンドル情報 +} + +// GetBundleInfo バンドル情報 取得 +func (p *propBundleInfo) GetBundleInfo() *BundleInfo { + return p.BundleInfo +} + +func (p *propBundleInfo) IsSophosUTM() bool { + // SophosUTMであれば編集不可 + if p.BundleInfo != nil && strings.Contains(strings.ToLower(p.BundleInfo.ServiceClass), "sophosutm") { + return true + } + return false +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_class.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_class.go new file mode 100644 index 000000000..ac1f09979 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_class.go @@ -0,0 +1,11 @@ +package sacloud + +// propClass クラス内包型 +type propClass struct { + Class string `json:",omitempty"` // サービスクラス +} + +// GetClass クラス 取得 +func (p *propClass) GetClass() string { + return p.Class +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_connected_switches.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_connected_switches.go new file mode 100644 index 000000000..40675841c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_connected_switches.go @@ -0,0 +1,45 @@ +package sacloud + +// propConnectedSwitches 接続先スイッチ内包型 +type propConnectedSwitches struct { + ConnectedSwitches []interface{} `json:",omitempty" libsacloud:"requestOnly"` // サーバー作成時の接続先スイッチ指定用パラメーター +} + +// GetConnectedSwitches 接続先スイッチ 取得 +func (p *propConnectedSwitches) GetConnectedSwitches() []interface{} { + return p.ConnectedSwitches +} + +// SetConnectedSwitches 接続先スイッチ 設定 +func (p *propConnectedSwitches) SetConnectedSwitches(switches []interface{}) { + p.ConnectedSwitches = switches +} + +// ClearConnectedSwitches 接続先スイッチ指定パラメータークリア +func (p *propConnectedSwitches) ClearConnectedSwitches() { + p.ConnectedSwitches = []interface{}{} +} + +// AddPublicNWConnectedParam 共有セグメントへ接続したNIC追加 +func (p *propConnectedSwitches) AddPublicNWConnectedParam() { + if p.ConnectedSwitches == nil { + p.ClearConnectedSwitches() + } + p.ConnectedSwitches = append(p.ConnectedSwitches, map[string]interface{}{"Scope": "shared"}) +} + +// AddExistsSwitchConnectedParam スイッチへ接続したNIC追加 +func (p *propConnectedSwitches) AddExistsSwitchConnectedParam(switchID string) { + if p.ConnectedSwitches == nil { + p.ClearConnectedSwitches() + } + p.ConnectedSwitches = append(p.ConnectedSwitches, map[string]interface{}{"ID": switchID}) +} + +// AddEmptyConnectedParam 未接続なNIC追加 +func (p *propConnectedSwitches) AddEmptyConnectedParam() { + if p.ConnectedSwitches == nil { + p.ClearConnectedSwitches() + } + p.ConnectedSwitches = append(p.ConnectedSwitches, nil) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_copy_source.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_copy_source.go new file mode 100644 index 000000000..bc4dfabb4 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_copy_source.go @@ -0,0 +1,56 @@ +package sacloud + +// propCopySource コピー元情報内包型 +type propCopySource struct { + SourceDisk *Disk `json:",omitempty"` // コピー元ディスク + SourceArchive *Archive `json:",omitempty"` // コピー元アーカイブ + +} + +// SetSourceArchive ソースアーカイブ設定 +func (p *propCopySource) SetSourceArchive(sourceID int64) { + if sourceID == EmptyID { + return + } + p.SourceArchive = &Archive{ + Resource: &Resource{ID: sourceID}, + } + p.SourceDisk = nil +} + +// SetSourceDisk ソースディスク設定 +func (p *propCopySource) SetSourceDisk(sourceID int64) { + if sourceID == EmptyID { + return + } + p.SourceDisk = &Disk{ + Resource: &Resource{ID: sourceID}, + } + p.SourceArchive = nil +} + +// GetSourceArchive ソースアーカイブ取得 +func (p *propCopySource) GetSourceArchive() *Archive { + return p.SourceArchive +} + +// GetSourceDisk ソースディスク取得 +func (p *propCopySource) GetSourceDisk() *Disk { + return p.SourceDisk +} + +// GetSourceArchiveID ソースアーカイブID取得 +func (p *propCopySource) GetSourceArchiveID() int64 { + if p.SourceArchive != nil { + return p.SourceArchive.GetID() + } + return EmptyID +} + +// GetSourceDiskID ソースディスクID取得 +func (p *propCopySource) GetSourceDiskID() int64 { + if p.SourceDisk != nil { + return p.SourceDisk.GetID() + } + return EmptyID +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_cpu.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_cpu.go new file mode 100644 index 000000000..2b1d5b468 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_cpu.go @@ -0,0 +1,11 @@ +package sacloud + +// propCPU CPUコア数内包型 +type propCPU struct { + CPU int `json:",omitempty"` // CPUコア数 +} + +// GetCPU CPUコア数 取得 +func (p *propCPU) GetCPU() int { + return p.CPU +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_description.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_description.go new file mode 100644 index 000000000..755569903 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_description.go @@ -0,0 +1,16 @@ +package sacloud + +// propDescription 説明内包型 +type propDescription struct { + Description string // 説明 +} + +// GetDescription 説明 取得 +func (p *propDescription) GetDescription() string { + return p.Description +} + +// SetDescription 説明 設定 +func (p *propDescription) SetDescription(desc string) { + p.Description = desc +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_connection.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_connection.go new file mode 100644 index 000000000..efd55339c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_connection.go @@ -0,0 +1,33 @@ +package sacloud + +// propDiskConnection ディスク接続情報内包型 +type propDiskConnection struct { + Connection EDiskConnection `json:",omitempty"` // ディスク接続方法 + ConnectionOrder int `json:",omitempty"` // コネクション順序 + +} + +// GetDiskConnection ディスク接続方法 取得 +func (p *propDiskConnection) GetDiskConnection() EDiskConnection { + return p.Connection +} + +// SetDiskConnection ディスク接続方法 設定 +func (p *propDiskConnection) SetDiskConnection(conn EDiskConnection) { + p.Connection = conn +} + +// GetDiskConnectionByStr ディスク接続方法 取得 +func (p *propDiskConnection) GetDiskConnectionByStr() string { + return string(p.Connection) +} + +// SetDiskConnectionByStr ディスク接続方法 設定 +func (p *propDiskConnection) SetDiskConnectionByStr(conn string) { + p.Connection = EDiskConnection(conn) +} + +// GetDiskConnectionOrder コネクション順序 取得 +func (p *propDiskConnection) GetDiskConnectionOrder() int { + return p.ConnectionOrder +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_size.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_size.go new file mode 100644 index 000000000..d5f77c039 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_disk_size.go @@ -0,0 +1,65 @@ +package sacloud + +// propSizeMB サイズ(MB)内包型 +type propSizeMB struct { + SizeMB int `json:",omitempty"` // サイズ(MB単位) +} + +// GetSizeMB サイズ(MB単位) 取得 +func (p *propSizeMB) GetSizeMB() int { + return p.SizeMB +} + +// SetSizeMB サイズ(MB単位) 設定 +func (p *propSizeMB) SetSizeMB(size int) { + p.SizeMB = size +} + +// GetSizeGB サイズ(GB単位) 取得 +func (p *propSizeMB) GetSizeGB() int { + if p.SizeMB <= 0 { + return 0 + } + return p.SizeMB / 1024 +} + +// SetSizeGB サイズ(GB単位) 設定 +func (p *propSizeMB) SetSizeGB(size int) { + if size <= 0 { + p.SizeMB = 0 + } else { + p.SizeMB = size * 1024 + } +} + +// propMigratedMB コピー済みデータサイズ(MB単位)内包型 +type propMigratedMB struct { + MigratedMB int `json:",omitempty"` // コピー済みデータサイズ(MB単位) +} + +// GetMigratedMB サイズ(MB単位) 取得 +func (p *propMigratedMB) GetMigratedMB() int { + return p.MigratedMB +} + +// SetMigratedMB サイズ(MB単位) 設定 +func (p *propMigratedMB) SetMigratedMB(size int) { + p.MigratedMB = size +} + +// GetMigratedGB サイズ(GB単位) 取得 +func (p *propMigratedMB) GetMigratedGB() int { + if p.MigratedMB <= 0 { + return 0 + } + return p.MigratedMB / 1024 +} + +// SetMigratedGB サイズ(GB単位) 設定 +func (p *propMigratedMB) SetMigratedGB(size int) { + if size <= 0 { + p.MigratedMB = 0 + } else { + p.MigratedMB = size * 1024 + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_disks.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_disks.go new file mode 100644 index 000000000..4e57433d8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_disks.go @@ -0,0 +1,22 @@ +package sacloud + +// propDisks ディスク配列内包型 +type propDisks struct { + Disks []Disk `json:",omitempty"` // ディスク +} + +// GetDisks ディスク配列 取得 +func (p *propDisks) GetDisks() []Disk { + return p.Disks +} + +// GetDiskIDs ディスクID配列を返す +func (p *propDisks) GetDiskIDs() []int64 { + + ids := []int64{} + for _, disk := range p.Disks { + ids = append(ids, disk.ID) + } + return ids + +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_distant_from.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_distant_from.go new file mode 100644 index 000000000..084a94378 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_distant_from.go @@ -0,0 +1,26 @@ +package sacloud + +// propDistantFrom ストレージ隔離対象ディスク内包型 +type propDistantFrom struct { + DistantFrom []int64 `json:",omitempty"` // ストレージ隔離対象ディスク +} + +// GetDistantFrom ストレージ隔離対象ディスク 取得 +func (p *propDistantFrom) GetDistantFrom() []int64 { + return p.DistantFrom +} + +// SetDistantFrom ストレージ隔離対象ディスク 設定 +func (p *propDistantFrom) SetDistantFrom(ids []int64) { + p.DistantFrom = ids +} + +// AddDistantFrom ストレージ隔離対象ディスク 追加 +func (p *propDistantFrom) AddDistantFrom(id int64) { + p.DistantFrom = append(p.DistantFrom, id) +} + +// ClearDistantFrom ストレージ隔離対象ディスク クリア +func (p *propDistantFrom) ClearDistantFrom() { + p.DistantFrom = []int64{} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_host.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_host.go new file mode 100644 index 000000000..092023ab5 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_host.go @@ -0,0 +1,19 @@ +package sacloud + +// propHost ホスト(物理)内包型 +type propHost struct { + Host *Host `json:",omitempty"` // サービスクラス +} + +// GetHost ホスト(物理) 取得 +func (p *propHost) GetHost() *Host { + return p.Host +} + +// GetHostName ホスト(物理)名称取得 +func (p *propHost) GetHostName() string { + if p.Host == nil { + return "" + } + return p.Host.GetName() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_host_name.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_host_name.go new file mode 100644 index 000000000..7e7291c6a --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_host_name.go @@ -0,0 +1,11 @@ +package sacloud + +// propHostName ホスト名内包型 +type propHostName struct { + HostName string `json:",omitempty"` // ホスト名 (ディスクの修正実施時に指定した初期ホスト名) +} + +// GetHostName (初期)ホスト名 取得 +func (p *propHostName) GetHostName() string { + return p.HostName +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_icon.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_icon.go new file mode 100644 index 000000000..7a489bc8c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_icon.go @@ -0,0 +1,47 @@ +package sacloud + +// propIcon アイコン内包型 +type propIcon struct { + Icon *Icon // アイコン +} + +// GetIcon アイコンを取得 +func (p *propIcon) GetIcon() *Icon { + return p.Icon +} + +// GetIconID アイコンIDを取得 +func (p *propIcon) GetIconID() int64 { + if p.HasIcon() { + return p.Icon.GetID() + } + return -1 +} + +// GetIconStrID アイコンID(文字列)を取得 +func (p *propIcon) GetIconStrID() string { + if p.HasIcon() { + return p.Icon.GetStrID() + } + return "" +} + +// HasIcon アイコンがセットされているか +func (p *propIcon) HasIcon() bool { + return p.Icon != nil +} + +// SetIconByID 指定のアイコンIDを設定 +func (p *propIcon) SetIconByID(id int64) { + p.Icon = &Icon{Resource: NewResource(id)} +} + +// SetIcon 指定のアイコンオブジェクトを設定 +func (p *propIcon) SetIcon(icon *Icon) { + p.Icon = icon +} + +// ClearIcon アイコンをクリア(空IDを持つアイコンオブジェクトをセット) +func (p *propIcon) ClearIcon() { + p.Icon = &Icon{Resource: NewResource(EmptyID)} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_instance.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_instance.go new file mode 100644 index 000000000..ba4fda6d9 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_instance.go @@ -0,0 +1,59 @@ +package sacloud + +// propInstance インスタンス内包型 +type propInstance struct { + Instance *Instance `json:",omitempty"` // インスタンス +} + +// GetInstance インスタンス 取得 +func (p *propInstance) GetInstance() *Instance { + return p.Instance +} + +// IsUp インスタンスが起動しているか判定 +func (p *propInstance) IsUp() bool { + if p.Instance == nil { + return false + } + return p.Instance.IsUp() +} + +// IsDown インスタンスがダウンしているか確認 +func (p *propInstance) IsDown() bool { + if p.Instance == nil { + return false + } + return p.Instance.IsDown() +} + +// GetInstanceStatus ステータス 取得 +func (p *propInstance) GetInstanceStatus() string { + if p.Instance == nil { + return "" + } + return p.Instance.GetStatus() +} + +// GetInstanceBeforeStatus 以前のステータス 取得 +func (p *propInstance) GetInstanceBeforeStatus() string { + if p.Instance == nil { + return "" + } + return p.Instance.GetBeforeStatus() +} + +// MaintenanceScheduled メンテナンス予定の有無 +func (p *propInstance) MaintenanceScheduled() bool { + if p.Instance == nil { + return false + } + return p.Instance.MaintenanceScheduled() +} + +// GetMaintenanceInfoURL メンテナンス情報 URL取得 +func (p *propInstance) GetMaintenanceInfoURL() string { + if p.Instance == nil { + return "" + } + return p.Instance.Host.InfoURL +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_interface_driver.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_interface_driver.go new file mode 100644 index 000000000..4cfca2be6 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_interface_driver.go @@ -0,0 +1,26 @@ +package sacloud + +// propInterfaceDriver インターフェースドライバ内包型 +type propInterfaceDriver struct { + InterfaceDriver EInterfaceDriver `json:",omitempty"` // NIC +} + +// SetInterfaceDriver インターフェースドライバ 設定 +func (p *propInterfaceDriver) SetInterfaceDriver(v EInterfaceDriver) { + p.InterfaceDriver = v +} + +// GetInterfaceDriver インターフェースドライバ 取得 +func (p *propInterfaceDriver) GetInterfaceDriver() EInterfaceDriver { + return p.InterfaceDriver +} + +// SetInterfaceDriverByString インターフェースドライバ 設定(文字列) +func (p *propInterfaceDriver) SetInterfaceDriverByString(v string) { + p.InterfaceDriver = EInterfaceDriver(v) +} + +// GetInterfaceDriverString インターフェースドライバ 取得(文字列) +func (p *propInterfaceDriver) GetInterfaceDriverString() string { + return string(p.InterfaceDriver) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_interfaces.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_interfaces.go new file mode 100644 index 000000000..5d5ddd8b1 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_interfaces.go @@ -0,0 +1,19 @@ +package sacloud + +// propInterfaces インターフェース(NIC)配列内包型 +type propInterfaces struct { + Interfaces []Interface `json:",omitempty"` // インターフェース +} + +// GetInterfaces インターフェース(NIC)配列 取得 +func (p *propInterfaces) GetInterfaces() []Interface { + return p.Interfaces +} + +// GetFirstInterface インターフェース(NIC)配列の先頭要素を返す +func (p *propInterfaces) GetFirstInterface() *Interface { + if len(p.Interfaces) == 0 { + return nil + } + return &p.Interfaces[0] +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_job_status.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_job_status.go new file mode 100644 index 000000000..9cfcee1fd --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_job_status.go @@ -0,0 +1,11 @@ +package sacloud + +// propJobStatus マイグレーションジョブステータス内包型 +type propJobStatus struct { + JobStatus *MigrationJobStatus `json:",omitempty"` // マイグレーションジョブステータス +} + +// GetJobStatus マイグレーションジョブステータス 取得 +func (p *propJobStatus) GetJobStatus() *MigrationJobStatus { + return p.JobStatus +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_memory.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_memory.go new file mode 100644 index 000000000..5c503a33b --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_memory.go @@ -0,0 +1,19 @@ +package sacloud + +// propMemoryMB サイズ(MB)内包型 +type propMemoryMB struct { + MemoryMB int `json:",omitempty"` // サイズ(MB単位) +} + +// GetMemoryMB サイズ(MB単位) 取得 +func (p *propMemoryMB) GetMemoryMB() int { + return p.MemoryMB +} + +// GetMemoryGB サイズ(GB単位) 取得 +func (p *propMemoryMB) GetMemoryGB() int { + if p.MemoryMB <= 0 { + return 0 + } + return p.MemoryMB / 1024 +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_name.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_name.go new file mode 100644 index 000000000..03f178df8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_name.go @@ -0,0 +1,16 @@ +package sacloud + +// propName 名称内包型 +type propName struct { + Name string `json:",omitempty"` // 名称 +} + +// GetName 名称 取得 +func (p *propName) GetName() string { + return p.Name +} + +// SetName 名称 設定 +func (p *propName) SetName(name string) { + p.Name = name +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_note_class.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_note_class.go new file mode 100644 index 000000000..a3e6fecf1 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_note_class.go @@ -0,0 +1,39 @@ +package sacloud + +// ENoteClass スタートアップスクリプトクラス +type ENoteClass string + +var ( + // NoteClassShell shellクラス + NoteClassShell = ENoteClass("shell") + // NoteClassYAMLCloudConfig yaml_cloud_configクラス + NoteClassYAMLCloudConfig = ENoteClass("yaml_cloud_config") +) + +// ENoteClasses 設定可能なスタートアップスクリプトクラス +var ENoteClasses = []ENoteClass{NoteClassShell, NoteClassYAMLCloudConfig} + +// propNoteClass スタートアップスクリプトクラス情報内包型 +type propNoteClass struct { + Class ENoteClass `json:",omitempty"` // クラス +} + +// GetClass クラス 取得 +func (p *propNoteClass) GetClass() ENoteClass { + return p.Class +} + +// SetClass クラス 設定 +func (p *propNoteClass) SetClass(c ENoteClass) { + p.Class = c +} + +// GetClassStr クラス 取得(文字列) +func (p *propNoteClass) GetClassStr() string { + return string(p.Class) +} + +// SetClassByStr クラス 設定(文字列) +func (p *propNoteClass) SetClassByStr(c string) { + p.Class = ENoteClass(c) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_original_archive_id.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_original_archive_id.go new file mode 100644 index 000000000..39b9d3761 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_original_archive_id.go @@ -0,0 +1,22 @@ +package sacloud + +// propOriginalArchiveID オリジナルアーカイブID内包型 +type propOriginalArchiveID struct { + OriginalArchive *Resource `json:",omitempty"` // オリジナルアーカイブ +} + +// GetOriginalArchiveID プランID 取得 +func (p *propOriginalArchiveID) GetOriginalArchiveID() int64 { + if p.OriginalArchive == nil { + return -1 + } + return p.OriginalArchive.GetID() +} + +// GetStrOriginalArchiveID プランID(文字列) 取得 +func (p *propOriginalArchiveID) GetStrOriginalArchiveID() string { + if p.OriginalArchive == nil { + return "" + } + return p.OriginalArchive.GetStrID() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_plan_id.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_plan_id.go new file mode 100644 index 000000000..1102262c4 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_plan_id.go @@ -0,0 +1,22 @@ +package sacloud + +// propPlanID プランID内包型 +type propPlanID struct { + Plan *Resource `json:",omitempty"` // プラン +} + +// GetPlanID プランID 取得 +func (p *propPlanID) GetPlanID() int64 { + if p.Plan == nil { + return -1 + } + return p.Plan.GetID() +} + +// GetStrPlanID プランID(文字列) 取得 +func (p *propPlanID) GetStrPlanID() string { + if p.Plan == nil { + return "" + } + return p.Plan.GetStrID() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host.go new file mode 100644 index 000000000..18059413e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host.go @@ -0,0 +1,21 @@ +package sacloud + +// propPrivateHost 専有ホスト内包型 +type propPrivateHost struct { + PrivateHost *PrivateHost // 専有ホスト +} + +// SetPrivateHostByID 指定のアイコンIDを設定 +func (p *propPrivateHost) SetPrivateHostByID(id int64) { + p.PrivateHost = &PrivateHost{Resource: NewResource(id)} +} + +// SetPrivateHost 指定のアイコンオブジェクトを設定 +func (p *propPrivateHost) SetPrivateHost(icon *PrivateHost) { + p.PrivateHost = icon +} + +// ClearPrivateHost アイコンをクリア(空IDを持つアイコンオブジェクトをセット) +func (p *propPrivateHost) ClearPrivateHost() { + p.PrivateHost = &PrivateHost{Resource: NewResource(EmptyID)} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host_plan.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host_plan.go new file mode 100644 index 000000000..572502b47 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_private_host_plan.go @@ -0,0 +1,51 @@ +package sacloud + +// propPrivateHostPlan 専有ホストプラン内包型 +type propPrivateHostPlan struct { + Plan *ProductPrivateHost `json:",omitempty"` // 専有ホストプラン +} + +// GetPrivateHostPlan 専有ホストプラン取得 +func (p *propPrivateHostPlan) GetPrivateHostPlan() *ProductPrivateHost { + return p.Plan +} + +// SetPrivateHostPlan 専有ホストプラン設定 +func (p *propPrivateHostPlan) SetPrivateHostPlan(plan *ProductPrivateHost) { + p.Plan = plan +} + +// SetPrivateHostPlanByID 専有ホストプラン設定 +func (p *propPrivateHostPlan) SetPrivateHostPlanByID(planID int64) { + if p.Plan == nil { + p.Plan = &ProductPrivateHost{} + } + p.Plan.Resource = NewResource(planID) +} + +// GetCPU CPUコア数 取得 +func (p *propPrivateHostPlan) GetCPU() int { + if p.Plan == nil { + return -1 + } + + return p.Plan.GetCPU() +} + +// GetMemoryMB メモリ(MB) 取得 +func (p *propPrivateHostPlan) GetMemoryMB() int { + if p.Plan == nil { + return -1 + } + + return p.Plan.GetMemoryMB() +} + +// GetMemoryGB メモリ(GB) 取得 +func (p *propPrivateHostPlan) GetMemoryGB() int { + if p.Plan == nil { + return -1 + } + + return p.Plan.GetMemoryGB() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_region.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_region.go new file mode 100644 index 000000000..1a38851c8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_region.go @@ -0,0 +1,44 @@ +package sacloud + +// propRegion リージョン内包型 +type propRegion struct { + Region *Region `json:",omitempty"` // リージョン +} + +// GetRegion リージョン 取得 +func (p *propRegion) GetRegion() *Region { + return p.Region +} + +// GetRegionID リージョンID 取得 +func (p *propRegion) GetRegionID() int64 { + if p.Region == nil { + return -1 + } + return p.Region.GetID() +} + +// GetRegionName リージョン名 取得 +func (p *propRegion) GetRegionName() string { + if p.Region == nil { + return "" + } + return p.Region.GetName() +} + +// GetRegionDescription リージョン説明 取得 +func (p *propRegion) GetRegionDescription() string { + if p.Region == nil { + return "" + } + return p.Region.GetDescription() +} + +// GetRegionNameServers リージョンのネームサーバー(のIPアドレス)取得 +func (p *propRegion) GetRegionNameServers() []string { + if p.Region == nil { + return []string{} + } + + return p.Region.GetNameServers() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_scope.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_scope.go new file mode 100644 index 000000000..d1146a6db --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_scope.go @@ -0,0 +1,42 @@ +package sacloud + +// propScope スコープ内包型 +type propScope struct { + Scope string `json:",omitempty"` // スコープ +} + +// GetScope スコープ 取得 +func (p *propScope) GetScope() string { + return p.Scope +} + +// SetScope スコープ 設定 +func (p *propScope) SetScope(scope string) { + p.Scope = scope +} + +// SetSharedScope 共有スコープに設定 +func (p *propScope) SetSharedScope() { + p.Scope = string(ESCopeShared) +} + +// SetUserScope ユーザースコープに設定 +func (p *propScope) SetUserScope() { + p.Scope = string(ESCopeUser) +} + +// IsSharedScope 共有スコープか判定 +func (p *propScope) IsSharedScope() bool { + if p == nil { + return false + } + return p.Scope == string(ESCopeShared) +} + +// IsUserScope ユーザースコープか判定 +func (p *propScope) IsUserScope() bool { + if p == nil { + return false + } + return p.Scope == string(ESCopeUser) +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_server.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_server.go new file mode 100644 index 000000000..2d573082e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_server.go @@ -0,0 +1,21 @@ +package sacloud + +// propServer 接続先サーバー内包型 +type propServer struct { + Server *Server `json:",omitempty"` // 接続先サーバー +} + +// GetServer 接続先サーバー 取得 +func (p *propServer) GetServer() *Server { + return p.Server +} + +// SetServer 接続先サーバー 設定 +func (p *propServer) SetServer(server *Server) { + p.Server = server +} + +// SetServerID サーバーIDの設定 +func (p *propServer) SetServerID(id int64) { + p.Server = &Server{Resource: &Resource{ID: id}} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_server_plan.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_server_plan.go new file mode 100644 index 000000000..b5fa34540 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_server_plan.go @@ -0,0 +1,51 @@ +package sacloud + +// propServerPlan サーバープラン内包型 +type propServerPlan struct { + ServerPlan *ProductServer `json:",omitempty"` // サーバープラン +} + +// GetServerPlan サーバープラン取得 +func (p *propServerPlan) GetServerPlan() *ProductServer { + return p.ServerPlan +} + +// SetServerPlan サーバープラン設定 +func (p *propServerPlan) SetServerPlan(plan *ProductServer) { + p.ServerPlan = plan +} + +// SetServerPlanByID サーバープラン設定 +func (p *propServerPlan) SetServerPlanByID(planID string) { + if p.ServerPlan == nil { + p.ServerPlan = &ProductServer{} + } + p.ServerPlan.Resource = NewResourceByStringID(planID) +} + +// GetCPU CPUコア数 取得 +func (p *propServerPlan) GetCPU() int { + if p.ServerPlan == nil { + return -1 + } + + return p.ServerPlan.GetCPU() +} + +// GetMemoryMB メモリ(MB) 取得 +func (p *propServerPlan) GetMemoryMB() int { + if p.ServerPlan == nil { + return -1 + } + + return p.ServerPlan.GetMemoryMB() +} + +// GetMemoryGB メモリ(GB) 取得 +func (p *propServerPlan) GetMemoryGB() int { + if p.ServerPlan == nil { + return -1 + } + + return p.ServerPlan.GetMemoryGB() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_service_class.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_service_class.go new file mode 100644 index 000000000..8b5d21686 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_service_class.go @@ -0,0 +1,11 @@ +package sacloud + +// propServiceClass サービスクラス内包型 +type propServiceClass struct { + ServiceClass string `json:",omitempty"` // サービスクラス +} + +// GetServiceClass サービスクラス 取得 +func (p *propServiceClass) GetServiceClass() string { + return p.ServiceClass +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_storage.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_storage.go new file mode 100644 index 000000000..fd45a84a8 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_storage.go @@ -0,0 +1,11 @@ +package sacloud + +// propStorage ストーレジ内包型 +type propStorage struct { + Storage *Storage `json:",omitempty"` // ストレージ +} + +// GetStorage ストレージ 取得 +func (p *propStorage) GetStorage() *Storage { + return p.Storage +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_storage_class.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_storage_class.go new file mode 100644 index 000000000..aba0e28d4 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_storage_class.go @@ -0,0 +1,11 @@ +package sacloud + +// propStorageClass ストレージクラス内包型 +type propStorageClass struct { + StorageClass string `json:",omitempty"` // ストレージクラス +} + +// GetStorageClass ストレージクラス 取得 +func (p *propStorageClass) GetStorageClass() string { + return p.StorageClass +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_switch.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_switch.go new file mode 100644 index 000000000..f217c6bfc --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_switch.go @@ -0,0 +1,21 @@ +package sacloud + +// propSwitch スイッチ内包型 +type propSwitch struct { + Switch *Switch `json:",omitempty"` // スイッチ +} + +// GetSwitch スイッチ 取得 +func (p *propSwitch) GetSwitch() *Switch { + return p.Switch +} + +// SetSwitch スイッチ 設定 +func (p *propSwitch) SetSwitch(sw *Switch) { + p.Switch = sw +} + +// SetSwitchID スイッチID 設定 +func (p *Interface) SetSwitchID(id int64) { + p.Switch = &Switch{Resource: &Resource{ID: id}} +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_tags.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_tags.go new file mode 100644 index 000000000..450cebd35 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_tags.go @@ -0,0 +1,57 @@ +package sacloud + +// propTags タグ内包型 +type propTags struct { + Tags []string // タグ +} + +// HasTag 指定のタグを持っているか判定 +func (p *propTags) HasTag(target string) bool { + + for _, tag := range p.Tags { + if target == tag { + return true + } + } + + return false +} + +// AppendTag タグを追加 +func (p *propTags) AppendTag(target string) { + if p.HasTag(target) { + return + } + + p.Tags = append(p.Tags, target) +} + +// RemoveTag 指定のタグを削除 +func (p *propTags) RemoveTag(target string) { + if !p.HasTag(target) { + return + } + res := []string{} + for _, tag := range p.Tags { + if tag != target { + res = append(res, tag) + } + } + + p.Tags = res +} + +// ClearTags 全タグを削除 +func (p *propTags) ClearTags() { + p.Tags = []string{} +} + +// GetTags タグ取得 +func (p *propTags) GetTags() []string { + return p.Tags +} + +// SetTags タグを設定 +func (p *propTags) SetTags(tags []string) { + p.Tags = tags +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_timestamp.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_timestamp.go new file mode 100644 index 000000000..732fd07f6 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_timestamp.go @@ -0,0 +1,35 @@ +package sacloud + +import "time" + +// propCreatedAt 作成日時内包型 +type propCreatedAt struct { + CreatedAt *time.Time `json:",omitempty"` // 作成日時 +} + +// GetCreatedAt 作成日時 取得 +func (p *propCreatedAt) GetCreatedAt() *time.Time { + return p.CreatedAt +} + +// propModifiedAt 変更日時内包型 +type propModifiedAt struct { + // ModifiedAt 変更日時 + ModifiedAt *time.Time `json:",omitempty"` +} + +// GetModifiedAt 変更日時 取得 +func (p *propModifiedAt) GetModifiedAt() *time.Time { + return p.ModifiedAt +} + +// propUpdatedAt 変更日時内包型 +type propUpdatedAt struct { + // UpdatedAt 変更日時 + UpdatedAt *time.Time `json:",omitempty"` +} + +// GetModifiedAt 変更日時 取得 +func (p *propUpdatedAt) GetModifiedAt() *time.Time { + return p.UpdatedAt +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/prop_zone.go b/vendor/github.com/sacloud/libsacloud/sacloud/prop_zone.go new file mode 100644 index 000000000..2fdfb8993 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/prop_zone.go @@ -0,0 +1,118 @@ +package sacloud + +// PropZone ゾーン内包型 +type propZone struct { + Zone *Zone `json:",omitempty"` // ゾーン +} + +// GetZone ゾーン 取得 +func (p *propZone) GetZone() *Zone { + return p.Zone +} + +// GetZoneID ゾーンID 取得 +func (p *propZone) GetZoneID() int64 { + if p.Zone == nil { + return -1 + } + return p.Zone.GetID() +} + +// GetZoneName ゾーン名 取得 +func (p *propZone) GetZoneName() string { + if p.Zone == nil { + return "" + } + + return p.Zone.GetName() +} + +// GetZoneDescription ゾーン説明 取得 +func (p *propZone) GetZoneDescription() string { + if p.Zone == nil { + return "" + } + return p.Zone.GetDescription() +} + +// GetRegion リージョン 取得 +func (p *propZone) GetRegion() *Region { + if p.Zone == nil { + return nil + } + return p.Zone.GetRegion() +} + +// GetRegionID リージョンID 取得 +func (p *propZone) GetRegionID() int64 { + if p.Zone == nil { + return -1 + } + return p.Zone.GetRegionID() +} + +// GetRegionName リージョン名 取得 +func (p *propZone) GetRegionName() string { + if p.Zone == nil { + return "" + } + return p.Zone.GetRegionName() +} + +// GetRegionDescription リージョン説明 取得 +func (p *propZone) GetRegionDescription() string { + if p.Zone == nil { + return "" + } + return p.Zone.GetRegionDescription() +} + +// GetRegionNameServers リージョンのネームサーバー(のIPアドレス)取得 +func (p *propZone) GetRegionNameServers() []string { + if p.Zone == nil { + return []string{} + } + + return p.Zone.GetRegionNameServers() +} + +// ZoneIsDummy ダミーフラグ 取得 +func (p *propZone) ZoneIsDummy() bool { + if p.Zone == nil { + return false + } + return p.Zone.ZoneIsDummy() +} + +// GetVNCProxyHostName VNCプロキシホスト名 取得 +func (p *propZone) GetVNCProxyHostName() string { + if p.Zone == nil { + return "" + } + + return p.Zone.GetVNCProxyHostName() +} + +// GetVPCProxyIPAddress VNCプロキシIPアドレス 取得 +func (p *propZone) GetVPCProxyIPAddress() string { + if p.Zone == nil { + return "" + } + return p.Zone.GetVPCProxyIPAddress() +} + +// GetFTPHostName FTPサーバーホスト名 取得 +func (p *propZone) GetFTPHostName() string { + if p.Zone == nil { + return "" + } + return p.Zone.GetFTPHostName() +} + +// GetFTPServerIPAddress FTPサーバーIPアドレス 取得 +func (p *propZone) GetFTPServerIPAddress() string { + if p.Zone == nil { + return "" + } + return p.Zone.GetFTPServerIPAddress() +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/public_price.go b/vendor/github.com/sacloud/libsacloud/sacloud/public_price.go new file mode 100644 index 000000000..8d85c3d30 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/public_price.go @@ -0,0 +1,18 @@ +package sacloud + +// PublicPrice 料金 +type PublicPrice struct { + DisplayName string `json:",omitempty"` // 表示名 + IsPublic bool `json:",omitempty"` // 公開フラグ + ServiceClassID int `json:",omitempty"` // サービスクラスID + ServiceClassName string `json:",omitempty"` // サービスクラス名 + ServiceClassPath string `json:",omitempty"` // サービスクラスパス + + Price struct { // 価格 + Base int `json:",omitempty"` // 基本料金 + Daily int `json:",omitempty"` // 日単位料金 + Hourly int `json:",omitempty"` // 時間単位料金 + Monthly int `json:",omitempty"` // 分単位料金 + Zone string `json:",omitempty"` // ゾーン + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/region.go b/vendor/github.com/sacloud/libsacloud/sacloud/region.go new file mode 100644 index 000000000..1e31f421e --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/region.go @@ -0,0 +1,18 @@ +package sacloud + +// Region リージョン +type Region struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + + NameServers []string `json:",omitempty"` // NameServers ネームサーバー +} + +// GetNameServers リージョン内のネームサーバー取得 +func (r *Region) GetNameServers() []string { + if r == nil { + return []string{} + } + return r.NameServers +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/server.go b/vendor/github.com/sacloud/libsacloud/sacloud/server.go new file mode 100644 index 000000000..37ed43b64 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/server.go @@ -0,0 +1,145 @@ +package sacloud + +import "fmt" + +// Server サーバー +type Server struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propHostName // ホスト名 + propInterfaceDriver // インターフェースドライバ + propAvailability // 有功状態 + propServerPlan // サーバープラン + propZone // ゾーン + propServiceClass // サービスクラス + propConnectedSwitches // 接続スイッチ + propDisks // ディスク配列 + propInstance // インスタンス + propInterfaces // インターフェース配列 + propPrivateHost // 専有ホスト + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 +} + +// DNSServers サーバの所属するリージョンの推奨ネームサーバリスト +func (s *Server) DNSServers() []string { + return s.Zone.Region.NameServers +} + +// IPAddress サーバの1番目のNIC(eth0)のIPアドレス +func (s *Server) IPAddress() string { + // has NIC? + if len(s.Interfaces) == 0 { + return "" + } + ip := s.Interfaces[0].IPAddress + if ip == "" { + ip = s.Interfaces[0].UserIPAddress + } + return ip +} + +// Gateway デフォルトゲートウェイアドレス +func (s *Server) Gateway() string { + if len(s.Interfaces) == 0 || s.Interfaces[0].Switch == nil || s.Interfaces[0].Switch.UserSubnet == nil { + return "" + } + return s.Interfaces[0].Switch.UserSubnet.DefaultRoute +} + +// DefaultRoute デフォルトゲートウェイアドレス(Gatewayのエイリアス) +func (s *Server) DefaultRoute() string { + return s.Gateway() +} + +// NetworkMaskLen サーバの1番目のNIC(eth0)のネットワークマスク長 +func (s *Server) NetworkMaskLen() int { + if len(s.Interfaces) == 0 || s.Interfaces[0].Switch == nil || s.Interfaces[0].Switch.UserSubnet == nil { + return 0 + } + return s.Interfaces[0].Switch.UserSubnet.NetworkMaskLen +} + +// NetworkAddress サーバの1番目のNIC(eth0)のネットワークアドレス +func (s *Server) NetworkAddress() string { + if len(s.Interfaces) == 0 || s.Interfaces[0].Switch == nil || s.Interfaces[0].Switch.Subnet == nil { + return "" + } + return s.Interfaces[0].Switch.Subnet.NetworkAddress +} + +// CIDRIPAddress サーバの1番目のNIC(eth0)のIPアドレス+ネットワークマスク長 +func (s *Server) CIDRIPAddress() string { + ip, maskLen := s.IPAddress(), s.NetworkMaskLen() + if ip != "" && maskLen > 0 { + return fmt.Sprintf("%s/%d", ip, maskLen) + } + return "" +} + +const ( + // ServerMaxInterfaceLen サーバーに接続できるNICの最大数 + ServerMaxInterfaceLen = 10 + // ServerMaxDiskLen サーバーに接続できるディスクの最大数 + ServerMaxDiskLen = 4 +) + +// KeyboardRequest キーボード送信リクエスト +type KeyboardRequest struct { + Keys []string `json:",omitempty"` // キー(複数) + Key string `json:",omitempty"` // キー(単体) +} + +// MouseRequest マウス送信リクエスト +type MouseRequest struct { + X *int `json:",omitempty"` // X + Y *int `json:",omitempty"` // Y + Z *int `json:",omitempty"` // Z + Buttons *MouseRequestButtons `json:",omitempty"` // マウスボタン + +} + +// VNCSnapshotRequest VNCスナップショット取得リクエスト +type VNCSnapshotRequest struct { + ScreenSaverExitTimeMS int `json:",omitempty"` // スクリーンセーバーからの復帰待ち時間 +} + +// MouseRequestButtons マウスボタン +type MouseRequestButtons struct { + L bool `json:",omitempty"` // 左ボタン + R bool `json:",omitempty"` // 右ボタン + M bool `json:",omitempty"` // 中ボタン +} + +// VNCProxyResponse VNCプロキシ取得レスポンス +type VNCProxyResponse struct { + *ResultFlagValue + Status string `json:",omitempty"` // ステータス + Host string `json:",omitempty"` // プロキシホスト + IOServerHost string `json:",omitempty"` // 新プロキシホスト(Hostがlocalhostの場合にこちらを利用する) + Port string `json:",omitempty"` // ポート番号 + Password string `json:",omitempty"` // VNCパスワード + VNCFile string `json:",omitempty"` // VNC接続情報ファイル(VNCビューア用) +} + +// ActualHost プロキシホスト名(Host or IOServerHost)を返す +func (r *VNCProxyResponse) ActualHost() string { + host := r.Host + if host == "localhost" { + host = r.IOServerHost + } + return host +} + +// VNCSizeResponse VNC画面サイズレスポンス +type VNCSizeResponse struct { + Width int `json:",string,omitempty"` // 幅 + Height int `json:",string,omitempty"` // 高さ +} + +// VNCSnapshotResponse VPCスナップショットレスポンス +type VNCSnapshotResponse struct { + Image string `json:",omitempty"` // スナップショット画像データ +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/sim.go b/vendor/github.com/sacloud/libsacloud/sacloud/sim.go new file mode 100644 index 000000000..e513a1e8a --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/sim.go @@ -0,0 +1,111 @@ +package sacloud + +import ( + "encoding/json" + "strings" + "time" +) + +// SIM SIM(CommonServiceItem) +type SIM struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + propAvailability // 有効状態 + + Status SIMStatus `json:",omitempty"` // ステータス + Provider SIMProvider `json:",omitempty"` // プロバイダ + Remark *SIMRemark `json:",omitempty"` // Remark +} + +// SIMStatus SIMステータス +type SIMStatus struct { + ICCID string `json:",omitempty"` // ICCID + SIMInfo *SIMInfo `json:"sim,omitempty"` // SIM詳細情報 +} + +// SIMInfo SIM詳細情報 +type SIMInfo struct { + ICCID string `json:"iccid,omitempty"` + IMSI []string `json:"imsi,omitempty"` + IP string `json:"ip,omitempty"` + SessionStatus string `json:"session_status,omitempty"` + IMEILock bool `json:"imei_lock,omitempty"` + Registered bool `json:"registered,omitempty"` + Activated bool `json:"activated,omitempty"` + ResourceID string `json:"resource_id,omitempty"` + RegisteredDate *time.Time `json:"registered_date,omitempty"` + ActivatedDate *time.Time `json:"activated_date,omitempty"` + DeactivatedDate *time.Time `json:"deactivated_date,omitempty"` + SIMGroupID string `json:"simgroiup_id,omitempty"` + TrafficBytesOfCurrentMonth *SIMTrafficBytes `json:"traffic_bytes_of_current_month,omitempty"` + ConnectedIMEI string `json:"connected_imei,omitempty"` +} + +// SIMTrafficBytes 当月通信量 +type SIMTrafficBytes struct { + UplinkBytes int64 `json:"uplink_bytes,omitempty"` + DownlinkBytes int64 `json:"downlink_bytes,omitempty"` +} + +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (s *SIMTrafficBytes) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `[]` { + return nil + } + tmp := &struct { + UplinkBytes int64 `json:"uplink_bytes,omitempty"` + DownlinkBytes int64 `json:"downlink_bytes,omitempty"` + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + s.UplinkBytes = tmp.UplinkBytes + s.DownlinkBytes = tmp.DownlinkBytes + return nil +} + +// SIMRemark remark +type SIMRemark struct { + PassCode string `json:",omitempty"` +} + +// SIMProvider プロバイダ +type SIMProvider struct { + Class string `json:",omitempty"` // クラス + Name string `json:",omitempty"` + ServiceClass string `json:",omitempty"` +} + +// SIMLog SIMログ +type SIMLog struct { + Date *time.Time `json:"date,omitempty"` + SessionStatus string `json:"session_status,omitempty"` + ResourceID string `json:"resource_id,omitempty"` + IMEI string `json:"imei,omitempty"` + IMSI string `json:"imsi,omitempty"` +} + +// CreateNewSIM SIM作成 +func CreateNewSIM(name string, iccID string, passcode string) *SIM { + return &SIM{ + Resource: &Resource{}, + propName: propName{Name: name}, + Provider: SIMProvider{ + Class: "sim", + }, + Status: SIMStatus{ + ICCID: iccID, + }, + Remark: &SIMRemark{ + PassCode: passcode, + }, + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go b/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go new file mode 100644 index 000000000..5df470976 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/simple_monitor.go @@ -0,0 +1,254 @@ +package sacloud + +// SimpleMonitor シンプル監視 +type SimpleMonitor struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + propModifiedAt // 変更日時 + + Settings *SimpleMonitorSettings `json:",omitempty"` // 設定 + Status *SimpleMonitorStatus `json:",omitempty"` // ステータス + Provider *SimpleMonitorProvider `json:",omitempty"` // プロバイダ + +} + +// SimpleMonitorSettings シンプル監視設定 リスト +type SimpleMonitorSettings struct { + SimpleMonitor *SimpleMonitorSetting `json:",omitempty"` // シンプル監視設定値 +} + +// SimpleMonitorSetting シンプル監視設定 +type SimpleMonitorSetting struct { + DelayLoop int `json:",omitempty"` // 監視間隔 + HealthCheck *SimpleMonitorHealthCheck `json:",omitempty"` // ヘルスチェック + Enabled string `json:",omitempty"` // 有効/無効 + NotifyEmail *SimpleMonitorNotify `json:",omitempty"` // Email通知 + NotifySlack *SimpleMonitorNotify `json:",omitempty"` // Slack通知 +} + +// SimpleMonitorStatus シンプル監視ステータス +type SimpleMonitorStatus struct { + Target string `json:",omitempty"` // 対象(IP or FQDN) +} + +// SimpleMonitorProvider プロバイダ +type SimpleMonitorProvider struct { + *Resource // ID + propName // 名称 + propServiceClass // サービスクラス + + Class string `json:",omitempty"` // クラス +} + +// SimpleMonitorHealthCheck ヘルスチェック +type SimpleMonitorHealthCheck struct { + Protocol string `json:",omitempty"` // プロトコル + Port string `json:",omitempty"` // ポート + Path string `json:",omitempty"` // HTTP/HTTPS監視の場合のリクエストパス + Status string `json:",omitempty"` // HTTP/HTTPS監視の場合の期待ステータスコード + SNI string `json:",omitempty"` // HTTPS監視時のSNI有効/無効 + Host string `json:",omitempty"` // 対象ホスト(IP or FQDN) + QName string `json:",omitempty"` // DNS監視の場合の問い合わせFQDN + ExpectedData string `json:",omitempty"` // 期待値 + Community string `json:",omitempty"` // SNMP監視の場合のコミュニティ名 + SNMPVersion string `json:",omitempty"` // SNMP監視 SNMPバージョン + OID string `json:",omitempty"` // SNMP監視 OID + RemainingDays int `json:",omitempty"` // SSL証明書 有効残日数 +} + +// SimpleMonitorNotify シンプル監視通知 +type SimpleMonitorNotify struct { + Enabled string `json:",omitempty"` // 有効/無効 + HTML string `json:",omitempty"` // メール通知の場合のHTMLメール有効フラグ + IncomingWebhooksURL string `json:",omitempty"` // Slack通知の場合のWebhook URL +} + +// CreateNewSimpleMonitor シンプル監視作成 +func CreateNewSimpleMonitor(target string) *SimpleMonitor { + return &SimpleMonitor{ + propName: propName{Name: target}, + Provider: &SimpleMonitorProvider{ + Class: "simplemon", + }, + Status: &SimpleMonitorStatus{ + Target: target, + }, + Settings: &SimpleMonitorSettings{ + SimpleMonitor: &SimpleMonitorSetting{ + HealthCheck: &SimpleMonitorHealthCheck{}, + Enabled: "True", + NotifyEmail: &SimpleMonitorNotify{ + Enabled: "False", + }, + NotifySlack: &SimpleMonitorNotify{ + Enabled: "False", + }, + }, + }, + } + +} + +// AllowSimpleMonitorHealthCheckProtocol シンプル監視対応プロトコルリスト +func AllowSimpleMonitorHealthCheckProtocol() []string { + return []string{"http", "https", "ping", "tcp", "dns", "ssh", "smtp", "pop3", "snmp", "sslcertificate"} +} + +func createSimpleMonitorNotifyEmail(withHTML bool) *SimpleMonitorNotify { + n := &SimpleMonitorNotify{ + Enabled: "True", + HTML: "False", + } + + if withHTML { + n.HTML = "True" + } + + return n +} + +func createSimpleMonitorNotifySlack(incomingWebhooksURL string) *SimpleMonitorNotify { + return &SimpleMonitorNotify{ + Enabled: "True", + IncomingWebhooksURL: incomingWebhooksURL, + } + +} + +// SetTarget 対象ホスト(IP or FQDN)の設定 +func (s *SimpleMonitor) SetTarget(target string) { + s.Name = target + s.Status.Target = target +} + +// SetHealthCheckPing pingでのヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckPing() { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "ping", + } +} + +// SetHealthCheckTCP TCPでのヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckTCP(port string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "tcp", + Port: port, + } +} + +// SetHealthCheckHTTP HTTPでのヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckHTTP(port string, path string, status string, host string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "http", + Port: port, + Path: path, + Status: status, + Host: host, + } +} + +// SetHealthCheckHTTPS HTTPSでのヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckHTTPS(port string, path string, status string, host string, sni bool) { + strSNI := "False" + if sni { + strSNI = "True" + } + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "https", + Port: port, + Path: path, + Status: status, + Host: host, + SNI: strSNI, + } +} + +// SetHealthCheckDNS DNSクエリでのヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckDNS(qname string, expectedData string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "dns", + QName: qname, + ExpectedData: expectedData, + } +} + +// SetHealthCheckSSH SSHヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckSSH(port string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "ssh", + Port: port, + } +} + +// SetHealthCheckSMTP SMTPヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckSMTP(port string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "smtp", + Port: port, + } +} + +// SetHealthCheckPOP3 POP3ヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckPOP3(port string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "pop3", + Port: port, + } +} + +// SetHealthCheckSNMP SNMPヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckSNMP(community string, version string, oid string, expectedData string) { + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "snmp", + Community: community, + SNMPVersion: version, + OID: oid, + ExpectedData: expectedData, + } +} + +// SetHealthCheckSSLCertificate SSLサーバ証明書有効期限ヘルスチェック設定 +func (s *SimpleMonitor) SetHealthCheckSSLCertificate(remainingDays int) { + // set default + if remainingDays < 0 { + remainingDays = 30 + } + s.Settings.SimpleMonitor.HealthCheck = &SimpleMonitorHealthCheck{ + Protocol: "sslcertificate", + RemainingDays: remainingDays, + } +} + +// EnableNotifyEmail Email通知の有効か +func (s *SimpleMonitor) EnableNotifyEmail(withHTML bool) { + s.Settings.SimpleMonitor.NotifyEmail = createSimpleMonitorNotifyEmail(withHTML) +} + +// DisableNotifyEmail Email通知の無効化 +func (s *SimpleMonitor) DisableNotifyEmail() { + s.Settings.SimpleMonitor.NotifyEmail = &SimpleMonitorNotify{ + Enabled: "False", + } +} + +// EnableNofitySlack Slack通知の有効化 +func (s *SimpleMonitor) EnableNofitySlack(incomingWebhooksURL string) { + s.Settings.SimpleMonitor.NotifySlack = createSimpleMonitorNotifySlack(incomingWebhooksURL) +} + +// DisableNotifySlack Slack通知の無効化 +func (s *SimpleMonitor) DisableNotifySlack() { + s.Settings.SimpleMonitor.NotifySlack = &SimpleMonitorNotify{ + Enabled: "False", + } +} + +// SetDelayLoop 監視間隔の設定 +func (s *SimpleMonitor) SetDelayLoop(loop int) { + s.Settings.SimpleMonitor.DelayLoop = loop +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/sitetosite_connection_detail.go b/vendor/github.com/sacloud/libsacloud/sacloud/sitetosite_connection_detail.go new file mode 100644 index 000000000..9ff081a78 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/sitetosite_connection_detail.go @@ -0,0 +1,38 @@ +package sacloud + +// SiteToSiteConnectionDetail サイト間VPN接続詳細情報 +type SiteToSiteConnectionDetail struct { + ESP struct { + AuthenticationProtocol string + DHGroup string + EncryptionProtocol string + Lifetime string + Mode string + PerfectForwardSecrecy string + } + IKE struct { + AuthenticationProtocol string + EncryptionProtocol string + Lifetime string + Mode string + PerfectForwardSecrecy string + PreSharedSecret string + } + Peer struct { + ID string + InsideNetworks []string + OutsideIPAddress string + } + VPCRouter struct { + ID string + InsideNetworks []string + OutsideIPAddress string + } +} + +// SiteToSiteConnectionInfo サイト間VPN接続情報 +type SiteToSiteConnectionInfo struct { + Details struct { + Config []SiteToSiteConnectionDetail + } +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/ssh_key.go b/vendor/github.com/sacloud/libsacloud/sacloud/ssh_key.go new file mode 100644 index 000000000..5d6b59240 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/ssh_key.go @@ -0,0 +1,38 @@ +package sacloud + +// SSHKey 公開鍵 +type SSHKey struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propCreatedAt // 作成日時 + + PublicKey string `json:",omitempty"` // 公開鍵 + Fingerprint string `json:",omitempty"` // フィンガープリント +} + +// SSHKeyGenerated 公開鍵生成戻り値(秘密鍵のダウンロード用) +type SSHKeyGenerated struct { + SSHKey + PrivateKey string `json:",omitempty"` // 秘密鍵 +} + +// GetPublicKey 公開鍵取得 +func (k *SSHKey) GetPublicKey() string { + return k.PublicKey +} + +// SetPublicKey 公開鍵設定 +func (k *SSHKey) SetPublicKey(pKey string) { + k.PublicKey = pKey +} + +// GetFingerprint フィンガープリント取得 +func (k *SSHKey) GetFingerprint() string { + return k.Fingerprint +} + +// GetPrivateKey 秘密鍵取得 +func (k *SSHKeyGenerated) GetPrivateKey() string { + return k.PrivateKey +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/storage.go b/vendor/github.com/sacloud/libsacloud/sacloud/storage.go new file mode 100644 index 000000000..31555a677 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/storage.go @@ -0,0 +1,18 @@ +package sacloud + +// Storage ストレージ +type Storage struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propZone // ゾーン + + Class string `json:",omitempty"` // クラス + DiskPlan struct { // ディスクプラン + *Resource // ID + propName // 名称 + propStorageClass // ストレージクラス + } `json:",omitempty"` + + //Capacity []string `json:",omitempty"` +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/subnet.go b/vendor/github.com/sacloud/libsacloud/sacloud/subnet.go new file mode 100644 index 000000000..cd4296e23 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/subnet.go @@ -0,0 +1,18 @@ +package sacloud + +// Subnet IPv4サブネット +type Subnet struct { + *Resource // ID + propServiceClass // サービスクラス + propCreatedAt // 作成日時 + + DefaultRoute string `json:",omitempty"` // デフォルトルート + IPAddresses []*IPAddress `json:",omitempty"` // IPv4アドレス範囲 + NetworkAddress string `json:",omitempty"` // ネットワークアドレス + NetworkMaskLen int `json:",omitempty"` // ネットワークマスク長 + ServiceID int64 `json:",omitempty"` // サービスID + StaticRoute string `json:",omitempty"` // スタティックルート + NextHop string `json:",omitempty"` // ネクストホップ + Switch *Switch `json:",omitempty"` // スイッチ + Internet *Internet `json:",omitempty"` // ルーター +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/switch.go b/vendor/github.com/sacloud/libsacloud/sacloud/switch.go new file mode 100644 index 000000000..cb90bfb1b --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/switch.go @@ -0,0 +1,84 @@ +package sacloud + +import ( + "encoding/json" + "fmt" + "net" +) + +// Switch スイッチ +type Switch struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propServiceClass // サービスクラス + propIcon // アイコン + propTags // タグ + propCreatedAt // 作成日時 + + ServerCount int `json:",omitempty"` // 接続サーバー数 + ApplianceCount int `json:",omitempty"` // 接続アプライアンス数 + Scope EScope `json:",omitempty"` // スコープ + Subnet *Subnet `json:",omitempty"` // サブネット + UserSubnet *Subnet `json:",omitempty"` // ユーザー定義サブネット + Subnets []SwitchSubnet `json:",omitempty"` // サブネット + IPv6Nets []IPv6Net `json:",omitempty"` // IPv6サブネットリスト + Internet *Internet `json:",omitempty"` // ルーター + + Bridge *struct { // 接続先ブリッジ(Info.Switches配下のIDデータ型HACK) + *Bridge // ブリッジ + Info *struct { + Switches []struct { // 接続スイッチリスト + *Switch // スイッチ + ID json.Number `json:",omitempty"` // HACK + } + } + } `json:",omitempty"` + + //HybridConnection //REMARK: !!ハイブリッド接続 not support!! +} + +// SwitchSubnet スイッチサブネット +type SwitchSubnet struct { + *Subnet + IPAddresses struct { // IPアドレス範囲 + Min string `json:",omitempty"` // IPアドレス開始 + Max string `json:",omitempty"` // IPアドレス終了 + } +} + +// GetDefaultIPAddressesForVPCRouter VPCルーター接続用にサブネットからIPアドレスを3つ取得 +func (s *Switch) GetDefaultIPAddressesForVPCRouter() (string, string, string, error) { + + if s.Subnets == nil || len(s.Subnets) < 1 { + return "", "", "", fmt.Errorf("switch[%d].Subnets is nil", s.ID) + } + + baseAddress := net.ParseIP(s.Subnets[0].IPAddresses.Min).To4() + address1 := net.IPv4(baseAddress[0], baseAddress[1], baseAddress[2], baseAddress[3]+1) + address2 := net.IPv4(baseAddress[0], baseAddress[1], baseAddress[2], baseAddress[3]+2) + + return baseAddress.String(), address1.String(), address2.String(), nil +} + +// GetIPAddressList IPアドレス範囲内の全てのIPアドレスを取得 +func (s *Switch) GetIPAddressList() ([]string, error) { + if s.Subnets == nil || len(s.Subnets) < 1 { + return nil, fmt.Errorf("switch[%d].Subnets is nil", s.ID) + } + + //さくらのクラウドの仕様上/24までしか割り当てできないためこのロジックでOK + baseIP := net.ParseIP(s.Subnets[0].IPAddresses.Min).To4() + min := baseIP[3] + max := net.ParseIP(s.Subnets[0].IPAddresses.Max).To4()[3] + + var i byte + ret := []string{} + for (min + i) <= max { //境界含む + ip := net.IPv4(baseIP[0], baseIP[1], baseIP[2], baseIP[3]+i) + ret = append(ret, ip.String()) + i++ + } + + return ret, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go new file mode 100644 index 000000000..20bee798c --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router.go @@ -0,0 +1,238 @@ +package sacloud + +import ( + "fmt" + "net" +) + +// VPCRouter VPCルーター +type VPCRouter struct { + *Appliance // アプライアンス共通属性 + + Remark *VPCRouterRemark `json:",omitempty"` // リマーク + Settings *VPCRouterSettings `json:",omitempty"` // VPCルーター設定リスト +} + +// VPCRouterRemark リマーク +type VPCRouterRemark struct { + *ApplianceRemarkBase + // TODO Zone + //Zone *Resource +} + +// VPCRouterSettings VPCルーター設定リスト +type VPCRouterSettings struct { + Router *VPCRouterSetting `json:",omitempty"` // VPCルーター設定 +} + +// CreateNewVPCRouter VPCルーター作成 +func CreateNewVPCRouter() *VPCRouter { + return &VPCRouter{ + Appliance: &Appliance{ + Class: "vpcrouter", + propPlanID: propPlanID{Plan: &Resource{}}, + }, + Remark: &VPCRouterRemark{ + ApplianceRemarkBase: &ApplianceRemarkBase{ + Servers: []interface{}{""}, + Switch: &ApplianceRemarkSwitch{}, + }, + }, + Settings: &VPCRouterSettings{ + Router: &VPCRouterSetting{}, + }, + } +} + +// InitVPCRouterSetting VPCルーター設定初期化 +func (v *VPCRouter) InitVPCRouterSetting() { + settings := &VPCRouterSettings{ + Router: &VPCRouterSetting{}, + } + + if v.Settings != nil && v.Settings.Router != nil && v.Settings.Router.Interfaces != nil { + settings.Router.Interfaces = v.Settings.Router.Interfaces + } + if v.Settings != nil && v.Settings.Router != nil && v.Settings.Router.VRID != nil { + settings.Router.VRID = v.Settings.Router.VRID + } + + v.Settings = settings +} + +// IsStandardPlan スタンダードプランか判定 +func (v *VPCRouter) IsStandardPlan() bool { + return v.Plan.ID == 1 +} + +// IsPremiumPlan プレミアムプランか判定 +func (v *VPCRouter) IsPremiumPlan() bool { + return v.Plan.ID == 2 +} + +// IsHighSpecPlan ハイスペックプランか判定 +func (v *VPCRouter) IsHighSpecPlan() bool { + return v.Plan.ID == 3 +} + +// SetStandardPlan スタンダードプランへ設定 +func (v *VPCRouter) SetStandardPlan() { + v.Plan.SetID(1) + v.Remark.Switch = &ApplianceRemarkSwitch{ + // Scope + propScope: propScope{Scope: "shared"}, + } + v.Settings = nil +} + +// SetPremiumPlan プレミアムプランへ設定 +func (v *VPCRouter) SetPremiumPlan(switchID string, virtualIPAddress string, ipAddress1 string, ipAddress2 string, vrid int, ipAliases []string) { + v.Plan.SetID(2) + v.setPremiumServices(switchID, virtualIPAddress, ipAddress1, ipAddress2, vrid, ipAliases) +} + +// SetHighSpecPlan ハイスペックプランへ設定 +func (v *VPCRouter) SetHighSpecPlan(switchID string, virtualIPAddress string, ipAddress1 string, ipAddress2 string, vrid int, ipAliases []string) { + v.Plan.SetID(3) + v.setPremiumServices(switchID, virtualIPAddress, ipAddress1, ipAddress2, vrid, ipAliases) +} + +func (v *VPCRouter) setPremiumServices(switchID string, virtualIPAddress string, ipAddress1 string, ipAddress2 string, vrid int, ipAliases []string) { + v.Remark.Switch = &ApplianceRemarkSwitch{ + ID: switchID, + } + v.Remark.Servers = []interface{}{ + map[string]string{"IPAddress": ipAddress1}, + map[string]string{"IPAddress": ipAddress2}, + } + + v.Settings = &VPCRouterSettings{ + Router: &VPCRouterSetting{ + Interfaces: []*VPCRouterInterface{ + { + IPAddress: []string{ + ipAddress1, + ipAddress2, + }, + VirtualIPAddress: virtualIPAddress, + IPAliases: ipAliases, + }, + }, + VRID: &vrid, + }, + } + +} + +// HasSetting VPCルータ設定を保持しているか +func (v *VPCRouter) HasSetting() bool { + return v.Settings != nil && v.Settings.Router != nil +} + +// HasInterfaces NIC設定を保持しているか +func (v *VPCRouter) HasInterfaces() bool { + return v.HasSetting() && v.Settings.Router.HasInterfaces() +} + +// HasStaticNAT スタティックNAT設定を保持しているか +func (v *VPCRouter) HasStaticNAT() bool { + return v.HasSetting() && v.Settings.Router.HasStaticNAT() +} + +// HasPortForwarding ポートフォワーディング設定を保持しているか +func (v *VPCRouter) HasPortForwarding() bool { + return v.HasSetting() && v.Settings.Router.HasPortForwarding() +} + +// HasFirewall ファイアウォール設定を保持しているか +func (v *VPCRouter) HasFirewall() bool { + return v.HasSetting() && v.Settings.Router.HasFirewall() +} + +// HasDHCPServer DHCPサーバー設定を保持しているか +func (v *VPCRouter) HasDHCPServer() bool { + return v.HasSetting() && v.Settings.Router.HasDHCPServer() +} + +// HasDHCPStaticMapping DHCPスタティックマッピング設定を保持しているか +func (v *VPCRouter) HasDHCPStaticMapping() bool { + return v.HasSetting() && v.Settings.Router.HasDHCPStaticMapping() +} + +// HasL2TPIPsecServer L2TP/IPSecサーバを保持しているか +func (v *VPCRouter) HasL2TPIPsecServer() bool { + return v.HasSetting() && v.Settings.Router.HasL2TPIPsecServer() +} + +// HasPPTPServer PPTPサーバを保持しているか +func (v *VPCRouter) HasPPTPServer() bool { + return v.HasSetting() && v.Settings.Router.HasPPTPServer() +} + +// HasRemoteAccessUsers リモートアクセスユーザー設定を保持しているか +func (v *VPCRouter) HasRemoteAccessUsers() bool { + return v.HasSetting() && v.Settings.Router.HasRemoteAccessUsers() +} + +// HasSiteToSiteIPsecVPN サイト間VPN設定を保持しているか +func (v *VPCRouter) HasSiteToSiteIPsecVPN() bool { + return v.HasSetting() && v.Settings.Router.HasSiteToSiteIPsecVPN() +} + +// HasStaticRoutes スタティックルートを保持しているか +func (v *VPCRouter) HasStaticRoutes() bool { + return v.HasSetting() && v.Settings.Router.HasStaticRoutes() +} + +// RealIPAddress プランに応じて外部向けIPアドレスを返す +// +// Standard: IPAddress1 +// Other: VirtualIPAddress +func (v *VPCRouter) RealIPAddress(index int) (string, int) { + if !v.HasInterfaces() { + return "", -1 + } + for i, nic := range v.Settings.Router.Interfaces { + if i == index { + if index > 0 && nic == nil { + return "", -1 + } + + if index == 0 && v.IsStandardPlan() { + return v.Interfaces[0].IPAddress, v.Interfaces[0].Switch.Subnet.NetworkMaskLen + } + + nwMask := nic.NetworkMaskLen + if index == 0 { + nwMask = v.Interfaces[0].Switch.Subnet.NetworkMaskLen + } + + if v.IsStandardPlan() { + return nic.IPAddress[0], nwMask + } + return nic.VirtualIPAddress, nwMask + } + } + return "", -1 +} + +// FindBelongsInterface 指定のIPアドレスが所属するIPレンジを持つインターフェースを取得 +func (v *VPCRouter) FindBelongsInterface(ip net.IP) (int, *VPCRouterInterface) { + if !v.HasInterfaces() { + return -1, nil + } + + for i, nic := range v.Settings.Router.Interfaces { + nicIP, maskLen := v.RealIPAddress(i) + if nicIP != "" { + _, ipv4Net, err := net.ParseCIDR(fmt.Sprintf("%s/%d", nicIP, maskLen)) + if err != nil { + return -1, nil + } + if ipv4Net.Contains(ip) { + return i, nic + } + } + } + return -1, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_setting.go b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_setting.go new file mode 100644 index 000000000..a9e85ca57 --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_setting.go @@ -0,0 +1,1158 @@ +package sacloud + +import ( + "bytes" + "fmt" + "net" + "reflect" + "strconv" + "strings" +) + +// VPCRouterSetting VPCルーター設定 +type VPCRouterSetting struct { + Interfaces []*VPCRouterInterface `json:",omitempty"` // NIC設定 + StaticNAT *VPCRouterStaticNAT `json:",omitempty"` // スタティックNAT設定 + PortForwarding *VPCRouterPortForwarding `json:",omitempty"` // ポートフォワーディング設定 + Firewall *VPCRouterFirewall `json:",omitempty"` // ファイアウォール設定 + DHCPServer *VPCRouterDHCPServer `json:",omitempty"` // DHCPサーバー設定 + DHCPStaticMapping *VPCRouterDHCPStaticMapping `json:",omitempty"` // DHCPスタティックマッピング設定 + L2TPIPsecServer *VPCRouterL2TPIPsecServer `json:",omitempty"` // L2TP/IPSecサーバー設定 + PPTPServer *VPCRouterPPTPServer `json:",omitempty"` // PPTPサーバー設定 + RemoteAccessUsers *VPCRouterRemoteAccessUsers `json:",omitempty"` // リモートアクセスユーザー設定 + SiteToSiteIPsecVPN *VPCRouterSiteToSiteIPsecVPN `json:",omitempty"` // サイト間VPN設定 + StaticRoutes *VPCRouterStaticRoutes `json:",omitempty"` // スタティックルート設定 + VRID *int `json:",omitempty"` // VRID + SyslogHost string `json:",omitempty"` // syslog転送先ホスト + +} + +// VPCRouterInterface NIC設定 +type VPCRouterInterface struct { + IPAddress []string `json:",omitempty"` // IPアドレスリスト + NetworkMaskLen int `json:",omitempty"` // ネットワークマスク長 + VirtualIPAddress string `json:",omitempty"` // 仮想IPアドレス + IPAliases []string `json:",omitempty"` // IPエイリアス +} + +// AddInterface NIC追加 +func (s *VPCRouterSetting) AddInterface(vip string, ipaddress []string, maskLen int) { + if s.Interfaces == nil { + s.Interfaces = []*VPCRouterInterface{nil} + } + s.Interfaces = append(s.Interfaces, &VPCRouterInterface{ + VirtualIPAddress: vip, + IPAddress: ipaddress, + NetworkMaskLen: maskLen, + }) +} + +// HasInterfaces NIC設定を保持しているか +func (s *VPCRouterSetting) HasInterfaces() bool { + return s.Interfaces != nil && len(s.Interfaces) > 0 +} + +// VPCRouterStaticNAT スタティックNAT設定 +type VPCRouterStaticNAT struct { + Config []*VPCRouterStaticNATConfig `json:",omitempty"` // スタティックNAT設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterStaticNATConfig スタティックNAT設定 +type VPCRouterStaticNATConfig struct { + GlobalAddress string `json:",omitempty"` // グローバルIPアドレス + PrivateAddress string `json:",omitempty"` // プライベートIPアドレス + Description string `json:",omitempty"` // 説明 +} + +// HasStaticNAT スタティックNAT設定を保持しているか +func (s *VPCRouterSetting) HasStaticNAT() bool { + return s.StaticNAT != nil && len(s.StaticNAT.Config) > 0 +} + +// AddStaticNAT スタティックNAT設定 追加 +func (s *VPCRouterSetting) AddStaticNAT(globalAddress string, privateAddress string, description string) (int, *VPCRouterStaticNATConfig) { + if s.StaticNAT == nil { + s.StaticNAT = &VPCRouterStaticNAT{ + Enabled: "True", + } + } + + if s.StaticNAT.Config == nil { + s.StaticNAT.Config = []*VPCRouterStaticNATConfig{} + } + + c := &VPCRouterStaticNATConfig{ + GlobalAddress: globalAddress, + PrivateAddress: privateAddress, + Description: description, + } + + s.StaticNAT.Config = append(s.StaticNAT.Config, c) + return len(s.StaticNAT.Config) - 1, c +} + +// RemoveStaticNAT スタティックNAT設定 削除 +func (s *VPCRouterSetting) RemoveStaticNAT(globalAddress string, privateAddress string) { + if s.StaticNAT == nil { + return + } + + if s.StaticNAT.Config == nil { + s.StaticNAT.Enabled = "False" + return + } + + dest := []*VPCRouterStaticNATConfig{} + for _, c := range s.StaticNAT.Config { + if c.GlobalAddress != globalAddress || c.PrivateAddress != privateAddress { + dest = append(dest, c) + } + } + s.StaticNAT.Config = dest + if len(s.StaticNAT.Config) == 0 { + s.StaticNAT.Enabled = "False" + s.StaticNAT.Config = nil + return + } + s.StaticNAT.Enabled = "True" +} + +// RemoveStaticNATAt スタティックNAT設定 削除 +func (s *VPCRouterSetting) RemoveStaticNATAt(index int) { + if s.StaticNAT == nil { + return + } + + if s.StaticNAT.Config == nil || len(s.StaticNAT.Config) == 0 { + s.StaticNAT.Enabled = "False" + return + } + + if index < len(s.StaticNAT.Config) { + c := s.StaticNAT.Config[index] + s.RemoveStaticNAT(c.GlobalAddress, c.PrivateAddress) + } +} + +// FindStaticNAT スタティックNAT設定検索 +func (s *VPCRouterSetting) FindStaticNAT(globalAddress string, privateAddress string) (int, *VPCRouterStaticNATConfig) { + for i, c := range s.StaticNAT.Config { + if c.GlobalAddress == globalAddress && c.PrivateAddress == privateAddress { + return i, c + } + } + return -1, nil +} + +// HasPortForwarding ポートフォワーディング設定を保持しているか +func (s *VPCRouterSetting) HasPortForwarding() bool { + return s.PortForwarding != nil && len(s.PortForwarding.Config) > 0 +} + +// VPCRouterPortForwarding ポートフォワーディング設定 +type VPCRouterPortForwarding struct { + Config []*VPCRouterPortForwardingConfig `json:",omitempty"` // ポートフォワーディング設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterPortForwardingConfig ポートフォワーディング設定 +type VPCRouterPortForwardingConfig struct { + Protocol string `json:",omitempty"` // プロトコル(tcp/udp) + GlobalPort string `json:",omitempty"` // グローバル側ポート + PrivateAddress string `json:",omitempty"` // プライベートIPアドレス + PrivatePort string `json:",omitempty"` // プライベート側ポート + Description string `json:",omitempty"` // 説明 +} + +// AddPortForwarding ポートフォワーディング 追加 +func (s *VPCRouterSetting) AddPortForwarding(protocol string, globalPort string, privateAddress string, + privatePort string, description string) (int, *VPCRouterPortForwardingConfig) { + if s.PortForwarding == nil { + s.PortForwarding = &VPCRouterPortForwarding{ + Enabled: "True", + } + } + + if s.PortForwarding.Config == nil { + s.PortForwarding.Config = []*VPCRouterPortForwardingConfig{} + } + + c := &VPCRouterPortForwardingConfig{ + Protocol: protocol, + GlobalPort: globalPort, + PrivateAddress: privateAddress, + PrivatePort: privatePort, + Description: description, + } + + s.PortForwarding.Config = append(s.PortForwarding.Config, c) + + return len(s.PortForwarding.Config) - 1, c +} + +// RemovePortForwarding ポートフォワーディング 削除 +func (s *VPCRouterSetting) RemovePortForwarding(protocol string, globalPort string, privateAddress string, privatePort string) { + if s.PortForwarding == nil { + return + } + + if s.PortForwarding.Config == nil { + s.PortForwarding.Enabled = "False" + return + } + + dest := []*VPCRouterPortForwardingConfig{} + for _, c := range s.PortForwarding.Config { + if c.Protocol != protocol || c.GlobalPort != globalPort || + c.PrivateAddress != privateAddress || c.PrivatePort != privatePort { + dest = append(dest, c) + } + } + s.PortForwarding.Config = dest + if len(s.PortForwarding.Config) == 0 { + s.PortForwarding.Enabled = "False" + s.PortForwarding.Config = nil + return + } + s.PortForwarding.Enabled = "True" +} + +// RemovePortForwardingAt ポートフォワーディング 削除 +func (s *VPCRouterSetting) RemovePortForwardingAt(index int) { + if s.PortForwarding == nil { + return + } + + if s.PortForwarding.Config == nil || len(s.PortForwarding.Config) == 0 { + s.PortForwarding.Enabled = "False" + return + } + + if index < len(s.PortForwarding.Config) { + c := s.PortForwarding.Config[index] + s.RemovePortForwarding(c.Protocol, c.GlobalPort, c.PrivateAddress, c.PrivatePort) + } +} + +// FindPortForwarding ポートフォワーディング検索 +func (s *VPCRouterSetting) FindPortForwarding(protocol string, globalPort string, + privateAddress string, privatePort string) (int, *VPCRouterPortForwardingConfig) { + + for i, c := range s.PortForwarding.Config { + if c.Protocol == protocol && c.GlobalPort == globalPort && + c.PrivateAddress == privateAddress && c.PrivatePort == privatePort { + return i, c + } + } + return -1, nil +} + +// HasFirewall ファイアウォール設定を保持しているか +func (s *VPCRouterSetting) HasFirewall() bool { + return s.Firewall != nil && len(s.Firewall.Config) > 0 +} + +// VPCRouterFirewall ファイアウォール設定 +type VPCRouterFirewall struct { + Config []*VPCRouterFirewallSetting // ファイアウォール設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterFirewallSetting ファイアウォール設定 +type VPCRouterFirewallSetting struct { + Receive []*VPCRouterFirewallRule // 受信ルール + Send []*VPCRouterFirewallRule // 送信ルール +} + +// VPCRouterFirewallRule ファイアウォール ルール +type VPCRouterFirewallRule struct { + Action string `json:",omitempty"` // 許可/拒否 + Protocol string `json:",omitempty"` // プロトコル + SourceNetwork string `json:",omitempty"` // 送信元ネットワーク + SourcePort string `json:",omitempty"` // 送信元ポート + DestinationNetwork string `json:",omitempty"` // 宛先ネットワーク + DestinationPort string `json:",omitempty"` // 宛先ポート + Logging string `json:",omitempty"` // ログ記録 + Description string `json:",omitempty"` // 説明 +} + +// VPCRouterMaxInterfaceCount VPCルータでの最大NIC数(グローバル含む) +const VPCRouterMaxInterfaceCount = 8 + +func (s *VPCRouterSetting) addFirewallRule(ifIndex int, direction string, rule *VPCRouterFirewallRule) int { + if ifIndex < 0 { + ifIndex = 0 + } + if s.Firewall == nil { + s.Firewall = &VPCRouterFirewall{ + Enabled: "True", + } + } + if s.Firewall.Config == nil { + s.Firewall.Config = []*VPCRouterFirewallSetting{} + } + for len(s.Firewall.Config) < VPCRouterMaxInterfaceCount { + s.Firewall.Config = append(s.Firewall.Config, &VPCRouterFirewallSetting{ + Receive: []*VPCRouterFirewallRule{}, + Send: []*VPCRouterFirewallRule{}, + }) + } + + switch direction { + case "send": + s.Firewall.Config[ifIndex].Send = append(s.Firewall.Config[ifIndex].Send, rule) + return len(s.Firewall.Config[ifIndex].Send) - 1 + case "receive": + s.Firewall.Config[ifIndex].Receive = append(s.Firewall.Config[ifIndex].Receive, rule) + return len(s.Firewall.Config[ifIndex].Receive) - 1 + } + return -1 +} + +func (s *VPCRouterSetting) removeFirewallRule(ifIndex int, direction string, rule *VPCRouterFirewallRule) { + + if s.Firewall == nil || !(len(s.Firewall.Config) < ifIndex) { + return + } + if ifIndex < 0 { + ifIndex = 0 + } + switch direction { + case "send": + dest := []*VPCRouterFirewallRule{} + for _, c := range s.Firewall.Config[ifIndex].Send { + if !s.isSameRule(rule, c) { + dest = append(dest, c) + } + } + s.Firewall.Config[ifIndex].Send = dest + case "receive": + dest := []*VPCRouterFirewallRule{} + for _, c := range s.Firewall.Config[ifIndex].Receive { + if !s.isSameRule(rule, c) { + dest = append(dest, c) + } + } + s.Firewall.Config[ifIndex].Receive = dest + } + + //if len(s.Firewall.Config) == 0 { + // s.Firewall.Enabled = "False" + // return + //} + // + //if len(s.Firewall.Config[0].Send) == 0 && len(s.Firewall.Config[0].Send) == 0 { + // s.Firewall.Enabled = "False" + // return + //} + + s.PortForwarding.Enabled = "True" + +} + +func (s *VPCRouterSetting) removeFirewallRuleAt(ifIndex int, direction string, index int) { + if s.Firewall == nil || !(len(s.Firewall.Config) < ifIndex) { + return + } + if ifIndex < 0 { + ifIndex = 0 + } + + switch direction { + case "send": + dest := []*VPCRouterFirewallRule{} + for i, c := range s.Firewall.Config[ifIndex].Send { + if i != index { + dest = append(dest, c) + } + } + s.Firewall.Config[0].Send = dest + case "receive": + dest := []*VPCRouterFirewallRule{} + for i, c := range s.Firewall.Config[ifIndex].Receive { + if i != index { + dest = append(dest, c) + } + } + s.Firewall.Config[0].Receive = dest + } + + //if len(s.Firewall.Config) == 0 { + // s.Firewall.Enabled = "False" + // return + //} + // + //if len(s.Firewall.Config[0].Send) == 0 && len(s.Firewall.Config[0].Send) == 0 { + // s.Firewall.Enabled = "False" + // return + //} + + s.PortForwarding.Enabled = "True" + +} + +func (s *VPCRouterSetting) findFirewallRule(ifIndex int, direction string, rule *VPCRouterFirewallRule) (int, *VPCRouterFirewallRule) { + if s.Firewall == nil || !(len(s.Firewall.Config) < ifIndex) { + return -1, nil + } + if ifIndex < 0 { + ifIndex = 0 + } + + switch direction { + case "send": + for i, c := range s.Firewall.Config[ifIndex].Send { + if s.isSameRule(rule, c) { + return i, c + } + } + case "receive": + for i, c := range s.Firewall.Config[ifIndex].Receive { + if s.isSameRule(rule, c) { + return i, c + } + } + } + + return -1, nil + +} + +func (s *VPCRouterSetting) isSameRule(r1 *VPCRouterFirewallRule, r2 *VPCRouterFirewallRule) bool { + return r1.Action == r2.Action && + r1.Protocol == r2.Protocol && + r1.SourceNetwork == r2.SourceNetwork && + r1.SourcePort == r2.SourcePort && + r1.DestinationNetwork == r2.DestinationNetwork && + r1.DestinationPort == r2.DestinationPort +} + +// AddFirewallRuleSend 送信ルール 追加 +func (s *VPCRouterSetting) AddFirewallRuleSend(ifIndex int, isAllow bool, protocol string, + sourceNetwork string, sourcePort string, destNetwork string, destPort string, + logging bool, description string) (int, *VPCRouterFirewallRule) { + action := "deny" + if isAllow { + action = "allow" + } + strLogging := "False" + if logging { + strLogging = "True" + } + + rule := &VPCRouterFirewallRule{ + Action: action, + Protocol: protocol, + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationNetwork: destNetwork, + DestinationPort: destPort, + Logging: strLogging, + Description: description, + } + + i := s.addFirewallRule(ifIndex, "send", rule) + return i, rule +} + +// RemoveFirewallRuleSend 送信ルール 削除 +func (s *VPCRouterSetting) RemoveFirewallRuleSend(ifIndex int, isAllow bool, protocol string, sourceNetwork string, sourcePort string, destNetwork string, destPort string) { + action := "deny" + if isAllow { + action = "allow" + } + + rule := &VPCRouterFirewallRule{ + Action: action, + Protocol: protocol, + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationNetwork: destNetwork, + DestinationPort: destPort, + } + + s.removeFirewallRule(ifIndex, "send", rule) +} + +// RemoveFirewallRuleSendAt 指定位置の送信ルールを削除 +func (s *VPCRouterSetting) RemoveFirewallRuleSendAt(ifIndex int, index int) { + s.removeFirewallRuleAt(ifIndex, "send", index) +} + +// FindFirewallRuleSend 送信ルール 検索 +func (s *VPCRouterSetting) FindFirewallRuleSend(ifIndex int, isAllow bool, protocol string, + sourceNetwork string, sourcePort string, destNetwork string, destPort string) (int, *VPCRouterFirewallRule) { + action := "deny" + if isAllow { + action = "allow" + } + rule := &VPCRouterFirewallRule{ + Action: action, + Protocol: protocol, + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationNetwork: destNetwork, + DestinationPort: destPort, + } + + return s.findFirewallRule(ifIndex, "send", rule) +} + +// AddFirewallRuleReceive 受信ルール 追加 +func (s *VPCRouterSetting) AddFirewallRuleReceive(ifIndex int, isAllow bool, protocol string, + sourceNetwork string, sourcePort string, destNetwork string, destPort string, logging bool, description string) (int, *VPCRouterFirewallRule) { + action := "deny" + if isAllow { + action = "allow" + } + strLogging := "False" + if logging { + strLogging = "True" + } + rule := &VPCRouterFirewallRule{ + Action: action, + Protocol: protocol, + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationNetwork: destNetwork, + DestinationPort: destPort, + Logging: strLogging, + Description: description, + } + + i := s.addFirewallRule(ifIndex, "receive", rule) + return i, rule +} + +// RemoveFirewallRuleReceiveAt 指定位置の受信ルールを削除 +func (s *VPCRouterSetting) RemoveFirewallRuleReceiveAt(ifIndex int, index int) { + s.removeFirewallRuleAt(ifIndex, "receive", index) +} + +// RemoveFirewallRuleReceive 受信ルール 削除 +func (s *VPCRouterSetting) RemoveFirewallRuleReceive(ifIndex int, isAllow bool, protocol string, sourceNetwork string, sourcePort string, destNetwork string, destPort string) { + action := "deny" + if isAllow { + action = "allow" + } + rule := &VPCRouterFirewallRule{ + Action: action, + Protocol: protocol, + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationNetwork: destNetwork, + DestinationPort: destPort, + } + + s.removeFirewallRule(ifIndex, "receive", rule) +} + +// FindFirewallRuleReceive 受信ルール 検索 +func (s *VPCRouterSetting) FindFirewallRuleReceive(ifIndex int, isAllow bool, protocol string, + sourceNetwork string, sourcePort string, destNetwork string, destPort string) (int, *VPCRouterFirewallRule) { + action := "deny" + if isAllow { + action = "allow" + } + rule := &VPCRouterFirewallRule{ + Action: action, + Protocol: protocol, + SourceNetwork: sourceNetwork, + SourcePort: sourcePort, + DestinationNetwork: destNetwork, + DestinationPort: destPort, + } + + return s.findFirewallRule(ifIndex, "receive", rule) +} + +// HasDHCPServer DHCPサーバー設定を保持しているか +func (s *VPCRouterSetting) HasDHCPServer() bool { + return s.DHCPServer != nil && len(s.DHCPServer.Config) > 0 +} + +// VPCRouterDHCPServer DHCPサーバー設定 +type VPCRouterDHCPServer struct { + Config []*VPCRouterDHCPServerConfig `json:",omitempty"` // DHCPサーバー設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterDHCPServerConfig DHCPサーバー設定 +type VPCRouterDHCPServerConfig struct { + Interface string `json:",omitempty"` // 対象NIC + RangeStart string `json:",omitempty"` // 割り当て範囲 開始アドレス + RangeStop string `json:",omitempty"` // 割り当て範囲 終了アドレス + DNSServers []string // 配布するDNSサーバIPアドレスのリスト +} + +// InterfaceIndex 対象NICのインデックス +func (c *VPCRouterDHCPServerConfig) InterfaceIndex() int { + strIndex := strings.Replace(c.Interface, "eth", "", -1) + index, err := strconv.Atoi(strIndex) + if err != nil { + return -1 + } + return index +} + +// AddDHCPServer DHCPサーバー設定追加 +func (s *VPCRouterSetting) AddDHCPServer(nicIndex int, rangeStart string, rangeStop string, dnsServers ...string) (int, *VPCRouterDHCPServerConfig) { + if s.DHCPServer == nil { + s.DHCPServer = &VPCRouterDHCPServer{ + Enabled: "True", + } + } + if s.DHCPServer.Config == nil { + s.DHCPServer.Config = []*VPCRouterDHCPServerConfig{} + } + + nic := fmt.Sprintf("eth%d", nicIndex) + c := &VPCRouterDHCPServerConfig{ + Interface: nic, + RangeStart: rangeStart, + RangeStop: rangeStop, + DNSServers: dnsServers, + } + s.DHCPServer.Config = append(s.DHCPServer.Config, c) + + return len(s.DHCPServer.Config) - 1, c +} + +// RemoveDHCPServer DHCPサーバー設定削除 +func (s *VPCRouterSetting) RemoveDHCPServer(nicIndex int) { + if s.DHCPServer == nil { + return + } + + if s.DHCPServer.Config == nil { + s.DHCPServer.Enabled = "False" + return + } + + dest := []*VPCRouterDHCPServerConfig{} + for _, c := range s.DHCPServer.Config { + if c.Interface != fmt.Sprintf("eth%d", nicIndex) { + dest = append(dest, c) + } + } + s.DHCPServer.Config = dest + + if len(s.DHCPServer.Config) == 0 { + s.DHCPServer.Enabled = "False" + s.DHCPServer.Config = nil + return + } + s.DHCPServer.Enabled = "True" + +} + +// RemoveDHCPServerAt DHCPサーバー設定削除 +func (s *VPCRouterSetting) RemoveDHCPServerAt(nicIndex int) { + s.RemoveDHCPServer(nicIndex) +} + +// FindDHCPServer DHCPサーバー設定 検索 +func (s *VPCRouterSetting) FindDHCPServer(nicIndex int) (int, *VPCRouterDHCPServerConfig) { + for i, c := range s.DHCPServer.Config { + if c.InterfaceIndex() == nicIndex { + return i, c + } + } + return -1, nil +} + +// FindDHCPServerAt DHCPサーバー設定 検索 +func (s *VPCRouterSetting) FindDHCPServerAt(nicIndex int) (int, *VPCRouterDHCPServerConfig) { + return s.FindDHCPServer(nicIndex) +} + +// FindBelongsDHCPServer 指定のIPアドレスが所属するIPレンジを持つをDHCPサーバを検索 +func (s *VPCRouterSetting) FindBelongsDHCPServer(ip net.IP) (int, *VPCRouterDHCPServerConfig) { + target := ip.To4() + for i, c := range s.DHCPServer.Config { + start := net.ParseIP(c.RangeStart).To4() + end := net.ParseIP(c.RangeStop).To4() + + if bytes.Compare(target, start) >= 0 && bytes.Compare(target, end) <= 0 { + return i, c + } + } + return -1, nil +} + +// HasDHCPStaticMapping DHCPスタティックマッピング設定を保持しているか +func (s *VPCRouterSetting) HasDHCPStaticMapping() bool { + return s.DHCPStaticMapping != nil && len(s.DHCPStaticMapping.Config) > 0 +} + +// VPCRouterDHCPStaticMapping DHCPスタティックマッピング設定 +type VPCRouterDHCPStaticMapping struct { + Config []*VPCRouterDHCPStaticMappingConfig `json:",omitempty"` // DHCPスタティックマッピング設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterDHCPStaticMappingConfig DHCPスタティックマッピング設定 +type VPCRouterDHCPStaticMappingConfig struct { + IPAddress string `json:",omitempty"` // 割り当てIPアドレス + MACAddress string `json:",omitempty"` // ソースMACアドレス +} + +// AddDHCPStaticMapping DHCPスタティックマッピング設定追加 +func (s *VPCRouterSetting) AddDHCPStaticMapping(ipAddress string, macAddress string) (int, *VPCRouterDHCPStaticMappingConfig) { + if s.DHCPStaticMapping == nil { + s.DHCPStaticMapping = &VPCRouterDHCPStaticMapping{ + Enabled: "True", + } + } + if s.DHCPStaticMapping.Config == nil { + s.DHCPStaticMapping.Config = []*VPCRouterDHCPStaticMappingConfig{} + } + + c := &VPCRouterDHCPStaticMappingConfig{ + IPAddress: ipAddress, + MACAddress: macAddress, + } + s.DHCPStaticMapping.Config = append(s.DHCPStaticMapping.Config, c) + return len(s.DHCPStaticMapping.Config) - 1, c +} + +// RemoveDHCPStaticMapping DHCPスタティックマッピング設定 削除 +func (s *VPCRouterSetting) RemoveDHCPStaticMapping(ipAddress string, macAddress string) { + if s.DHCPStaticMapping == nil { + return + } + + if s.DHCPStaticMapping.Config == nil { + s.DHCPStaticMapping.Enabled = "False" + return + } + + dest := []*VPCRouterDHCPStaticMappingConfig{} + for _, c := range s.DHCPStaticMapping.Config { + if c.IPAddress != ipAddress || c.MACAddress != macAddress { + dest = append(dest, c) + } + } + s.DHCPStaticMapping.Config = dest + + if len(s.DHCPStaticMapping.Config) == 0 { + s.DHCPStaticMapping.Enabled = "False" + s.DHCPStaticMapping.Config = nil + return + } + s.DHCPStaticMapping.Enabled = "True" + +} + +// RemoveDHCPStaticMappingAt DHCPスタティックマッピング設定 削除 +func (s *VPCRouterSetting) RemoveDHCPStaticMappingAt(index int) { + if s.DHCPStaticMapping == nil { + return + } + + if s.DHCPStaticMapping.Config == nil || len(s.DHCPStaticMapping.Config) == 0 { + s.DHCPStaticMapping.Enabled = "False" + return + } + + if index < len(s.DHCPStaticMapping.Config) { + c := s.DHCPStaticMapping.Config[index] + s.RemoveDHCPStaticMapping(c.IPAddress, c.MACAddress) + } +} + +// FindDHCPStaticMapping DHCPスタティックマッピング設定 検索 +func (s *VPCRouterSetting) FindDHCPStaticMapping(ipAddress string, macAddress string) (int, *VPCRouterDHCPStaticMappingConfig) { + for i, c := range s.DHCPStaticMapping.Config { + if c.IPAddress == ipAddress && c.MACAddress == macAddress { + return i, c + } + } + return -1, nil +} + +// HasL2TPIPsecServer L2TP/IPSecサーバを保持しているか +func (s *VPCRouterSetting) HasL2TPIPsecServer() bool { + return s.L2TPIPsecServer != nil && s.L2TPIPsecServer.Config != nil +} + +// VPCRouterL2TPIPsecServer L2TP/IPSecサーバー設定 +type VPCRouterL2TPIPsecServer struct { + Config *VPCRouterL2TPIPsecServerConfig `json:",omitempty"` // L2TP/IPSecサーバー設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterL2TPIPsecServerConfig L2TP/IPSecサーバー設定 +type VPCRouterL2TPIPsecServerConfig struct { + PreSharedSecret string `json:",omitempty"` // 事前共有シークレット + RangeStart string `json:",omitempty"` // 割り当て範囲 開始IPアドレス + RangeStop string `json:",omitempty"` // 割り当て範囲 終了IPアドレス +} + +// EnableL2TPIPsecServer L2TP/IPSecサーバー設定 有効化 +func (s *VPCRouterSetting) EnableL2TPIPsecServer(preSharedSecret string, rangeStart string, rangeStop string) { + if s.L2TPIPsecServer == nil { + s.L2TPIPsecServer = &VPCRouterL2TPIPsecServer{ + Enabled: "True", + } + } + s.L2TPIPsecServer.Config = &VPCRouterL2TPIPsecServerConfig{ + PreSharedSecret: preSharedSecret, + RangeStart: rangeStart, + RangeStop: rangeStop, + } +} + +// DisableL2TPIPsecServer L2TP/IPSecサーバー設定 無効化 +func (s *VPCRouterSetting) DisableL2TPIPsecServer() { + if s.L2TPIPsecServer == nil { + s.L2TPIPsecServer = &VPCRouterL2TPIPsecServer{} + } + s.L2TPIPsecServer.Enabled = "False" + s.L2TPIPsecServer.Config = nil +} + +// HasPPTPServer PPTPサーバを保持しているか +func (s *VPCRouterSetting) HasPPTPServer() bool { + return s.PPTPServer != nil && s.PPTPServer.Config != nil +} + +// VPCRouterPPTPServer PPTPサーバー設定 +type VPCRouterPPTPServer struct { + Config *VPCRouterPPTPServerConfig `json:",omitempty"` // PPTPサーバー設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterPPTPServerConfig PPTPサーバー設定 +type VPCRouterPPTPServerConfig struct { + RangeStart string `json:",omitempty"` // 割り当て範囲 開始IPアドレス + RangeStop string `json:",omitempty"` // 割り当て範囲 終了IPアドレス +} + +// EnablePPTPServer PPTPサーバー設定 有効化 +func (s *VPCRouterSetting) EnablePPTPServer(rangeStart string, rangeStop string) { + if s.PPTPServer == nil { + s.PPTPServer = &VPCRouterPPTPServer{ + Enabled: "True", + } + } + s.PPTPServer.Config = &VPCRouterPPTPServerConfig{ + RangeStart: rangeStart, + RangeStop: rangeStop, + } +} + +// DisablePPTPServer PPTPサーバー設定 無効化 +func (s *VPCRouterSetting) DisablePPTPServer() { + if s.PPTPServer == nil { + s.PPTPServer = &VPCRouterPPTPServer{} + } + s.PPTPServer.Enabled = "False" + s.PPTPServer.Config = nil +} + +// HasRemoteAccessUsers リモートアクセスユーザー設定を保持しているか +func (s *VPCRouterSetting) HasRemoteAccessUsers() bool { + return s.RemoteAccessUsers != nil && len(s.RemoteAccessUsers.Config) > 0 +} + +// VPCRouterRemoteAccessUsers リモートアクセスユーザー設定 +type VPCRouterRemoteAccessUsers struct { + Config []*VPCRouterRemoteAccessUsersConfig `json:",omitempty"` // リモートアクセスユーザー設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterRemoteAccessUsersConfig リモートアクセスユーザー設定 +type VPCRouterRemoteAccessUsersConfig struct { + UserName string `json:",omitempty"` // ユーザー名 + Password string `json:",omitempty"` // パスワード +} + +// AddRemoteAccessUser リモートアクセスユーザー設定 追加 +func (s *VPCRouterSetting) AddRemoteAccessUser(userName string, password string) (int, *VPCRouterRemoteAccessUsersConfig) { + if s.RemoteAccessUsers == nil { + s.RemoteAccessUsers = &VPCRouterRemoteAccessUsers{ + Enabled: "True", + } + } + if s.RemoteAccessUsers.Config == nil { + s.RemoteAccessUsers.Config = []*VPCRouterRemoteAccessUsersConfig{} + } + + c := &VPCRouterRemoteAccessUsersConfig{ + UserName: userName, + Password: password, + } + s.RemoteAccessUsers.Config = append(s.RemoteAccessUsers.Config, c) + return len(s.RemoteAccessUsers.Config) - 1, c +} + +// RemoveRemoteAccessUser リモートアクセスユーザー設定 削除 +func (s *VPCRouterSetting) RemoveRemoteAccessUser(userName string, password string) { + if s.RemoteAccessUsers == nil { + return + } + + if s.RemoteAccessUsers.Config == nil { + s.RemoteAccessUsers.Enabled = "False" + return + } + + dest := []*VPCRouterRemoteAccessUsersConfig{} + for _, c := range s.RemoteAccessUsers.Config { + if c.UserName != userName || c.Password != password { + dest = append(dest, c) + } + } + s.RemoteAccessUsers.Config = dest + + if len(s.RemoteAccessUsers.Config) == 0 { + s.RemoteAccessUsers.Enabled = "False" + s.RemoteAccessUsers.Config = nil + return + } + s.RemoteAccessUsers.Enabled = "True" +} + +// RemoveRemoteAccessUserAt リモートアクセスユーザー設定 削除 +func (s *VPCRouterSetting) RemoveRemoteAccessUserAt(index int) { + if s.RemoteAccessUsers == nil { + return + } + + if s.RemoteAccessUsers.Config == nil || len(s.RemoteAccessUsers.Config) == 0 { + s.RemoteAccessUsers.Enabled = "False" + return + } + + if index < len(s.RemoteAccessUsers.Config) { + c := s.RemoteAccessUsers.Config[index] + s.RemoveRemoteAccessUser(c.UserName, c.Password) + } +} + +// FindRemoteAccessUser リモートアクセスユーザー設定 検索 +func (s *VPCRouterSetting) FindRemoteAccessUser(userName string, password string) (int, *VPCRouterRemoteAccessUsersConfig) { + for i, c := range s.RemoteAccessUsers.Config { + if c.UserName == userName && c.Password == password { + return i, c + } + } + return -1, nil +} + +// HasSiteToSiteIPsecVPN サイト間VPN設定を保持しているか +func (s *VPCRouterSetting) HasSiteToSiteIPsecVPN() bool { + return s.SiteToSiteIPsecVPN != nil && len(s.SiteToSiteIPsecVPN.Config) > 0 +} + +// VPCRouterSiteToSiteIPsecVPN サイト間VPN設定 +type VPCRouterSiteToSiteIPsecVPN struct { + Config []*VPCRouterSiteToSiteIPsecVPNConfig `json:",omitempty"` // サイト間VPC設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterSiteToSiteIPsecVPNConfig サイト間VPN設定 +type VPCRouterSiteToSiteIPsecVPNConfig struct { + LocalPrefix []string `json:",omitempty"` // ローカルプレフィックス リスト + Peer string `json:",omitempty"` // 対向IPアドレス + PreSharedSecret string `json:",omitempty"` // 事前共有シークレット + RemoteID string `json:",omitempty"` // 対向ID + Routes []string `json:",omitempty"` // 対向プレフィックス リスト +} + +// AddSiteToSiteIPsecVPN サイト間VPN設定 追加 +func (s *VPCRouterSetting) AddSiteToSiteIPsecVPN(localPrefix []string, peer string, + preSharedSecret string, remoteID string, routes []string) (int, *VPCRouterSiteToSiteIPsecVPNConfig) { + if s.SiteToSiteIPsecVPN == nil { + s.SiteToSiteIPsecVPN = &VPCRouterSiteToSiteIPsecVPN{ + Enabled: "True", + } + } else { + s.SiteToSiteIPsecVPN.Enabled = "True" + } + + if s.SiteToSiteIPsecVPN.Config == nil { + s.SiteToSiteIPsecVPN.Config = []*VPCRouterSiteToSiteIPsecVPNConfig{} + } + + c := &VPCRouterSiteToSiteIPsecVPNConfig{ + LocalPrefix: localPrefix, + Peer: peer, + PreSharedSecret: preSharedSecret, + RemoteID: remoteID, + Routes: routes, + } + s.SiteToSiteIPsecVPN.Config = append(s.SiteToSiteIPsecVPN.Config, c) + return len(s.SiteToSiteIPsecVPN.Config) - 1, c +} + +// RemoveSiteToSiteIPsecVPN サイト間VPN設定 削除 +func (s *VPCRouterSetting) RemoveSiteToSiteIPsecVPN(localPrefix []string, peer string, preSharedSecret string, remoteID string, routes []string) { + config := &VPCRouterSiteToSiteIPsecVPNConfig{ + LocalPrefix: localPrefix, + Peer: peer, + PreSharedSecret: preSharedSecret, + RemoteID: remoteID, + Routes: routes, + } + + if s.SiteToSiteIPsecVPN == nil { + return + } + + if s.SiteToSiteIPsecVPN.Config == nil { + s.SiteToSiteIPsecVPN.Enabled = "False" + return + } + + dest := []*VPCRouterSiteToSiteIPsecVPNConfig{} + for _, c := range s.SiteToSiteIPsecVPN.Config { + if !s.isSameSiteToSiteIPsecVPNConfig(c, config) { + dest = append(dest, c) + } + } + s.SiteToSiteIPsecVPN.Config = dest + + if len(s.SiteToSiteIPsecVPN.Config) == 0 { + s.SiteToSiteIPsecVPN.Enabled = "False" + s.SiteToSiteIPsecVPN.Config = nil + return + } + s.SiteToSiteIPsecVPN.Enabled = "True" +} + +// RemoveSiteToSiteIPsecVPNAt サイト間VPN設定 削除 +func (s *VPCRouterSetting) RemoveSiteToSiteIPsecVPNAt(index int) { + if s.SiteToSiteIPsecVPN == nil { + return + } + + if s.SiteToSiteIPsecVPN.Config == nil || len(s.SiteToSiteIPsecVPN.Config) == 0 { + s.SiteToSiteIPsecVPN.Enabled = "False" + return + } + + if index < len(s.SiteToSiteIPsecVPN.Config) { + c := s.SiteToSiteIPsecVPN.Config[index] + s.RemoveSiteToSiteIPsecVPN(c.LocalPrefix, c.Peer, c.PreSharedSecret, c.RemoteID, c.Routes) + } +} + +// FindSiteToSiteIPsecVPN サイト間VPC設定 検索 +func (s *VPCRouterSetting) FindSiteToSiteIPsecVPN(localPrefix []string, peer string, + preSharedSecret string, remoteID string, routes []string) (int, *VPCRouterSiteToSiteIPsecVPNConfig) { + config := &VPCRouterSiteToSiteIPsecVPNConfig{ + LocalPrefix: localPrefix, + Peer: peer, + PreSharedSecret: preSharedSecret, + RemoteID: remoteID, + Routes: routes, + } + + for i, c := range s.SiteToSiteIPsecVPN.Config { + if s.isSameSiteToSiteIPsecVPNConfig(c, config) { + return i, c + } + } + return -1, nil +} + +func (s *VPCRouterSetting) isSameSiteToSiteIPsecVPNConfig(c1 *VPCRouterSiteToSiteIPsecVPNConfig, c2 *VPCRouterSiteToSiteIPsecVPNConfig) bool { + return reflect.DeepEqual(c1.LocalPrefix, c2.LocalPrefix) && + c1.Peer == c2.Peer && + c1.PreSharedSecret == c2.PreSharedSecret && + c1.RemoteID == c2.RemoteID && + reflect.DeepEqual(c1.Routes, c2.Routes) +} + +// HasStaticRoutes スタティックルートを保持しているか +func (s *VPCRouterSetting) HasStaticRoutes() bool { + return s.StaticRoutes != nil && len(s.StaticRoutes.Config) > 0 +} + +// VPCRouterStaticRoutes スタティックルート設定 +type VPCRouterStaticRoutes struct { + Config []*VPCRouterStaticRoutesConfig `json:",omitempty"` // スタティックルート設定 + Enabled string `json:",omitempty"` // 有効/無効 +} + +// VPCRouterStaticRoutesConfig スタティックルート設定 +type VPCRouterStaticRoutesConfig struct { + Prefix string `json:",omitempty"` // プレフィックス + NextHop string `json:",omitempty"` // ネクストホップ +} + +// AddStaticRoute スタティックルート設定 追加 +func (s *VPCRouterSetting) AddStaticRoute(prefix string, nextHop string) (int, *VPCRouterStaticRoutesConfig) { + if s.StaticRoutes == nil { + s.StaticRoutes = &VPCRouterStaticRoutes{ + Enabled: "True", + } + } + if s.StaticRoutes.Config == nil { + s.StaticRoutes.Config = []*VPCRouterStaticRoutesConfig{} + } + + c := &VPCRouterStaticRoutesConfig{ + Prefix: prefix, + NextHop: nextHop, + } + s.StaticRoutes.Config = append(s.StaticRoutes.Config, c) + return len(s.StaticRoutes.Config) - 1, c +} + +// RemoveStaticRoute スタティックルート設定 削除 +func (s *VPCRouterSetting) RemoveStaticRoute(prefix string, nextHop string) { + if s.StaticRoutes == nil { + return + } + + if s.StaticRoutes.Config == nil { + s.StaticRoutes.Enabled = "False" + return + } + + dest := []*VPCRouterStaticRoutesConfig{} + for _, c := range s.StaticRoutes.Config { + if c.Prefix != prefix || c.NextHop != nextHop { + dest = append(dest, c) + } + } + s.StaticRoutes.Config = dest + + if len(s.StaticRoutes.Config) == 0 { + s.StaticRoutes.Enabled = "False" + s.StaticRoutes.Config = nil + return + } + s.StaticRoutes.Enabled = "True" +} + +// RemoveStaticRouteAt スタティックルート設定 削除 +func (s *VPCRouterSetting) RemoveStaticRouteAt(index int) { + if s.StaticRoutes == nil { + return + } + + if s.StaticRoutes.Config == nil || len(s.StaticRoutes.Config) == 0 { + s.StaticRoutes.Enabled = "False" + return + } + + if index < len(s.StaticRoutes.Config) { + c := s.StaticRoutes.Config[index] + s.RemoveStaticRoute(c.Prefix, c.NextHop) + } +} + +// FindStaticRoute スタティックルート設定 検索 +func (s *VPCRouterSetting) FindStaticRoute(prefix string, nextHop string) (int, *VPCRouterStaticRoutesConfig) { + for i, c := range s.StaticRoutes.Config { + if c.Prefix == prefix && c.NextHop == nextHop { + return i, c + } + } + return -1, nil +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go new file mode 100644 index 000000000..ffe0ea0ef --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/vpc_router_status.go @@ -0,0 +1,8 @@ +package sacloud + +// VPCRouterStatus VPCルータのステータス情報 +type VPCRouterStatus struct { + FirewallReceiveLogs []string + FirewallSendLogs []string + VPNLogs []string +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/webaccel.go b/vendor/github.com/sacloud/libsacloud/sacloud/webaccel.go new file mode 100644 index 000000000..b7282b29d --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/webaccel.go @@ -0,0 +1,201 @@ +package sacloud + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + +// EWebAccelDomainType ウェブアクセラレータ ドメイン種別 +type EWebAccelDomainType string + +const ( + // EWebAccelDomainTypeOwn 独自ドメイン + EWebAccelDomainTypeOwn = EWebAccelDomainType("own_domain") + // EWebAccelDomainTypeSubDomain サブドメイン + EWebAccelDomainTypeSubDomain = EWebAccelDomainType("subdomain") +) + +// EWebAccelStatus ウェブアクセラレータ ステータス +type EWebAccelStatus string + +const ( + // EWebAccelStatusEnabled 状態:有効 + EWebAccelStatusEnabled = EWebAccelStatus("enabled") + // EWebAccelStatusDisabled 状態:無効 + EWebAccelStatusDisabled = EWebAccelStatus("disabled") +) + +// WebAccelSite ウェブアクセラレータ サイト +type WebAccelSite struct { + ID string // ID + Name string `json:",omitempty"` + DomainType EWebAccelDomainType `json:",omitempty"` + Domain string `json:",omitempty"` + Subdomain string `json:",omitempty"` + ASCIIDomain string `json:",omitempty"` + Origin string `json:",omitempty"` + HostHeader string + Status EWebAccelStatus `json:",omitempty"` + HasCertificate bool `json:",omitempty"` + HasOldCertificate bool `json:",omitempty"` + GibSentInLastWeek int64 `json:",omitempty"` + CertValidNotBefore int64 `json:",omitempty"` + CertValidNotAfter int64 `json:",omitempty"` + propCreatedAt +} + +// SetID ID 設定 +func (n *WebAccelSite) SetID(id int64) { + n.ID = fmt.Sprintf("%d", id) +} + +// GetID ID 取得 +func (n *WebAccelSite) GetID() int64 { + if n == nil { + return -1 + } + i, err := strconv.ParseInt(n.ID, 10, 64) + if err != nil { + return -1 + } + return i +} + +// GetStrID 文字列でID取得 +func (n *WebAccelSite) GetStrID() string { + if n == nil { + return "" + } + return n.ID +} + +// GetName 名称取得 +func (n *WebAccelSite) GetName() string { + if n == nil { + return "" + } + return n.Name +} + +// SetName 名称取得 +func (n *WebAccelSite) SetName(name string) { + if n == nil { + return + } + n.Name = name +} + +// WebAccelCert ウェブアクセラレータ証明書 +type WebAccelCert struct { + ID string `json:",omitempty"` + SiteID string `json:",omitempty"` + CertificateChain string `json:",omitempty"` + Key string `json:",omitempty"` + propCreatedAt `json:",omitempty"` + propUpdatedAt `json:",omitempty"` + + SerialNumber string `json:",omitempty"` + NotBefore int64 `json:",omitempty"` + NotAfter int64 `json:",omitempty"` + Issuer *struct { + Country string `json:",omitempty"` + Organization string `json:",omitempty"` + OrganizationalUnit string `json:",omitempty"` + CommonName string `json:",omitempty"` + } `json:",omitempty"` + Subject *struct { + Country string `json:",omitempty"` + Organization string `json:",omitempty"` + OrganizationalUnit string `json:",omitempty"` + Locality string `json:",omitempty"` + Province string `json:",omitempty"` + StreetAddress string `json:",omitempty"` + PostalCode string `json:",omitempty"` + SerialNumber string `json:",omitempty"` + CommonName string `json:",omitempty"` + } `json:",omitempty"` + DNSNames []string `json:",omitempty"` + SHA256Fingerprint string `json:",omitempty"` +} + +// SetID ID 設定 +func (n *WebAccelCert) SetID(id int64) { + n.ID = fmt.Sprintf("%d", id) +} + +// GetID ID 取得 +func (n *WebAccelCert) GetID() int64 { + if n == nil { + return -1 + } + i, err := strconv.ParseInt(n.ID, 10, 64) + if err != nil { + return -1 + } + return i +} + +// GetStrID 文字列でID取得 +func (n *WebAccelCert) GetStrID() string { + if n == nil { + return "" + } + return n.ID +} + +// WebAccelCertRequest ウェブアクセラレータ証明書API リクエスト +type WebAccelCertRequest struct { + CertificateChain string + Key string `json:",omitempty"` +} + +// WebAccelCertResponse ウェブアクセラレータ証明書API レスポンス +type WebAccelCertResponse struct { + Certificate *WebAccelCertResponseBody `json:",omitempty"` + ResultFlagValue +} + +// WebAccelCertResponseBody ウェブアクセラレータ証明書API レスポンスボディ +type WebAccelCertResponseBody struct { + Current *WebAccelCert `json:",omitempty"` + Old []*WebAccelCert `json:",omitempty"` +} + +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (s *WebAccelCertResponse) UnmarshalJSON(data []byte) error { + tmp := &struct { + Certificate *WebAccelCertResponseBody `json:",omitempty"` + ResultFlagValue + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + if tmp.Certificate.Current != nil || len(tmp.Certificate.Old) > 0 { + s.Certificate = tmp.Certificate + } + s.ResultFlagValue = tmp.ResultFlagValue + return nil +} + +// UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応) +func (s *WebAccelCertResponseBody) UnmarshalJSON(data []byte) error { + targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1) + if targetData == `[]` { + return nil + } + + tmp := &struct { + Current *WebAccelCert `json:",omitempty"` + Old []*WebAccelCert `json:",omitempty"` + }{} + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + s.Current = tmp.Current + s.Old = tmp.Old + return nil +} diff --git a/vendor/github.com/sacloud/libsacloud/sacloud/zone.go b/vendor/github.com/sacloud/libsacloud/sacloud/zone.go new file mode 100644 index 000000000..c3ae58f9f --- /dev/null +++ b/vendor/github.com/sacloud/libsacloud/sacloud/zone.go @@ -0,0 +1,46 @@ +package sacloud + +// Zone ゾーン +type Zone struct { + *Resource // ID + propName // 名称 + propDescription // 説明 + propRegion // リージョン + + IsDummy bool `json:",omitempty"` // IsDummy ダミーフラグ + + VNCProxy struct { // VNCProxy VPCプロキシ + HostName string `json:",omitempty"` // HostName ホスト名 + IPAddress string `json:",omitempty"` // IPAddress IPアドレス + } `json:",omitempty"` + + FTPServer struct { // FTPServer FTPサーバー + HostName string `json:",omitempty"` // HostName ホスト名 + IPAddress string `json:",omitempty"` // IPAddress IPアドレス + } `json:",omitempty"` +} + +// ZoneIsDummy ダミーフラグ 取得 +func (z *Zone) ZoneIsDummy() bool { + return z.IsDummy +} + +// GetVNCProxyHostName VNCプロキシホスト名 取得 +func (z *Zone) GetVNCProxyHostName() string { + return z.VNCProxy.HostName +} + +// GetVPCProxyIPAddress VNCプロキシIPアドレス 取得 +func (z *Zone) GetVPCProxyIPAddress() string { + return z.VNCProxy.IPAddress +} + +// GetFTPHostName FTPサーバーホスト名 取得 +func (z *Zone) GetFTPHostName() string { + return z.FTPServer.HostName +} + +// GetFTPServerIPAddress FTPサーバーIPアドレス 取得 +func (z *Zone) GetFTPServerIPAddress() string { + return z.FTPServer.IPAddress +} diff --git a/vendor/github.com/xenolf/lego/acme/challenges.go b/vendor/github.com/xenolf/lego/acme/challenges.go index cf7bd7f75..1140b1073 100644 --- a/vendor/github.com/xenolf/lego/acme/challenges.go +++ b/vendor/github.com/xenolf/lego/acme/challenges.go @@ -10,4 +10,6 @@ const ( // DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns // Note: DNS01Record returns a DNS record which will fulfill this challenge DNS01 = Challenge("dns-01") + // TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01 + TLSALPN01 = Challenge("tls-alpn-01") ) diff --git a/vendor/github.com/xenolf/lego/acme/client.go b/vendor/github.com/xenolf/lego/acme/client.go index a9e346089..b8daa7512 100644 --- a/vendor/github.com/xenolf/lego/acme/client.go +++ b/vendor/github.com/xenolf/lego/acme/client.go @@ -5,6 +5,7 @@ import ( "crypto" "crypto/x509" "encoding/base64" + "encoding/pem" "errors" "fmt" "io/ioutil" @@ -81,8 +82,10 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) { // REVIEW: best possibility? // Add all available solvers with the right index as per ACME // spec to this map. Otherwise they won`t be found. - solvers := make(map[Challenge]solver) - solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}} + solvers := map[Challenge]solver{ + HTTP01: &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}, + TLSALPN01: &tlsALPNChallenge{jws: jws, validate: validate, provider: &TLSALPNProviderServer{}}, + } return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil } @@ -94,8 +97,10 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p} case DNS01: c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p} + case TLSALPN01: + c.solvers[challenge] = &tlsALPNChallenge{jws: c.jws, validate: validate, provider: p} default: - return fmt.Errorf("Unknown challenge %v", challenge) + return fmt.Errorf("unknown challenge %v", challenge) } return nil } @@ -119,6 +124,24 @@ func (c *Client) SetHTTPAddress(iface string) error { return nil } +// SetTLSAddress specifies a custom interface:port to be used for TLS based challenges. +// If this option is not used, the default port 443 and all interfaces will be used. +// To only specify a port and no interface use the ":port" notation. +// +// NOTE: This REPLACES any custom TLS-ALPN provider previously set by calling +// c.SetChallengeProvider with the default TLS-ALPN challenge provider. +func (c *Client) SetTLSAddress(iface string) error { + host, port, err := net.SplitHostPort(iface) + if err != nil { + return err + } + + if chlng, ok := c.solvers[TLSALPN01]; ok { + chlng.(*tlsALPNChallenge).provider = NewTLSALPNProviderServer(host, port) + } + return nil +} + // ExcludeChallenges explicitly removes challenges from the pool for solving. func (c *Client) ExcludeChallenges(challenges []Challenge) { // Loop through all challenges and delete the requested one if found. @@ -142,7 +165,7 @@ func (c *Client) Register(tosAgreed bool) (*RegistrationResource, error) { if c == nil || c.user == nil { return nil, errors.New("acme: cannot register a nil client or user") } - log.Printf("[INFO] acme: Registering account for %s", c.user.GetEmail()) + log.Infof("acme: Registering account for %s", c.user.GetEmail()) accMsg := accountMessage{} if c.user.GetEmail() != "" { @@ -176,7 +199,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string, if c == nil || c.user == nil { return nil, errors.New("acme: cannot register a nil client or user") } - log.Printf("[INFO] acme: Registering account (EAB) for %s", c.user.GetEmail()) + log.Infof("acme: Registering account (EAB) for %s", c.user.GetEmail()) accMsg := accountMessage{} if c.user.GetEmail() != "" { @@ -222,7 +245,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string, // ResolveAccountByKey will attempt to look up an account using the given account key // and return its registration resource. func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) { - log.Printf("[INFO] acme: Trying to resolve account by key") + log.Infof("acme: Trying to resolve account by key") acc := accountMessage{OnlyReturnExisting: true} hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil) @@ -251,7 +274,7 @@ func (c *Client) DeleteRegistration() error { if c == nil || c.user == nil { return errors.New("acme: cannot unregister a nil client or user") } - log.Printf("[INFO] acme: Deleting account for %s", c.user.GetEmail()) + log.Infof("acme: Deleting account for %s", c.user.GetEmail()) accMsg := accountMessage{ Status: "deactivated", @@ -271,7 +294,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) { return nil, errors.New("acme: cannot query the registration of a nil client or user") } // Log the URL here instead of the email as the email may not be set - log.Printf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI) + log.Infof("acme: Querying account for %s", c.user.GetRegistration().URI) accMsg := accountMessage{} @@ -317,9 +340,9 @@ DNSNames: } if bundle { - log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) } else { - log.Printf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) } order, err := c.createOrderForIdentifiers(domains) @@ -341,7 +364,7 @@ DNSNames: return nil, err } - log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) failures := make(ObtainError) cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil) @@ -377,9 +400,9 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto } if bundle { - log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) } else { - log.Printf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) } order, err := c.createOrderForIdentifiers(domains) @@ -401,7 +424,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto return nil, err } - log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) + log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) failures := make(ObtainError) cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple) @@ -460,7 +483,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b // This is just meant to be informal for the user. timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) + log.Infof("[%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours())) // We always need to request a new certificate to renew. // Start by checking to see if the certificate was based off a CSR, and @@ -534,7 +557,7 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error { for _, authz := range authorizations { if authz.Status == "valid" { // Boulder might recycle recent validated authz (see issue #267) - log.Printf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value) + log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value) continue } @@ -565,7 +588,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) { if solver, ok := c.solvers[Challenge(challenge.Type)]; ok { return i, solver } - log.Printf("[INFO][%s] acme: Could not find solver for: %s", domain, challenge.Type) + log.Infof("[%s] acme: Could not find solver for: %s", domain, challenge.Type) } return 0, nil } @@ -617,7 +640,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error) func logAuthz(order orderResource) { for i, auth := range order.Authorizations { - log.Printf("[INFO][%s] AuthURL: %s", order.Identifiers[i].Value, auth) + log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) } } @@ -640,9 +663,18 @@ func (c *Client) requestCertificateForOrder(order orderResource, bundle bool, pr // determine certificate name(s) based on the authorization resources commonName := order.Domains[0] - var san []string + + // ACME draft Section 7.4 "Applying for Certificate Issuance" + // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4 + // says: + // Clients SHOULD NOT make any assumptions about the sort order of + // "identifiers" or "authorizations" elements in the returned order + // object. + san := []string{commonName} for _, auth := range order.Identifiers { - san = append(san, auth.Value) + if auth.Value != commonName { + san = append(san, auth.Value) + } } // TODO: should the CSR be customizable? @@ -659,13 +691,13 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr csrString := base64.RawURLEncoding.EncodeToString(csr) var retOrder orderMessage - _, error := postJSON(c.jws, order.Finalize, csrMessage{Csr: csrString}, &retOrder) - if error != nil { - return nil, error + _, err := postJSON(c.jws, order.Finalize, csrMessage{Csr: csrString}, &retOrder) + if err != nil { + return nil, err } if retOrder.Status == "invalid" { - return nil, error + return nil, err } certRes := CertificateResource{ @@ -686,25 +718,30 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr } } - maxChecks := 1000 - for i := 0; i < maxChecks; i++ { - _, err := getJSON(order.URL, &retOrder) - if err != nil { - return nil, err - } - done, err := c.checkCertResponse(retOrder, &certRes, bundle) - if err != nil { - return nil, err - } - if done { - break - } - if i == maxChecks-1 { - return nil, fmt.Errorf("polled for certificate %d times; giving up", i) + stopTimer := time.NewTimer(30 * time.Second) + defer stopTimer.Stop() + retryTick := time.NewTicker(500 * time.Millisecond) + defer retryTick.Stop() + + for { + select { + case <-stopTimer.C: + return nil, errors.New("certificate polling timed out") + case <-retryTick.C: + _, err := getJSON(order.URL, &retOrder) + if err != nil { + return nil, err + } + + done, err := c.checkCertResponse(retOrder, &certRes, bundle) + if err != nil { + return nil, err + } + if done { + return &certRes, nil + } } } - - return &certRes, nil } // checkCertResponse checks to see if the certificate is ready and a link is contained in the @@ -726,15 +763,16 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou return false, err } - // The issuer certificate link is always supplied via an "up" link - // in the response headers of a new certificate. + // The issuer certificate link may be supplied via an "up" link + // in the response headers of a new certificate. See + // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.2 links := parseLinks(resp.Header["Link"]) if link, ok := links["up"]; ok { issuerCert, err := c.getIssuerCertificate(link) if err != nil { // If we fail to acquire the issuer cert, return the issued certificate - do not fail. - log.Printf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err) + log.Warnf("[%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err) } else { issuerCert = pemEncode(derCertificateBytes(issuerCert)) @@ -746,26 +784,33 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou certRes.IssuerCertificate = issuerCert } + } else { + // Get issuerCert from bundled response from Let's Encrypt + // See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962 + _, rest := pem.Decode(cert) + if rest != nil { + certRes.IssuerCertificate = rest + } } certRes.Certificate = cert certRes.CertURL = order.Certificate certRes.CertStableURL = order.Certificate - log.Printf("[INFO][%s] Server responded with a certificate.", certRes.Domain) + log.Infof("[%s] Server responded with a certificate.", certRes.Domain) return true, nil case "processing": return false, nil case "invalid": - return false, errors.New("Order has invalid state: invalid") + return false, errors.New("order has invalid state: invalid") + default: + return false, nil } - - return false, nil } // getIssuerCertificate requests the issuer certificate func (c *Client) getIssuerCertificate(url string) ([]byte, error) { - log.Printf("[INFO] acme: Requesting issuer cert from %s", url) + log.Infof("acme: Requesting issuer cert from %s", url) resp, err := httpGet(url) if err != nil { return nil, err @@ -819,7 +864,7 @@ func validate(j *jws, domain, uri string, c challenge) error { for { switch chlng.Status { case "valid": - log.Printf("[INFO][%s] The server validated our request", domain) + log.Infof("[%s] The server validated our request", domain) return nil case "pending": case "processing": diff --git a/vendor/github.com/xenolf/lego/acme/crypto.go b/vendor/github.com/xenolf/lego/acme/crypto.go index 7d4f44258..f5ebbf08e 100644 --- a/vendor/github.com/xenolf/lego/acme/crypto.go +++ b/vendor/github.com/xenolf/lego/acme/crypto.go @@ -215,9 +215,7 @@ func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) { func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) { template := x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: domain, - }, + Subject: pkix.Name{CommonName: domain}, } if len(san) > 0 { @@ -303,8 +301,8 @@ func getCertExpiration(cert []byte) (time.Time, error) { return pCert.NotAfter, nil } -func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) { - derBytes, err := generateDerCert(privKey, time.Time{}, domain) +func generatePemCert(privKey *rsa.PrivateKey, domain string, extensions []pkix.Extension) ([]byte, error) { + derBytes, err := generateDerCert(privKey, time.Time{}, domain, extensions) if err != nil { return nil, err } @@ -312,7 +310,7 @@ func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) { return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil } -func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) { +func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string, extensions []pkix.Extension) ([]byte, error) { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { @@ -334,6 +332,7 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin KeyUsage: x509.KeyUsageKeyEncipherment, BasicConstraintsValid: true, DNSNames: []string{domain}, + ExtraExtensions: extensions, } return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey) diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go index d494501c8..c8a35eb88 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go @@ -72,10 +72,10 @@ type dnsChallenge struct { } func (s *dnsChallenge) Solve(chlng challenge, domain string) error { - log.Printf("[INFO][%s] acme: Trying to solve DNS-01", domain) + log.Infof("[%s] acme: Trying to solve DNS-01", domain) if s.provider == nil { - return errors.New("No DNS Provider configured") + return errors.New("no DNS Provider configured") } // Generate the Key Authorization for the challenge @@ -86,18 +86,18 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error { err = s.provider.Present(domain, chlng.Token, keyAuth) if err != nil { - return fmt.Errorf("Error presenting token: %s", err) + return fmt.Errorf("error presenting token: %s", err) } defer func() { err := s.provider.CleanUp(domain, chlng.Token, keyAuth) if err != nil { - log.Printf("Error cleaning up %s: %v ", domain, err) + log.Warnf("Error cleaning up %s: %v ", domain, err) } }() fqdn, value, _ := DNS01Record(domain, keyAuth) - log.Printf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) + log.Infof("[%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers) var timeout, interval time.Duration switch provider := s.provider.(type) { diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go b/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go index cd4c3c8a6..ca94fcac7 100644 --- a/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go +++ b/vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go @@ -30,9 +30,9 @@ func (*DNSProviderManual) Present(domain, token, keyAuth string) error { return err } - log.Printf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone) - log.Printf("[INFO] acme: %s", dnsRecord) - log.Printf("[INFO] acme: Press 'Enter' when you are done") + log.Infof("acme: Please create the following TXT record in your %s zone:", authZone) + log.Infof("acme: %s", dnsRecord) + log.Infof("acme: Press 'Enter' when you are done") reader := bufio.NewReader(os.Stdin) _, _ = reader.ReadString('\n') @@ -49,7 +49,7 @@ func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error { return err } - log.Printf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone) - log.Printf("[INFO] acme: %s", dnsRecord) + log.Infof("acme: You can now remove this TXT record from your %s zone:", authZone) + log.Infof("acme: %s", dnsRecord) return nil } diff --git a/vendor/github.com/xenolf/lego/acme/http.go b/vendor/github.com/xenolf/lego/acme/http.go index f45e7f871..8d7ee51ef 100644 --- a/vendor/github.com/xenolf/lego/acme/http.go +++ b/vendor/github.com/xenolf/lego/acme/http.go @@ -1,33 +1,45 @@ package acme import ( + "crypto/tls" + "crypto/x509" "encoding/json" "errors" "fmt" "io" + "io/ioutil" "net" "net/http" + "os" "runtime" "strings" "time" ) -// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests. -var UserAgent string +var ( + // UserAgent (if non-empty) will be tacked onto the User-Agent string in requests. + UserAgent string -// HTTPClient is an HTTP client with a reasonable timeout value. -var HTTPClient = http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 15 * time.Second, - ResponseHeaderTimeout: 15 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - }, -} + // HTTPClient is an HTTP client with a reasonable timeout value and + // potentially a custom *x509.CertPool based on the caCertificatesEnvVar + // environment variable (see the `initCertPool` function) + HTTPClient = http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + TLSHandshakeTimeout: 15 * time.Second, + ResponseHeaderTimeout: 15 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: &tls.Config{ + ServerName: os.Getenv(caServerNameEnvVar), + RootCAs: initCertPool(), + }, + }, + } +) const ( // defaultGoUserAgent is the Go HTTP package user agent string. Too @@ -36,12 +48,46 @@ const ( // ourUserAgent is the User-Agent of this underlying library package. ourUserAgent = "xenolf-acme" + + // caCertificatesEnvVar is the environment variable name that can be used to + // specify the path to PEM encoded CA Certificates that can be used to + // authenticate an ACME server with a HTTPS certificate not issued by a CA in + // the system-wide trusted root list. + caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" + + // caServerNameEnvVar is the environment variable name that can be used to + // specify the CA server name that can be used to + // authenticate an ACME server with a HTTPS certificate not issued by a CA in + // the system-wide trusted root list. + caServerNameEnvVar = "LEGO_CA_SERVER_NAME" ) +// initCertPool creates a *x509.CertPool populated with the PEM certificates +// found in the filepath specified in the caCertificatesEnvVar OS environment +// variable. If the caCertificatesEnvVar is not set then initCertPool will +// return nil. If there is an error creating a *x509.CertPool from the provided +// caCertificatesEnvVar value then initCertPool will panic. +func initCertPool() *x509.CertPool { + if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { + customCAs, err := ioutil.ReadFile(customCACertsPath) + if err != nil { + panic(fmt.Sprintf("error reading %s=%q: %v", + caCertificatesEnvVar, customCACertsPath, err)) + } + certPool := x509.NewCertPool() + if ok := certPool.AppendCertsFromPEM(customCAs); !ok { + panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", + caCertificatesEnvVar, customCACertsPath, err)) + } + return certPool + } + return nil +} + // httpHead performs a HEAD request with a proper User-Agent string. // The response body (resp.Body) is already closed when this function returns. func httpHead(url string) (resp *http.Response, err error) { - req, err := http.NewRequest("HEAD", url, nil) + req, err := http.NewRequest(http.MethodHead, url, nil) if err != nil { return nil, fmt.Errorf("failed to head %q: %v", url, err) } @@ -59,7 +105,7 @@ func httpHead(url string) (resp *http.Response, err error) { // httpPost performs a POST request with a proper User-Agent string. // Callers should close resp.Body when done reading from it. func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) { - req, err := http.NewRequest("POST", url, body) + req, err := http.NewRequest(http.MethodPost, url, body) if err != nil { return nil, fmt.Errorf("failed to post %q: %v", url, err) } @@ -72,7 +118,7 @@ func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, // httpGet performs a GET request with a proper User-Agent string. // Callers should close resp.Body when done reading from it. func httpGet(url string) (resp *http.Response, err error) { - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("failed to get %q: %v", url, err) } diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge.go b/vendor/github.com/xenolf/lego/acme/http_challenge.go index 7659bfc51..77a8edd49 100644 --- a/vendor/github.com/xenolf/lego/acme/http_challenge.go +++ b/vendor/github.com/xenolf/lego/acme/http_challenge.go @@ -19,7 +19,7 @@ func HTTP01ChallengePath(token string) string { func (s *httpChallenge) Solve(chlng challenge, domain string) error { - log.Printf("[INFO][%s] acme: Trying to solve HTTP-01", domain) + log.Infof("[%s] acme: Trying to solve HTTP-01", domain) // Generate the Key Authorization for the challenge keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey) @@ -34,7 +34,7 @@ func (s *httpChallenge) Solve(chlng challenge, domain string) error { defer func() { err := s.provider.CleanUp(domain, chlng.Token, keyAuth) if err != nil { - log.Printf("[%s] error cleaning up: %v", domain, err) + log.Warnf("[%s] error cleaning up: %v", domain, err) } }() diff --git a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go b/vendor/github.com/xenolf/lego/acme/http_challenge_server.go index 214a278fc..319e26185 100644 --- a/vendor/github.com/xenolf/lego/acme/http_challenge_server.go +++ b/vendor/github.com/xenolf/lego/acme/http_challenge_server.go @@ -60,12 +60,12 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) { // For validation it then writes the token the server returned with the challenge mux := http.NewServeMux() mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.Host, domain) && r.Method == "GET" { + if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet { w.Header().Add("Content-Type", "text/plain") w.Write([]byte(keyAuth)) - log.Printf("[INFO][%s] Served key authentication", domain) + log.Infof("[%s] Served key authentication", domain) } else { - log.Printf("[WARN] Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) + log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) w.Write([]byte("TEST")) } }) diff --git a/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go b/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go new file mode 100644 index 000000000..d80351999 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go @@ -0,0 +1,104 @@ +package acme + +import ( + "crypto/rsa" + "crypto/sha256" + "crypto/tls" + "crypto/x509/pkix" + "encoding/asn1" + "fmt" + + "github.com/xenolf/lego/log" +) + +// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. +// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.1 +var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1} + +type tlsALPNChallenge struct { + jws *jws + validate validateFunc + provider ChallengeProvider +} + +// Solve manages the provider to validate and solve the challenge. +func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error { + log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", domain) + + // Generate the Key Authorization for the challenge + keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey) + if err != nil { + return err + } + + err = t.provider.Present(domain, chlng.Token, keyAuth) + if err != nil { + return fmt.Errorf("[%s] error presenting token: %v", domain, err) + } + defer func() { + err := t.provider.CleanUp(domain, chlng.Token, keyAuth) + if err != nil { + log.Warnf("[%s] error cleaning up: %v", domain, err) + } + }() + + return t.validate(t.jws, domain, chlng.URL, challenge{Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth}) +} + +// TLSALPNChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension +// and domain name for the `tls-alpn-01` challenge. +func TLSALPNChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { + // Compute the SHA-256 digest of the key authorization. + zBytes := sha256.Sum256([]byte(keyAuth)) + + value, err := asn1.Marshal(zBytes[:sha256.Size]) + if err != nil { + return nil, nil, err + } + + // Add the keyAuth digest as the acmeValidation-v1 extension + // (marked as critical such that it won't be used by non-ACME software). + // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-3 + extensions := []pkix.Extension{ + { + Id: idPeAcmeIdentifierV1, + Critical: true, + Value: value, + }, + } + + // Generate a new RSA key for the certificates. + tempPrivKey, err := generatePrivateKey(RSA2048) + if err != nil { + return nil, nil, err + } + + rsaPrivKey := tempPrivKey.(*rsa.PrivateKey) + + // Generate the PEM certificate using the provided private key, domain, and extra extensions. + tempCertPEM, err := generatePemCert(rsaPrivKey, domain, extensions) + if err != nil { + return nil, nil, err + } + + // Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair. + rsaPrivPEM := pemEncode(rsaPrivKey) + + return tempCertPEM, rsaPrivPEM, nil +} + +// TLSALPNChallengeCert returns a certificate with the acmeValidation-v1 extension +// and domain name for the `tls-alpn-01` challenge. +func TLSALPNChallengeCert(domain, keyAuth string) (*tls.Certificate, error) { + tempCertPEM, rsaPrivPEM, err := TLSALPNChallengeBlocks(domain, keyAuth) + if err != nil { + return nil, err + } + + certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM) + if err != nil { + return nil, err + } + + return &certificate, nil +} diff --git a/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go b/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go new file mode 100644 index 000000000..8d33668e1 --- /dev/null +++ b/vendor/github.com/xenolf/lego/acme/tls_alpn_challenge_server.go @@ -0,0 +1,86 @@ +package acme + +import ( + "crypto/tls" + "fmt" + "net" + "net/http" +) + +const ( + // ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol. + ACMETLS1Protocol = "acme-tls/1" + + // defaultTLSPort is the port that the TLSALPNProviderServer will default to + // when no other port is provided. + defaultTLSPort = "443" +) + +// TLSALPNProviderServer implements ChallengeProvider for `TLS-ALPN-01` +// challenge. It may be instantiated without using the NewTLSALPNProviderServer +// if you want only to use the default values. +type TLSALPNProviderServer struct { + iface string + port string + listener net.Listener +} + +// NewTLSALPNProviderServer creates a new TLSALPNProviderServer on the selected +// interface and port. Setting iface and / or port to an empty string will make +// the server fall back to the "any" interface and port 443 respectively. +func NewTLSALPNProviderServer(iface, port string) *TLSALPNProviderServer { + return &TLSALPNProviderServer{iface: iface, port: port} +} + +// Present generates a certificate with a SHA-256 digest of the keyAuth provided +// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN +// spec. +func (t *TLSALPNProviderServer) Present(domain, token, keyAuth string) error { + if t.port == "" { + // Fallback to port 443 if the port was not provided. + t.port = defaultTLSPort + } + + // Generate the challenge certificate using the provided keyAuth and domain. + cert, err := TLSALPNChallengeCert(domain, keyAuth) + if err != nil { + return err + } + + // Place the generated certificate with the extension into the TLS config + // so that it can serve the correct details. + tlsConf := new(tls.Config) + tlsConf.Certificates = []tls.Certificate{*cert} + + // We must set that the `acme-tls/1` application level protocol is supported + // so that the protocol negotiation can succeed. Reference: + // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.2 + tlsConf.NextProtos = []string{ACMETLS1Protocol} + + // Create the listener with the created tls.Config. + t.listener, err = tls.Listen("tcp", net.JoinHostPort(t.iface, t.port), tlsConf) + if err != nil { + return fmt.Errorf("could not start HTTPS server for challenge -> %v", err) + } + + // Shut the server down when we're finished. + go func() { + http.Serve(t.listener, nil) + }() + + return nil +} + +// CleanUp closes the HTTPS server. +func (t *TLSALPNProviderServer) CleanUp(domain, token, keyAuth string) error { + if t.listener == nil { + return nil + } + + // Server was created, close it. + if err := t.listener.Close(); err != nil && err != http.ErrServerClosed { + return err + } + + return nil +} diff --git a/vendor/github.com/xenolf/lego/log/logger.go b/vendor/github.com/xenolf/lego/log/logger.go index 291541c0a..101a2c993 100644 --- a/vendor/github.com/xenolf/lego/log/logger.go +++ b/vendor/github.com/xenolf/lego/log/logger.go @@ -6,54 +6,44 @@ import ( ) // Logger is an optional custom logger. -var Logger *log.Logger +var Logger = log.New(os.Stdout, "", log.LstdFlags) // Fatal writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Fatal(args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stderr, "", log.LstdFlags) - } - Logger.Fatal(args...) } // Fatalf writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Fatalf(format string, args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stderr, "", log.LstdFlags) - } - Logger.Fatalf(format, args...) } // Print writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Print(args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stdout, "", log.LstdFlags) - } - Logger.Print(args...) } // Println writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Println(args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stdout, "", log.LstdFlags) - } - Logger.Println(args...) } // Printf writes a log entry. // It uses Logger if not nil, otherwise it uses the default log.Logger. func Printf(format string, args ...interface{}) { - if Logger == nil { - Logger = log.New(os.Stdout, "", log.LstdFlags) - } - Logger.Printf(format, args...) } + +// Warnf writes a log entry. +func Warnf(format string, args ...interface{}) { + Printf("[WARN] "+format, args...) +} + +// Infof writes a log entry. +func Infof(format string, args ...interface{}) { + Printf("[INFO] "+format, args...) +} diff --git a/vendor/github.com/xenolf/lego/platform/config/env/env.go b/vendor/github.com/xenolf/lego/platform/config/env/env.go new file mode 100644 index 000000000..1267def78 --- /dev/null +++ b/vendor/github.com/xenolf/lego/platform/config/env/env.go @@ -0,0 +1,27 @@ +package env + +import ( + "fmt" + "os" + "strings" +) + +// Get environment variables +func Get(names ...string) (map[string]string, error) { + values := map[string]string{} + + var missingEnvVars []string + for _, envVar := range names { + value := os.Getenv(envVar) + if value == "" { + missingEnvVars = append(missingEnvVars, envVar) + } + values[envVar] = value + } + + if len(missingEnvVars) > 0 { + return nil, fmt.Errorf("some credentials information are missing: %s", strings.Join(missingEnvVars, ",")) + } + + return values, nil +} 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 03d31754d..5c821507b 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go @@ -9,6 +9,7 @@ import ( "github.com/edeckers/auroradnsclient/records" "github.com/edeckers/auroradnsclient/zones" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider describes a provider for AuroraDNS @@ -22,20 +23,23 @@ type DNSProvider struct { // Credentials must be passed in the environment variables: AURORA_USER_ID // and AURORA_KEY. func NewDNSProvider() (*DNSProvider, error) { - userID := os.Getenv("AURORA_USER_ID") - key := os.Getenv("AURORA_KEY") - - endpoint := os.Getenv("AURORA_ENDPOINT") - if endpoint == "" { - endpoint = "https://api.auroradns.eu" + values, err := env.Get("AURORA_USER_ID", "AURORA_KEY") + if err != nil { + return nil, fmt.Errorf("AuroraDNS: %v", err) } - return NewDNSProviderCredentials(endpoint, userID, key) + endpoint := os.Getenv("AURORA_ENDPOINT") + + return NewDNSProviderCredentials(endpoint, values["AURORA_USER_ID"], values["AURORA_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a // DNSProvider instance configured for AuroraDNS. func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) { + if baseURL == "" { + baseURL = "https://api.auroradns.eu" + } + client, err := auroradnsclient.NewAuroraDNSClient(baseURL, userID, key) if err != nil { return nil, err @@ -47,8 +51,8 @@ func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSP }, nil } -func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { - zs, err := provider.client.GetZones() +func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) { + zs, err := d.client.GetZones() if err != nil { return zones.ZoneRecord{}, err @@ -64,12 +68,12 @@ func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRe } // Present creates a record with a secret -func (provider *DNSProvider) Present(domain, token, keyAuth string) error { +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("could not determine zone for domain: '%s'. %s", domain, err) } // 1. Aurora will happily create the TXT record when it is provided a fqdn, @@ -83,7 +87,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error { authZone = acme.UnFqdn(authZone) - zoneRecord, err := provider.getZoneInformationByName(authZone) + zoneRecord, err := d.getZoneInformationByName(authZone) if err != nil { return fmt.Errorf("could not create record: %v", err) } @@ -96,25 +100,25 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error { TTL: 300, } - respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData) + respData, err := d.client.CreateRecord(zoneRecord.ID, reqData) if err != nil { return fmt.Errorf("could not create record: %v", err) } - provider.recordIDsMu.Lock() - provider.recordIDs[fqdn] = respData.ID - provider.recordIDsMu.Unlock() + d.recordIDsMu.Lock() + d.recordIDs[fqdn] = respData.ID + d.recordIDsMu.Unlock() return nil } // CleanUp removes a given record that was generated by Present -func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - provider.recordIDsMu.Lock() - recordID, ok := provider.recordIDs[fqdn] - provider.recordIDsMu.Unlock() + d.recordIDsMu.Lock() + recordID, ok := d.recordIDs[fqdn] + d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown recordID for %q", fqdn) @@ -127,19 +131,19 @@ func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone = acme.UnFqdn(authZone) - zoneRecord, err := provider.getZoneInformationByName(authZone) + zoneRecord, err := d.getZoneInformationByName(authZone) if err != nil { return err } - _, err = provider.client.RemoveRecord(zoneRecord.ID, recordID) + _, err = d.client.RemoveRecord(zoneRecord.ID, recordID) if err != nil { return err } - provider.recordIDsMu.Lock() - delete(provider.recordIDs, fqdn) - provider.recordIDsMu.Unlock() + d.recordIDsMu.Lock() + delete(d.recordIDs, fqdn) + d.recordIDsMu.Unlock() return nil } 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 b26d8526c..bbf2c7fb9 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go +++ b/vendor/github.com/xenolf/lego/providers/dns/azure/azure.go @@ -5,8 +5,8 @@ package azure import ( "context" + "errors" "fmt" - "os" "strings" "time" @@ -16,6 +16,7 @@ import ( "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/to" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface @@ -32,25 +33,25 @@ type DNSProvider struct { // Credentials must be passed in the environment variables: AZURE_CLIENT_ID, // AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP func NewDNSProvider() (*DNSProvider, error) { - clientID := os.Getenv("AZURE_CLIENT_ID") - clientSecret := os.Getenv("AZURE_CLIENT_SECRET") - subscriptionID := os.Getenv("AZURE_SUBSCRIPTION_ID") - tenantID := os.Getenv("AZURE_TENANT_ID") - resourceGroup := os.Getenv("AZURE_RESOURCE_GROUP") - return NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup) + 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 NewDNSProviderCredentials( + values["AZURE_CLIENT_ID"], + values["AZURE_CLIENT_SECRET"], + values["AZURE_SUBSCRIPTION_ID"], + values["AZURE_TENANT_ID"], + values["AZURE_RESOURCE_GROUP"], + ) } // NewDNSProviderCredentials uses the supplied credentials to return a // DNSProvider instance configured for azure. func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup string) (*DNSProvider, error) { if clientID == "" || clientSecret == "" || subscriptionID == "" || tenantID == "" || resourceGroup == "" { - var missingEnvVars []string - for _, envVar := range []string{"AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID", "AZURE_TENANT_ID", "AZURE_RESOURCE_GROUP"} { - if os.Getenv(envVar) == "" { - missingEnvVars = append(missingEnvVars, envVar) - } - } - return nil, fmt.Errorf("Azure configuration missing: %s", strings.Join(missingEnvVars, ",")) + return nil, errors.New("Azure: some credentials information are missing") } return &DNSProvider{ @@ -66,20 +67,20 @@ func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZoneID(fqdn) + zone, err := d.getHostedZoneID(fqdn) if err != nil { return err } - rsc := dns.NewRecordSetsClient(c.subscriptionID) - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + rsc := dns.NewRecordSetsClient(d.subscriptionID) + spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } @@ -95,7 +96,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { }, } - _, err = rsc.CreateOrUpdate(c.context, c.resourceGroup, zone, relative, dns.TXT, rec, "", "") + _, err = rsc.CreateOrUpdate(d.context, d.resourceGroup, zone, relative, dns.TXT, rec, "", "") return err } @@ -105,44 +106,44 @@ func toRelativeRecord(domain, zone string) string { } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZoneID(fqdn) + zone, err := d.getHostedZoneID(fqdn) if err != nil { return err } relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) - rsc := dns.NewRecordSetsClient(c.subscriptionID) - spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + rsc := dns.NewRecordSetsClient(d.subscriptionID) + spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return err } rsc.Authorizer = autorest.NewBearerAuthorizer(spt) - _, err = rsc.Delete(c.context, c.resourceGroup, zone, relative, dns.TXT, "") + _, err = rsc.Delete(d.context, d.resourceGroup, zone, relative, dns.TXT, "") return err } // Checks that azure has a zone for this domain name. -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(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 := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) + spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) if err != nil { return "", err } - dc := dns.NewZonesClient(c.subscriptionID) + dc := dns.NewZonesClient(d.subscriptionID) dc.Authorizer = autorest.NewBearerAuthorizer(spt) - zone, err := dc.Get(c.context, c.resourceGroup, acme.UnFqdn(authZone)) + zone, err := dc.Get(d.context, d.resourceGroup, acme.UnFqdn(authZone)) if err != nil { return "", err } @@ -153,10 +154,10 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the // passed credentials map. -func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantID) +func (d *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) { + oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, d.tenantID) if err != nil { return nil, err } - return adal.NewServicePrincipalToken(*oauthConfig, c.clientID, c.clientSecret, scope) + return adal.NewServicePrincipalToken(*oauthConfig, d.clientID, d.clientSecret, scope) } 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 b0374b7e6..d88d73b1c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go +++ b/vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go @@ -6,16 +6,15 @@ import ( "bytes" "encoding/json" "fmt" + "io/ioutil" "net/http" - "os" "regexp" "strconv" "strings" "time" - "io/ioutil" - "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) const bluecatURLTemplate = "%s/Services/REST/v1" @@ -40,7 +39,7 @@ type DNSProvider struct { configName string dnsView string token string - httpClient *http.Client + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS. @@ -51,29 +50,42 @@ type DNSProvider struct { // and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and // BLUECAT_DNS_VIEW func NewDNSProvider() (*DNSProvider, error) { - server := os.Getenv("BLUECAT_SERVER_URL") - userName := os.Getenv("BLUECAT_USER_NAME") - password := os.Getenv("BLUECAT_PASSWORD") - configName := os.Getenv("BLUECAT_CONFIG_NAME") - dnsView := os.Getenv("BLUECAT_DNS_VIEW") - httpClient := http.Client{Timeout: 30 * time.Second} - return NewDNSProviderCredentials(server, userName, password, configName, dnsView, httpClient) + 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) + } + + httpClient := &http.Client{Timeout: 30 * time.Second} + + return NewDNSProviderCredentials( + values["BLUECAT_SERVER_URL"], + values["BLUECAT_USER_NAME"], + values["BLUECAT_PASSWORD"], + values["BLUECAT_CONFIG_NAME"], + values["BLUECAT_DNS_VIEW"], + httpClient, + ) } // 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) { +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") } + client := http.DefaultClient + if httpClient != nil { + client = httpClient + } + return &DNSProvider{ baseURL: fmt.Sprintf(bluecatURLTemplate, server), userName: userName, password: password, configName: configName, dnsView: dnsView, - httpClient: http.DefaultClient, + client: client, }, nil } @@ -102,7 +114,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, q.Add(argName, argVal) } req.URL.RawQuery = q.Encode() - resp, err := d.httpClient.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -125,13 +137,16 @@ func (d *DNSProvider) login() error { "password": d.password, } - resp, err := d.sendRequest("GET", "login", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "login", nil, queryArgs) if err != nil { return err } defer resp.Body.Close() - authBytes, _ := ioutil.ReadAll(resp.Body) + authBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } authResp := string(authBytes) if strings.Contains(authResp, "Authentication Error") { @@ -152,7 +167,7 @@ func (d *DNSProvider) logout() error { return nil } - resp, err := d.sendRequest("GET", "logout", nil, nil) + resp, err := d.sendRequest(http.MethodGet, "logout", nil, nil) if err != nil { return err } @@ -162,7 +177,10 @@ func (d *DNSProvider) logout() error { return fmt.Errorf("Bluecat API request failed to delete session with HTTP status code %d", resp.StatusCode) } - authBytes, _ := ioutil.ReadAll(resp.Body) + authBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } authResp := string(authBytes) if !strings.Contains(authResp, "successfully") { @@ -183,7 +201,7 @@ func (d *DNSProvider) lookupConfID() (uint, error) { "type": configType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) if err != nil { return 0, err } @@ -210,7 +228,7 @@ func (d *DNSProvider) lookupViewID(viewName string) (uint, error) { "type": viewType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) if err != nil { return 0, err } @@ -259,7 +277,7 @@ func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) { "type": zoneType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + 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) @@ -309,8 +327,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", ttl, fqdn, value), } - resp, err := d.sendRequest("POST", "addEntity", body, queryArgs) - + resp, err := d.sendRequest(http.MethodPost, "addEntity", body, queryArgs) if err != nil { return err } @@ -338,8 +355,7 @@ func (d *DNSProvider) deploy(entityID uint) error { "entityId": strconv.FormatUint(uint64(entityID), 10), } - resp, err := d.sendRequest("POST", "quickDeploy", nil, queryArgs) - + resp, err := d.sendRequest(http.MethodPost, "quickDeploy", nil, queryArgs) if err != nil { return err } @@ -373,7 +389,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { "type": txtType, } - resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs) + resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs) if err != nil { return err } @@ -388,7 +404,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { "objectId": strconv.FormatUint(uint64(txtRec.ID), 10), } - resp, err = d.sendRequest("DELETE", "delete", nil, queryArgs) + resp, err = d.sendRequest(http.MethodDelete, http.MethodDelete, nil, queryArgs) if err != nil { return err } 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 9b6556a64..7d8979ef5 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go @@ -5,15 +5,15 @@ package cloudflare import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" "net/http" - "os" - "strings" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // CloudFlareAPIURL represents the API endpoint to call. @@ -24,47 +24,45 @@ const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" type DNSProvider struct { authEmail string authKey string + client *http.Client } // 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) { - email := os.Getenv("CLOUDFLARE_EMAIL") - key := os.Getenv("CLOUDFLARE_API_KEY") - return NewDNSProviderCredentials(email, key) + values, err := env.Get("CLOUDFLARE_EMAIL", "CLOUDFLARE_API_KEY") + if err != nil { + return nil, fmt.Errorf("CloudFlare: %v", err) + } + + return NewDNSProviderCredentials(values["CLOUDFLARE_EMAIL"], values["CLOUDFLARE_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a // DNSProvider instance configured for cloudflare. func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) { if email == "" || key == "" { - missingEnvVars := []string{} - if email == "" { - missingEnvVars = append(missingEnvVars, "CLOUDFLARE_EMAIL") - } - if key == "" { - missingEnvVars = append(missingEnvVars, "CLOUDFLARE_API_KEY") - } - return nil, fmt.Errorf("CloudFlare credentials missing: %s", strings.Join(missingEnvVars, ",")) + return nil, errors.New("CloudFlare: some credentials information are missing") } return &DNSProvider{ authEmail: email, authKey: key, + client: &http.Client{Timeout: 30 * time.Second}, }, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { - fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) +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 } @@ -73,7 +71,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { Type: "TXT", Name: acme.UnFqdn(fqdn), Content: value, - TTL: 120, + TTL: ttl, } body, err := json.Marshal(rec) @@ -81,24 +79,24 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("POST", fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) + _, err = d.doRequest(http.MethodPost, fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) return err } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - record, err := c.findTxtRecord(fqdn) + record, err := d.findTxtRecord(fqdn) if err != nil { return err } - _, err = c.makeRequest("DELETE", fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) + _, err = d.doRequest(http.MethodDelete, fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) return err } -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { // HostedZone represents a CloudFlare DNS zone type HostedZone struct { ID string `json:"id"` @@ -110,7 +108,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return "", err } - result, err := c.makeRequest("GET", "/zones?name="+acme.UnFqdn(authZone), nil) + result, err := d.doRequest(http.MethodGet, "/zones?name="+acme.UnFqdn(authZone), nil) if err != nil { return "", err } @@ -122,20 +120,20 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { } if len(hostedZone) != 1 { - return "", fmt.Errorf("Zone %s not found in CloudFlare for domain %s", authZone, fqdn) + return "", fmt.Errorf("zone %s not found in CloudFlare for domain %s", authZone, fqdn) } return hostedZone[0].ID, nil } -func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { - zoneID, err := c.getHostedZoneID(fqdn) +func (d *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return nil, err } - result, err := c.makeRequest( - "GET", + result, err := d.doRequest( + http.MethodGet, fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil, ) @@ -158,33 +156,18 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { return nil, fmt.Errorf("no existing record found for %s", fqdn) } -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - // 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"` - } - +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", c.authEmail) - req.Header.Set("X-Auth-Key", c.authKey) + req.Header.Set("X-Auth-Email", d.authEmail) + req.Header.Set("X-Auth-Key", d.authKey) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { - return nil, fmt.Errorf("Error querying Cloudflare API -> %v", err) + return nil, fmt.Errorf("error querying Cloudflare API -> %v", err) } defer resp.Body.Close() @@ -216,6 +199,20 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM 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"` 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 1204af149..ae819f12c 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go @@ -9,11 +9,11 @@ import ( "encoding/json" "fmt" "net/http" - "os" "strconv" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) const cloudXNSBaseURL = "https://www.cloudxns.net/api2/" @@ -28,9 +28,12 @@ type DNSProvider struct { // Credentials must be passed in the environment variables: CLOUDXNS_API_KEY // and CLOUDXNS_SECRET_KEY. func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("CLOUDXNS_API_KEY") - secretKey := os.Getenv("CLOUDXNS_SECRET_KEY") - return NewDNSProviderCredentials(apiKey, secretKey) + values, err := env.Get("CLOUDXNS_API_KEY", "CLOUDXNS_SECRET_KEY") + if err != nil { + return nil, fmt.Errorf("CloudXNS: %v", err) + } + + return NewDNSProviderCredentials(values["CLOUDXNS_API_KEY"], values["CLOUDXNS_SECRET_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -47,33 +50,33 @@ func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } - return c.addTxtRecord(zoneID, fqdn, value, ttl) + return d.addTxtRecord(zoneID, fqdn, value, ttl) } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } - recordID, err := c.findTxtRecord(zoneID, fqdn) + recordID, err := d.findTxtRecord(zoneID, fqdn) if err != nil { return err } - return c.delTxtRecord(recordID, zoneID) + return d.delTxtRecord(recordID, zoneID) } -func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) { type Data struct { ID string `json:"id"` Domain string `json:"domain"` @@ -84,7 +87,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return "", err } - result, err := c.makeRequest("GET", "domain", nil) + result, err := d.makeRequest(http.MethodGet, "domain", nil) if err != nil { return "", err } @@ -104,8 +107,8 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) { return "", fmt.Errorf("zone %s not found in cloudxns for domain %s", authZone, fqdn) } -func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { - result, err := c.makeRequest("GET", fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil) +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 } @@ -125,7 +128,7 @@ func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) { return "", fmt.Errorf("no existing record found for %s", fqdn) } -func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { +func (d *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { id, err := strconv.Atoi(zoneID) if err != nil { return err @@ -145,21 +148,21 @@ func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error { return err } - _, err = c.makeRequest("POST", "record", body) + _, err = d.makeRequest(http.MethodPost, "record", body) return err } -func (c *DNSProvider) delTxtRecord(recordID, zoneID string) error { - _, err := c.makeRequest("DELETE", fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) +func (d *DNSProvider) delTxtRecord(recordID, zoneID string) error { + _, err := d.makeRequest(http.MethodDelete, fmt.Sprintf("record/%s/%s", recordID, zoneID), nil) return err } -func (c *DNSProvider) hmac(url, date, body string) string { - sum := md5.Sum([]byte(c.apiKey + url + body + date + c.secretKey)) +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 (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) { +func (d *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) { type APIResponse struct { Code int `json:"code"` Message string `json:"message"` @@ -174,9 +177,9 @@ func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMess requestDate := time.Now().Format(time.RFC1123Z) - req.Header.Set("API-KEY", c.apiKey) + req.Header.Set("API-KEY", d.apiKey) req.Header.Set("API-REQUEST-DATE", requestDate) - req.Header.Set("API-HMAC", c.hmac(url, requestDate, string(body))) + req.Header.Set("API-HMAC", d.hmac(url, requestDate, string(body))) req.Header.Set("API-FORMAT", "json") resp, err := acme.HTTPClient.Do(req) 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 ef749e6fa..e4247046a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go +++ b/vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go @@ -7,11 +7,11 @@ import ( "encoding/json" "fmt" "net/http" - "os" "sync" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface @@ -20,20 +20,19 @@ type DNSProvider struct { apiAuthToken string recordIDs map[string]int recordIDsMu sync.Mutex -} - -// 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 + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Digital // Ocean. Credentials must be passed in the environment variable: // DO_AUTH_TOKEN. func NewDNSProvider() (*DNSProvider, error) { - apiAuthToken := os.Getenv("DO_AUTH_TOKEN") - return NewDNSProviderCredentials(apiAuthToken) + values, err := env.Get("DO_AUTH_TOKEN") + if err != nil { + return nil, fmt.Errorf("DigitalOcean: %v", err) + } + + return NewDNSProviderCredentials(values["DO_AUTH_TOKEN"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -45,9 +44,16 @@ func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) { return &DNSProvider{ apiAuthToken: apiAuthToken, recordIDs: make(map[string]int), + client: &http.Client{Timeout: 30 * time.Second}, }, 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 60 * time.Second, 5 * time.Second +} + // Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) @@ -66,15 +72,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return err } - req, err := http.NewRequest("POST", reqURL, bytes.NewReader(body)) + 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)) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } @@ -119,15 +124,15 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID) - req, err := http.NewRequest("DELETE", reqURL, nil) + 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)) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } 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 d00028617..96881c19d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go @@ -27,6 +27,7 @@ import ( "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/nifcloud" "github.com/xenolf/lego/providers/dns/ns1" "github.com/xenolf/lego/providers/dns/otc" "github.com/xenolf/lego/providers/dns/ovh" @@ -34,80 +35,85 @@ import ( "github.com/xenolf/lego/providers/dns/rackspace" "github.com/xenolf/lego/providers/dns/rfc2136" "github.com/xenolf/lego/providers/dns/route53" + "github.com/xenolf/lego/providers/dns/sakuracloud" + "github.com/xenolf/lego/providers/dns/vegadns" "github.com/xenolf/lego/providers/dns/vultr" ) // NewDNSChallengeProviderByName Factory for DNS providers func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) { - var err error - var provider acme.ChallengeProvider switch name { case "azure": - provider, err = azure.NewDNSProvider() + return azure.NewDNSProvider() case "auroradns": - provider, err = auroradns.NewDNSProvider() + return auroradns.NewDNSProvider() case "bluecat": - provider, err = bluecat.NewDNSProvider() + return bluecat.NewDNSProvider() case "cloudflare": - provider, err = cloudflare.NewDNSProvider() + return cloudflare.NewDNSProvider() case "cloudxns": - provider, err = cloudxns.NewDNSProvider() + return cloudxns.NewDNSProvider() case "digitalocean": - provider, err = digitalocean.NewDNSProvider() + return digitalocean.NewDNSProvider() case "dnsimple": - provider, err = dnsimple.NewDNSProvider() + return dnsimple.NewDNSProvider() case "dnsmadeeasy": - provider, err = dnsmadeeasy.NewDNSProvider() + return dnsmadeeasy.NewDNSProvider() case "dnspod": - provider, err = dnspod.NewDNSProvider() + return dnspod.NewDNSProvider() case "duckdns": - provider, err = duckdns.NewDNSProvider() + return duckdns.NewDNSProvider() case "dyn": - provider, err = dyn.NewDNSProvider() + return dyn.NewDNSProvider() case "fastdns": - provider, err = fastdns.NewDNSProvider() + return fastdns.NewDNSProvider() case "exoscale": - provider, err = exoscale.NewDNSProvider() + return exoscale.NewDNSProvider() case "gandi": - provider, err = gandi.NewDNSProvider() + return gandi.NewDNSProvider() case "gandiv5": - provider, err = gandiv5.NewDNSProvider() + return gandiv5.NewDNSProvider() case "glesys": - provider, err = glesys.NewDNSProvider() + return glesys.NewDNSProvider() case "gcloud": - provider, err = gcloud.NewDNSProvider() + return gcloud.NewDNSProvider() case "godaddy": - provider, err = godaddy.NewDNSProvider() + return godaddy.NewDNSProvider() case "lightsail": - provider, err = lightsail.NewDNSProvider() + return lightsail.NewDNSProvider() case "linode": - provider, err = linode.NewDNSProvider() + return linode.NewDNSProvider() case "manual": - provider, err = acme.NewDNSProviderManual() + return acme.NewDNSProviderManual() case "namecheap": - provider, err = namecheap.NewDNSProvider() + return namecheap.NewDNSProvider() case "namedotcom": - provider, err = namedotcom.NewDNSProvider() + return namedotcom.NewDNSProvider() + case "nifcloud": + return nifcloud.NewDNSProvider() case "rackspace": - provider, err = rackspace.NewDNSProvider() + return rackspace.NewDNSProvider() case "route53": - provider, err = route53.NewDNSProvider() + return route53.NewDNSProvider() case "rfc2136": - provider, err = rfc2136.NewDNSProvider() + return rfc2136.NewDNSProvider() + case "sakuracloud": + return sakuracloud.NewDNSProvider() case "vultr": - provider, err = vultr.NewDNSProvider() + return vultr.NewDNSProvider() case "ovh": - provider, err = ovh.NewDNSProvider() + return ovh.NewDNSProvider() case "pdns": - provider, err = pdns.NewDNSProvider() + return pdns.NewDNSProvider() case "ns1": - provider, err = ns1.NewDNSProvider() + return ns1.NewDNSProvider() case "otc": - provider, err = otc.NewDNSProvider() + return otc.NewDNSProvider() case "exec": - provider, err = exec.NewDNSProvider() + return exec.NewDNSProvider() + case "vegadns": + return vegadns.NewDNSProvider() default: - err = fmt.Errorf("unrecognised DNS provider: %s", name) + return nil, fmt.Errorf("unrecognised DNS provider: %s", name) } - return provider, err } 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 30c7fc2f6..a9f8424ad 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go @@ -46,22 +46,22 @@ func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneName, err := c.getHostedZone(domain) + zoneName, err := d.getHostedZone(domain) if err != nil { return err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return err } - recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) - _, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + _, err = d.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes) if err != nil { return fmt.Errorf("DNSimple API call failed: %v", err) } @@ -70,21 +70,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - records, err := c.findTxtRecords(domain, fqdn) + records, err := d.findTxtRecords(domain, fqdn) if err != nil { return err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return err } for _, rec := range records { - _, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID) + _, err := d.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID) if err != nil { return err } @@ -93,20 +93,20 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getHostedZone(domain string) (string, error) { +func (d *DNSProvider) getHostedZone(domain string) (string, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return "", err } zoneName := acme.UnFqdn(authZone) - zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) + zones, err := d.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName}) if err != nil { return "", fmt.Errorf("DNSimple API call failed: %v", err) } @@ -125,20 +125,20 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) { return hostedZone.Name, nil } -func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) { - zoneName, err := c.getHostedZone(domain) +func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) { + zoneName, err := d.getHostedZone(domain) if err != nil { return nil, err } - accountID, err := c.getAccountID() + accountID, err := d.getAccountID() if err != nil { return nil, err } - recordName := c.extractRecordName(fqdn, zoneName) + recordName := d.extractRecordName(fqdn, zoneName) - result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}}) + 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) } @@ -146,8 +146,8 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord return result.Data, nil } -func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord { - name := c.extractRecordName(fqdn, zoneName) +func (d *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord { + name := d.extractRecordName(fqdn, zoneName) return &dnsimple.ZoneRecord{ Type: "TXT", @@ -157,7 +157,7 @@ func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsim } } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] @@ -165,8 +165,8 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string { return name } -func (c *DNSProvider) getAccountID() (string, error) { - whoamiResponse, err := c.client.Identity.Whoami() +func (d *DNSProvider) getAccountID() (string, error) { + whoamiResponse, err := d.client.Identity.Whoami() if err != nil { return "", err } 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 11b1ac44d..9f7f31a9a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go @@ -15,6 +15,7 @@ import ( "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses @@ -23,6 +24,7 @@ type DNSProvider struct { baseURL string apiKey string apiSecret string + client *http.Client } // Domain holds the DNSMadeEasy API representation of a Domain @@ -45,20 +47,19 @@ type Record struct { // Credentials must be passed in the environment variables: DNSMADEEASY_API_KEY // and DNSMADEEASY_API_SECRET. func NewDNSProvider() (*DNSProvider, error) { - dnsmadeeasyAPIKey := os.Getenv("DNSMADEEASY_API_KEY") - dnsmadeeasyAPISecret := os.Getenv("DNSMADEEASY_API_SECRET") - dnsmadeeasySandbox := os.Getenv("DNSMADEEASY_SANDBOX") + values, err := env.Get("DNSMADEEASY_API_KEY", "DNSMADEEASY_API_SECRET") + if err != nil { + return nil, fmt.Errorf("DNSMadeEasy: %v", err) + } var baseURL string - - sandbox, _ := strconv.ParseBool(dnsmadeeasySandbox) - if sandbox { + if sandbox, _ := strconv.ParseBool(os.Getenv("DNSMADEEASY_SANDBOX")); sandbox { baseURL = "https://api.sandbox.dnsmadeeasy.com/V2.0" } else { baseURL = "https://api.dnsmadeeasy.com/V2.0" } - return NewDNSProviderCredentials(baseURL, dnsmadeeasyAPIKey, dnsmadeeasyAPISecret) + return NewDNSProviderCredentials(baseURL, values["DNSMADEEASY_API_KEY"], values["DNSMADEEASY_API_SECRET"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -68,10 +69,19 @@ func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider, return nil, fmt.Errorf("DNS Made Easy credentials missing") } + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{ + Transport: transport, + Timeout: 10 * time.Second, + } + return &DNSProvider{ baseURL: baseURL, apiKey: apiKey, apiSecret: apiSecret, + client: client, }, nil } @@ -135,7 +145,7 @@ 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("GET", resource, nil) + resp, err := d.sendRequest(http.MethodGet, resource, nil) if err != nil { return nil, err } @@ -153,7 +163,7 @@ func (d *DNSProvider) getDomain(authZone string) (*Domain, error) { 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("GET", resource, nil) + resp, err := d.sendRequest(http.MethodGet, resource, nil) if err != nil { return nil, err } @@ -175,7 +185,7 @@ func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) func (d *DNSProvider) createRecord(domain *Domain, record *Record) error { url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records") - resp, err := d.sendRequest("POST", url, record) + resp, err := d.sendRequest(http.MethodPost, url, record) if err != nil { return err } @@ -187,7 +197,7 @@ func (d *DNSProvider) createRecord(domain *Domain, record *Record) error { 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("DELETE", resource, nil) + resp, err := d.sendRequest(http.MethodDelete, resource, nil) if err != nil { return err } @@ -217,14 +227,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) req.Header.Set("accept", "application/json") req.Header.Set("content-type", "application/json") - transport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := &http.Client{ - Transport: transport, - Timeout: 10 * time.Second, - } - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } 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 0ce08a8bb..e42e36338 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go @@ -4,11 +4,11 @@ package dnspod import ( "fmt" - "os" "strings" "github.com/decker502/dnspod-go" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface. @@ -19,8 +19,12 @@ type DNSProvider struct { // NewDNSProvider returns a DNSProvider instance configured for dnspod. // Credentials must be passed in the environment variables: DNSPOD_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("DNSPOD_API_KEY") - return NewDNSProviderCredentials(key) + values, err := env.Get("DNSPOD_API_KEY") + if err != nil { + return nil, fmt.Errorf("DNSPod: %v", err) + } + + return NewDNSProviderCredentials(values["DNSPOD_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -37,15 +41,15 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneID, zoneName, err := c.getHostedZone(domain) + zoneID, zoneName, err := d.getHostedZone(domain) if err != nil { return err } - recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) - _, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes) + recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl) + _, _, err = d.client.Domains.CreateRecord(zoneID, *recordAttributes) if err != nil { return fmt.Errorf("dnspod API call failed: %v", err) } @@ -54,21 +58,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - records, err := c.findTxtRecords(domain, fqdn) + records, err := d.findTxtRecords(domain, fqdn) if err != nil { return err } - zoneID, _, err := c.getHostedZone(domain) + zoneID, _, err := d.getHostedZone(domain) if err != nil { return err } for _, rec := range records { - _, err := c.client.Domains.DeleteRecord(zoneID, rec.ID) + _, err := d.client.Domains.DeleteRecord(zoneID, rec.ID) if err != nil { return err } @@ -76,8 +80,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { - zones, _, err := c.client.Domains.List() +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) } @@ -95,15 +99,15 @@ func (c *DNSProvider) getHostedZone(domain string) (string, string, error) { } if hostedZone.ID == 0 { - return "", "", fmt.Errorf("Zone %s not found in dnspod for domain %s", authZone, domain) + return "", "", fmt.Errorf("zone %s not found in dnspod for domain %s", authZone, domain) } return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil } -func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record { - name := c.extractRecordName(fqdn, zone) +func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record { + name := d.extractRecordName(fqdn, zone) return &dnspod.Record{ Type: "TXT", @@ -114,19 +118,19 @@ func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Re } } -func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) { - zoneID, zoneName, err := c.getHostedZone(domain) +func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) { + zoneID, zoneName, err := d.getHostedZone(domain) if err != nil { return nil, err } var records []dnspod.Record - result, _, err := c.client.Domains.ListRecords(zoneID, "") + result, _, err := d.client.Domains.ListRecords(zoneID, "") if err != nil { return records, fmt.Errorf("dnspod API call has failed: %v", err) } - recordName := c.extractRecordName(fqdn, zoneName) + recordName := d.extractRecordName(fqdn, zoneName) for _, record := range result { if record.Name == recordName { @@ -137,7 +141,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, erro return records, nil } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] 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 855838ae6..e00f71370 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go @@ -1,4 +1,4 @@ -// Package duckdns Adds lego support for http://duckdns.org . +// Package duckdns Adds lego support for http://duckdns.org. // See http://www.duckdns.org/spec.jsp for more info on updating TXT records. package duckdns @@ -6,30 +6,33 @@ import ( "errors" "fmt" "io/ioutil" - "os" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider adds and removes the record for the DNS challenge type DNSProvider struct { - // The duckdns api token + // The api token token string } // NewDNSProvider returns a new DNS provider using // environment variable DUCKDNS_TOKEN for adding and removing the DNS record. func NewDNSProvider() (*DNSProvider, error) { - duckdnsToken := os.Getenv("DUCKDNS_TOKEN") + values, err := env.Get("DUCKDNS_TOKEN") + if err != nil { + return nil, fmt.Errorf("DuckDNS: %v", err) + } - return NewDNSProviderCredentials(duckdnsToken) + return NewDNSProviderCredentials(values["DUCKDNS_TOKEN"]) } // NewDNSProviderCredentials uses the supplied credentials to return a // DNSProvider instance configured for http://duckdns.org . func NewDNSProviderCredentials(duckdnsToken string) (*DNSProvider, error) { if duckdnsToken == "" { - return nil, errors.New("environment variable DUCKDNS_TOKEN not set") + return nil, errors.New("DuckDNS: credentials missing") } return &DNSProvider{token: duckdnsToken}, nil 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 ad65fab61..187b1b48d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go +++ b/vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go @@ -7,11 +7,11 @@ import ( "encoding/json" "fmt" "net/http" - "os" "strconv" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) var dynBaseURL = "https://api.dynect.net/REST" @@ -37,16 +37,19 @@ type DNSProvider struct { userName string password string token string + client *http.Client } // 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. func NewDNSProvider() (*DNSProvider, error) { - customerName := os.Getenv("DYN_CUSTOMER_NAME") - userName := os.Getenv("DYN_USER_NAME") - password := os.Getenv("DYN_PASSWORD") - return NewDNSProviderCredentials(customerName, userName, password) + values, err := env.Get("DYN_CUSTOMER_NAME", "DYN_USER_NAME", "DYN_PASSWORD") + if err != nil { + return nil, fmt.Errorf("DynDNS: %v", err) + } + + return NewDNSProviderCredentials(values["DYN_CUSTOMER_NAME"], values["DYN_USER_NAME"], values["DYN_PASSWORD"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -60,6 +63,7 @@ func NewDNSProviderCredentials(customerName, userName, password string) (*DNSPro customerName: customerName, userName: userName, password: password, + client: &http.Client{Timeout: 10 * time.Second}, }, nil } @@ -80,8 +84,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{}) req.Header.Set("Auth-Token", d.token) } - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -127,7 +130,7 @@ func (d *DNSProvider) login() error { } payload := &creds{Customer: d.customerName, User: d.userName, Pass: d.password} - dynRes, err := d.sendRequest("POST", "Session", payload) + dynRes, err := d.sendRequest(http.MethodPost, "Session", payload) if err != nil { return err } @@ -151,15 +154,14 @@ func (d *DNSProvider) logout() error { } url := fmt.Sprintf("%s/Session", dynBaseURL) - req, err := http.NewRequest("DELETE", url, nil) + 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) - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } @@ -196,7 +198,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) - _, err = d.sendRequest("POST", resource, data) + _, err = d.sendRequest(http.MethodPost, resource, data) if err != nil { return err } @@ -218,7 +220,7 @@ func (d *DNSProvider) publish(zone, notes string) error { pub := &publish{Publish: true, Notes: notes} resource := fmt.Sprintf("Zone/%s/", zone) - _, err := d.sendRequest("PUT", resource, pub) + _, err := d.sendRequest(http.MethodPut, resource, pub) return err } @@ -238,15 +240,16 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) url := fmt.Sprintf("%s/%s", dynBaseURL, resource) - req, err := http.NewRequest("DELETE", url, nil) + + 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) - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return err } diff --git a/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go b/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go index ee140ae76..9bd97d03e 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exec/exec.go @@ -48,7 +48,13 @@ func NewDNSProvider() (*DNSProvider, error) { return nil, errors.New("environment variable EXEC_PATH not set") } - return &DNSProvider{program: s}, nil + return NewDNSProviderProgram(s) +} + +// NewDNSProviderProgram returns a new DNS provider which runs the given program +// for adding and removing the DNS record. +func NewDNSProviderProgram(program string) (*DNSProvider, error) { + return &DNSProvider{program: program}, nil } // Present creates a TXT record to fulfil the dns-01 challenge. 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 1467e8d75..c5b26d1dc 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go +++ b/vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go @@ -9,6 +9,7 @@ import ( "github.com/exoscale/egoscale" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface. @@ -19,10 +20,13 @@ type DNSProvider struct { // NewDNSProvider Credentials must be passed in the environment variables: // EXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT. func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("EXOSCALE_API_KEY") - secret := os.Getenv("EXOSCALE_API_SECRET") + values, err := env.Get("EXOSCALE_API_KEY", "EXOSCALE_API_SECRET") + if err != nil { + return nil, fmt.Errorf("Exoscale: %v", err) + } + endpoint := os.Getenv("EXOSCALE_ENDPOINT") - return NewDNSProviderClient(key, secret, endpoint) + return NewDNSProviderClient(values["EXOSCALE_API_KEY"], values["EXOSCALE_API_SECRET"], endpoint) } // NewDNSProviderClient Uses the supplied parameters to return a DNSProvider instance @@ -31,6 +35,7 @@ func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) { if key == "" || secret == "" { return nil, fmt.Errorf("Exoscale credentials missing") } + if endpoint == "" { endpoint = "https://api.exoscale.ch/dns" } @@ -41,14 +46,14 @@ func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) + zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err } - recordID, err := c.FindExistingRecordID(zone, recordName) + recordID, err := d.FindExistingRecordID(zone, recordName) if err != nil { return err } @@ -61,13 +66,13 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } if recordID == 0 { - _, err := c.client.CreateRecord(zone, record) + _, err := d.client.CreateRecord(zone, record) if err != nil { return errors.New("Error while creating DNS record: " + err.Error()) } } else { record.ID = recordID - _, err := c.client.UpdateRecord(zone, record) + _, err := d.client.UpdateRecord(zone, record) if err != nil { return errors.New("Error while updating DNS record: " + err.Error()) } @@ -77,20 +82,20 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) + zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain) if err != nil { return err } - recordID, err := c.FindExistingRecordID(zone, recordName) + recordID, err := d.FindExistingRecordID(zone, recordName) if err != nil { return err } if recordID != 0 { - err = c.client.DeleteRecord(zone, recordID) + err = d.client.DeleteRecord(zone, recordID) if err != nil { return errors.New("Error while deleting DNS record: " + err.Error()) } @@ -101,8 +106,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { // FindExistingRecordID Query Exoscale to find an existing record for this name. // Returns nil if no record could be found -func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) { - records, err := c.client.GetRecords(zone) +func (d *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) { + records, err := d.client.GetRecords(zone) if err != nil { return -1, errors.New("Error while retrievening DNS records: " + err.Error()) } @@ -115,7 +120,7 @@ func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, erro } // FindZoneAndRecordName Extract DNS zone and DNS entry name -func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { +func (d *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) { zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", "", err 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 dcbb93e5b..4bc794007 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go @@ -2,12 +2,12 @@ package fastdns import ( "fmt" - "os" "reflect" configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1" "github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface. @@ -18,19 +18,24 @@ type DNSProvider struct { // NewDNSProvider uses the supplied environment variables to return a DNSProvider instance: // AKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN func NewDNSProvider() (*DNSProvider, error) { - host := os.Getenv("AKAMAI_HOST") - clientToken := os.Getenv("AKAMAI_CLIENT_TOKEN") - clientSecret := os.Getenv("AKAMAI_CLIENT_SECRET") - accessToken := os.Getenv("AKAMAI_ACCESS_TOKEN") + 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 NewDNSProviderClient(host, clientToken, clientSecret, accessToken) + return NewDNSProviderClient( + values["AKAMAI_HOST"], + values["AKAMAI_CLIENT_TOKEN"], + values["AKAMAI_CLIENT_SECRET"], + values["AKAMAI_ACCESS_TOKEN"], + ) } // NewDNSProviderClient uses the supplied parameters to return a DNSProvider instance // configured for FastDNS. func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (*DNSProvider, error) { if clientToken == "" || clientSecret == "" || accessToken == "" || host == "" { - return nil, fmt.Errorf("Akamai FastDNS credentials missing") + return nil, fmt.Errorf("FastDNS credentials are missing") } config := edgegrid.Config{ Host: host, @@ -46,14 +51,14 @@ func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) ( } // Present creates a TXT record to fullfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain) + zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { return err } - configdns.Init(c.config) + configdns.Init(d.config) zone, err := configdns.GetZone(zoneName) if err != nil { @@ -66,35 +71,35 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { record.SetField("target", value) record.SetField("active", true) - existingRecord := c.findExistingRecord(zone, recordName) + existingRecord := d.findExistingRecord(zone, recordName) if existingRecord != nil { if reflect.DeepEqual(existingRecord.ToMap(), record.ToMap()) { return nil } zone.RemoveRecord(existingRecord) - return c.createRecord(zone, record) + return d.createRecord(zone, record) } - return c.createRecord(zone, record) + return d.createRecord(zone, record) } // CleanUp removes the record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain) + zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain) if err != nil { return err } - configdns.Init(c.config) + configdns.Init(d.config) zone, err := configdns.GetZone(zoneName) if err != nil { return err } - existingRecord := c.findExistingRecord(zone, recordName) + existingRecord := d.findExistingRecord(zone, recordName) if existingRecord != nil { err := zone.RemoveRecord(existingRecord) @@ -107,7 +112,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) { +func (d *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) { zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", "", err @@ -119,7 +124,7 @@ func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string return zone, name, nil } -func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord { +func (d *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord { for _, r := range zone.Zone.Txt { if r.Name == recordName { return r @@ -129,7 +134,7 @@ func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string return nil } -func (c *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error { +func (d *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error { err := zone.AddRecord(record) if err != nil { return err 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 750bc8b45..d7243009e 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go @@ -9,12 +9,12 @@ import ( "io" "io/ioutil" "net/http" - "os" "strings" "sync" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // Gandi API reference: http://doc.rpc.gandi.net/index.html @@ -44,25 +44,31 @@ type DNSProvider struct { inProgressFQDNs map[string]inProgressInfo inProgressAuthZones map[string]struct{} inProgressMu sync.Mutex + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Gandi. // Credentials must be passed in the environment variable: GANDI_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("GANDI_API_KEY") - return NewDNSProviderCredentials(apiKey) + values, err := env.Get("GANDI_API_KEY") + if err != nil { + return nil, fmt.Errorf("GandiDNS: %v", err) + } + + return NewDNSProviderCredentials(values["GANDI_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a // DNSProvider instance configured for Gandi. func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { if apiKey == "" { - return nil, fmt.Errorf("No Gandi API Key given") + return nil, fmt.Errorf("no Gandi API Key given") } return &DNSProvider{ apiKey: apiKey, inProgressFQDNs: make(map[string]inProgressInfo), inProgressAuthZones: make(map[string]struct{}), + client: &http.Client{Timeout: 60 * time.Second}, }, nil } @@ -269,10 +275,8 @@ func (e rpcError) Error() string { "Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString) } -func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { - client := http.Client{Timeout: 60 * time.Second} - - resp, err := client.Post(url, bodyType, body) +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) } @@ -290,7 +294,7 @@ func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) { // marshalling the data given in the call argument to XML and sending // that via HTTP Post to Gandi. The response is then unmarshalled into // the resp argument. -func rpcCall(call *methodCall, resp response) error { +func (d *DNSProvider) rpcCall(call *methodCall, resp response) error { // marshal b, err := xml.MarshalIndent(call, "", " ") if err != nil { @@ -299,7 +303,7 @@ func rpcCall(call *methodCall, resp response) error { // post b = append([]byte(``+"\n"), b...) - respBody, err := httpPost(endpoint, "text/xml", bytes.NewReader(b)) + respBody, err := d.httpPost(endpoint, "text/xml", bytes.NewReader(b)) if err != nil { return err } @@ -320,7 +324,7 @@ func rpcCall(call *methodCall, resp response) error { func (d *DNSProvider) getZoneID(domain string) (int, error) { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.info", Params: []param{ paramString{Value: d.apiKey}, @@ -347,7 +351,7 @@ func (d *DNSProvider) getZoneID(domain string) (int, error) { func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.clone", Params: []param{ paramString{Value: d.apiKey}, @@ -381,7 +385,7 @@ func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) { func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { resp := &responseInt{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.version.new", Params: []param{ paramString{Value: d.apiKey}, @@ -400,7 +404,7 @@ func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) { func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.record.add", Params: []param{ paramString{Value: d.apiKey}, @@ -429,7 +433,7 @@ func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value s func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { resp := &responseBool{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.version.set", Params: []param{ paramString{Value: d.apiKey}, @@ -449,7 +453,7 @@ func (d *DNSProvider) setZoneVersion(zoneID int, version int) error { func (d *DNSProvider) setZone(domain string, zoneID int) error { resp := &responseStruct{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.set", Params: []param{ paramString{Value: d.apiKey}, @@ -477,7 +481,7 @@ func (d *DNSProvider) setZone(domain string, zoneID int) error { func (d *DNSProvider) deleteZone(zoneID int) error { resp := &responseBool{} - err := rpcCall(&methodCall{ + err := d.rpcCall(&methodCall{ MethodName: "domain.zone.delete", Params: []param{ paramString{Value: d.apiKey}, 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 4614723ae..dea0f5f4b 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go @@ -7,12 +7,13 @@ import ( "encoding/json" "fmt" "net/http" - "os" "strings" "sync" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" + "github.com/xenolf/lego/platform/config/env" ) // Gandi API reference: http://doc.livedns.gandi.net/ @@ -40,13 +41,18 @@ type DNSProvider struct { apiKey string inProgressFQDNs map[string]inProgressInfo inProgressMu sync.Mutex + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for Gandi. // Credentials must be passed in the environment variable: GANDIV5_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("GANDIV5_API_KEY") - return NewDNSProviderCredentials(apiKey) + values, err := env.Get("GANDIV5_API_KEY") + if err != nil { + return nil, fmt.Errorf("GandiDNS: %v", err) + } + + return NewDNSProviderCredentials(values["GANDIV5_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -58,6 +64,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { return &DNSProvider{ apiKey: apiKey, inProgressFQDNs: make(map[string]inProgressInfo), + client: &http.Client{Timeout: 10 * time.Second}, }, nil } @@ -165,8 +172,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf req.Header.Set("X-Api-Key", d.apiKey) } - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -177,7 +183,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf } var response responseStruct err = json.NewDecoder(resp.Body).Decode(&response) - if err != nil && method != "DELETE" { + if err != nil && method != http.MethodDelete { return nil, err } @@ -188,23 +194,23 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf 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("PUT", target, addFieldRequest{ + response, err := d.sendRequest(http.MethodPut, target, addFieldRequest{ RRSetTTL: ttl, RRSetValues: []string{value}, }) if response != nil { - fmt.Printf("Gandi DNS: %s\n", response.Message) + 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("DELETE", target, deleteFieldRequest{ + response, err := d.sendRequest(http.MethodDelete, target, deleteFieldRequest{ Delete: true, }) if response != nil && response.Message == "" { - fmt.Printf("Gandi DNS: Zone record deleted\n") + 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 4a9387093..2999a79dc 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go +++ b/vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go @@ -31,6 +31,7 @@ func NewDNSProvider() (*DNSProvider, error) { if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok { return NewDNSProviderServiceAccount(saFile) } + project := os.Getenv("GCE_PROJECT") return NewDNSProviderCredentials(project) } @@ -44,11 +45,11 @@ func NewDNSProviderCredentials(project string) (*DNSProvider, error) { 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("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 nil, fmt.Errorf("unable to create Google Cloud DNS service: %v", err) } return &DNSProvider{ project: project, @@ -65,7 +66,7 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { dat, err := ioutil.ReadFile(saFile) if err != nil { - return nil, fmt.Errorf("Unable to read Service Account file: %v", err) + return nil, fmt.Errorf("unable to read Service Account file: %v", err) } // read project id from service account file @@ -74,19 +75,19 @@ 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("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("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) + return nil, fmt.Errorf("unable to create Google Cloud DNS service: %v", err) } return &DNSProvider{ project: project, @@ -95,10 +96,10 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } @@ -114,7 +115,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // Look for existing records. - list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do() + list, err := d.client.ResourceRecordSets.List(d.project, zone).Name(fqdn).Type("TXT").Do() if err != nil { return err } @@ -123,7 +124,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { change.Deletions = list.Rrsets } - chg, err := c.client.Changes.Create(c.project, zone, change).Do() + chg, err := d.client.Changes.Create(d.project, zone, change).Do() if err != nil { return err } @@ -132,7 +133,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { for chg.Status == "pending" { time.Sleep(time.Second) - chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do() + chg, err = d.client.Changes.Get(d.project, zone, chg.Id).Do() if err != nil { return err } @@ -142,15 +143,15 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } - records, err := c.findTxtRecords(zone, fqdn) + records, err := d.findTxtRecords(zone, fqdn) if err != nil { return err } @@ -159,7 +160,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { change := &dns.Change{ Deletions: []*dns.ResourceRecordSet{rec}, } - _, err = c.client.Changes.Create(c.project, zone, change).Do() + _, err = d.client.Changes.Create(d.project, zone, change).Do() if err != nil { return err } @@ -169,19 +170,19 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { // Timeout customizes the timeout values used by the ACME package for checking // DNS record validity. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 180 * time.Second, 5 * time.Second } // getHostedZone returns the managed-zone -func (c *DNSProvider) getHostedZone(domain string) (string, error) { +func (d *DNSProvider) getHostedZone(domain string) (string, error) { authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return "", err } - zones, err := c.client.ManagedZones. - List(c.project). + zones, err := d.client.ManagedZones. + List(d.project). DnsName(authZone). Do() if err != nil { @@ -189,20 +190,20 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) { } if len(zones.ManagedZones) == 0 { - return "", fmt.Errorf("No matching GoogleCloud domain found for domain %s", authZone) + return "", fmt.Errorf("no matching GoogleCloud domain found for domain %s", authZone) } return zones.ManagedZones[0].Name, nil } -func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) { +func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) { - recs, err := c.client.ResourceRecordSets.List(c.project, zone).Do() + recs, err := d.client.ResourceRecordSets.List(d.project, zone).Do() if err != nil { return nil, err } - found := []*dns.ResourceRecordSet{} + var found []*dns.ResourceRecordSet for _, r := range recs.Rrsets { if r.Type == "TXT" && r.Name == fqdn { found = append(found, r) 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 7623b564b..d6b071963 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go +++ b/vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go @@ -7,13 +7,13 @@ import ( "encoding/json" "fmt" "net/http" - "os" "strings" "sync" "time" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/log" + "github.com/xenolf/lego/platform/config/env" ) // GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation @@ -29,15 +29,19 @@ type DNSProvider struct { apiKey string 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. func NewDNSProvider() (*DNSProvider, error) { - apiUser := os.Getenv("GLESYS_API_USER") - apiKey := os.Getenv("GLESYS_API_KEY") - return NewDNSProviderCredentials(apiUser, apiKey) + values, err := env.Get("GLESYS_API_USER", "GLESYS_API_KEY") + if err != nil { + return nil, fmt.Errorf("GleSYS DNS: %v", err) + } + + return NewDNSProviderCredentials(values["GLESYS_API_USER"], values["GLESYS_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -46,10 +50,12 @@ func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, err if apiUser == "" || apiKey == "" { return nil, fmt.Errorf("GleSYS DNS: Incomplete credentials provided") } + return &DNSProvider{ apiUser: apiUser, apiKey: apiKey, activeRecords: make(map[string]int), + client: &http.Client{Timeout: 10 * time.Second}, }, nil } @@ -118,7 +124,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { // types for JSON method calls, parameters, and responses type addRecordRequest struct { - Domainname string `json:"domainname"` + DomainName string `json:"domainname"` Host string `json:"host"` Type string `json:"type"` Data string `json:"data"` @@ -126,7 +132,7 @@ type addRecordRequest struct { } type deleteRecordRequest struct { - Recordid int `json:"recordid"` + RecordID int `json:"recordid"` } type responseStruct struct { @@ -156,8 +162,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf req.Header.Set("Content-Type", "application/json") req.SetBasicAuth(d.apiUser, d.apiKey) - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { return nil, err } @@ -176,26 +181,27 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf // functions to perform API actions func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) { - response, err := d.sendRequest("POST", "addrecord", addRecordRequest{ - Domainname: domain, + response, err := d.sendRequest(http.MethodPost, "addrecord", addRecordRequest{ + DomainName: domain, Host: name, Type: "TXT", Data: value, TTL: ttl, }) - if response != nil && response.Response.Status.Code == 200 { - log.Printf("[INFO][%s] GleSYS DNS: Successfully created recordid %d", fqdn, response.Response.Record.Recordid) - return response.Response.Record.Recordid, nil + + if response != nil && response.Response.Status.Code == http.StatusOK { + log.Infof("[%s] GleSYS DNS: Successfully created record id %d", fqdn, response.Response.Record.RecordID) + return response.Response.Record.RecordID, nil } return 0, err } func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error { - response, err := d.sendRequest("POST", "deleterecord", deleteRecordRequest{ - Recordid: recordid, + response, err := d.sendRequest(http.MethodPost, "deleterecord", deleteRecordRequest{ + RecordID: recordid, }) if response != nil && response.Response.Status.Code == 200 { - log.Printf("[INFO][%s] GleSYS DNS: Successfully deleted recordid %d", fqdn, recordid) + log.Infof("[%s] GleSYS DNS: 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 54fbad032..c569adcbe 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go +++ b/vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go @@ -2,18 +2,17 @@ package godaddy import ( - "fmt" - "io" - "net/http" - "os" - "time" - "bytes" "encoding/json" + "fmt" + "io" "io/ioutil" + "net/http" "strings" + "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // GoDaddyAPIURL represents the API endpoint to call. @@ -23,15 +22,19 @@ const apiURL = "https://api.godaddy.com" type DNSProvider struct { apiKey string apiSecret string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for godaddy. // Credentials must be passed in the environment variables: GODADDY_API_KEY // and GODADDY_API_SECRET. func NewDNSProvider() (*DNSProvider, error) { - apikey := os.Getenv("GODADDY_API_KEY") - secret := os.Getenv("GODADDY_API_SECRET") - return NewDNSProviderCredentials(apikey, secret) + values, err := env.Get("GODADDY_API_KEY", "GODADDY_API_SECRET") + if err != nil { + return nil, fmt.Errorf("GoDaddy: %v", err) + } + + return NewDNSProviderCredentials(values["GODADDY_API_KEY"], values["GODADDY_API_SECRET"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -41,16 +44,20 @@ func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) { return nil, fmt.Errorf("GoDaddy credentials missing") } - return &DNSProvider{apiKey, apiSecret}, nil + return &DNSProvider{ + apiKey: apiKey, + apiSecret: apiSecret, + client: &http.Client{Timeout: 30 * time.Second}, + }, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] @@ -59,9 +66,9 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string { } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - domainZone, err := c.getZone(fqdn) + domainZone, err := d.getZone(fqdn) if err != nil { return err } @@ -70,7 +77,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { ttl = 600 } - recordName := c.extractRecordName(fqdn, domainZone) + recordName := d.extractRecordName(fqdn, domainZone) rec := []DNSRecord{ { Type: "TXT", @@ -80,17 +87,17 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { }, } - return c.updateRecords(rec, domainZone, recordName) + return d.updateRecords(rec, domainZone, recordName) } -func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { +func (d *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error { body, err := json.Marshal(records) if err != nil { return err } var resp *http.Response - resp, err = c.makeRequest("PUT", fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) + resp, err = d.makeRequest(http.MethodPut, fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body)) if err != nil { return err } @@ -105,14 +112,14 @@ func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, reco } // CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - domainZone, err := c.getZone(fqdn) + domainZone, err := d.getZone(fqdn) if err != nil { return err } - recordName := c.extractRecordName(fqdn, domainZone) + recordName := d.extractRecordName(fqdn, domainZone) rec := []DNSRecord{ { Type: "TXT", @@ -121,10 +128,10 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { }, } - return c.updateRecords(rec, domainZone, recordName) + return d.updateRecords(rec, domainZone, recordName) } -func (c *DNSProvider) getZone(fqdn string) (string, error) { +func (d *DNSProvider) getZone(fqdn string) (string, error) { authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { return "", err @@ -133,7 +140,7 @@ func (c *DNSProvider) getZone(fqdn string) (string, error) { return acme.UnFqdn(authZone), nil } -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, 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) if err != nil { return nil, err @@ -141,10 +148,9 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Res req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", c.apiKey, c.apiSecret)) + req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.apiKey, d.apiSecret)) - client := http.Client{Timeout: 30 * time.Second} - return client.Do(req) + return d.client.Do(req) } // DNSRecord a DNS record 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 a4d2efafc..6a3089a1d 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go +++ b/vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go @@ -4,6 +4,7 @@ package lightsail import ( "math/rand" + "os" "time" "github.com/aws/aws-sdk-go/aws" @@ -20,7 +21,8 @@ const ( // DNSProvider implements the acme.ChallengeProvider interface type DNSProvider struct { - client *lightsail.Lightsail + client *lightsail.Lightsail + dnsZone string } // customRetryer implements the client.Retryer interface by composing the @@ -35,7 +37,7 @@ type customRetryer struct { // delay of ~400ms with an upper limit of ~30 seconds which should prevent // causing a high number of consecutive throttling errors. // For reference: Route 53 enforces an account-wide(!) 5req/s query limit. -func (d customRetryer) RetryRules(r *request.Request) time.Duration { +func (c customRetryer) RetryRules(r *request.Request) time.Duration { retryCount := r.RetryCount if retryCount > 7 { retryCount = 7 @@ -61,47 +63,53 @@ func (d customRetryer) RetryRules(r *request.Request) time.Duration { func NewDNSProvider() (*DNSProvider, error) { r := customRetryer{} r.NumMaxRetries = maxRetries - config := request.WithRetryer(aws.NewConfig(), r) - client := lightsail.New(session.New(config)) + + config := aws.NewConfig().WithRegion("us-east-1") + sess, err := session.NewSession(request.WithRetryer(config, r)) + if err != nil { + return nil, err + } return &DNSProvider{ - client: client, + dnsZone: os.Getenv("DNS_ZONE"), + client: lightsail.New(sess), }, nil } // Present creates a TXT record using the specified parameters -func (r *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` - err := r.newTxtRecord(domain, fqdn, value) + + err := d.newTxtRecord(domain, fqdn, value) return err } // 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, value, _ := acme.DNS01Record(domain, keyAuth) value = `"` + value + `"` params := &lightsail.DeleteDomainEntryInput{ - DomainName: aws.String(domain), + DomainName: aws.String(d.dnsZone), DomainEntry: &lightsail.DomainEntry{ Name: aws.String(fqdn), Type: aws.String("TXT"), Target: aws.String(value), }, } - _, err := r.client.DeleteDomainEntry(params) + _, err := d.client.DeleteDomainEntry(params) return err } -func (r *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error { +func (d *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error { params := &lightsail.CreateDomainEntryInput{ - DomainName: aws.String(domain), + DomainName: aws.String(d.dnsZone), DomainEntry: &lightsail.DomainEntry{ Name: aws.String(fqdn), Target: aws.String(value), Type: aws.String("TXT"), }, } - _, err := r.client.CreateDomainEntry(params) + _, err := d.client.CreateDomainEntry(params) return err } 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 1e8cbc503..087d8b199 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go +++ b/vendor/github.com/xenolf/lego/providers/dns/linode/linode.go @@ -4,12 +4,13 @@ package linode import ( "errors" - "os" + "fmt" "strings" "time" "github.com/timewasted/linode/dns" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) const ( @@ -25,14 +26,18 @@ type hostedZoneInfo struct { // DNSProvider implements the acme.ChallengeProvider interface. type DNSProvider struct { - linode *dns.DNS + client *dns.DNS } // NewDNSProvider returns a DNSProvider instance configured for Linode. // Credentials must be passed in the environment variable: LINODE_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("LINODE_API_KEY") - return NewDNSProviderCredentials(apiKey) + values, err := env.Get("LINODE_API_KEY") + if err != nil { + return nil, fmt.Errorf("Linode: %v", err) + } + + return NewDNSProviderCredentials(values["LINODE_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -43,7 +48,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { } return &DNSProvider{ - linode: dns.New(apiKey), + client: dns.New(apiKey), }, nil } @@ -72,7 +77,7 @@ func (p *DNSProvider) Present(domain, token, keyAuth string) error { return err } - if _, err = p.linode.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil { + if _, err = p.client.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil { return err } @@ -88,7 +93,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { } // Get all TXT records for the specified domain. - resources, err := p.linode.GetResourcesByType(zone.domainID, "TXT") + resources, err := p.client.GetResourcesByType(zone.domainID, "TXT") if err != nil { return err } @@ -96,7 +101,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.linode.DeleteDomainResource(resource.DomainID, resource.ResourceID) + resp, err := p.client.DeleteDomainResource(resource.DomainID, resource.ResourceID) if err != nil { return err } @@ -119,7 +124,7 @@ func (p *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) { resourceName := strings.TrimSuffix(fqdn, "."+authZone) // Query the authority zone. - domain, err := p.linode.GetDomain(acme.UnFqdn(authZone)) + domain, err := p.client.GetDomain(acme.UnFqdn(authZone)) if err != nil { return nil, err } 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 2991d8171..f0ce56a85 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go @@ -8,11 +8,12 @@ import ( "io/ioutil" "net/http" "net/url" - "os" "strings" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" + "github.com/xenolf/lego/platform/config/env" ) // Notes about namecheap's tool API: @@ -32,7 +33,6 @@ var ( debug = false defaultBaseURL = "https://api.namecheap.com/xml.response" getIPURL = "https://dynamicdns.park-your-domain.com/getip" - httpClient = http.Client{Timeout: 60 * time.Second} ) // DNSProvider is an implementation of the ChallengeProviderTimeout interface @@ -42,15 +42,19 @@ type DNSProvider struct { apiUser string apiKey string clientIP string + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for namecheap. // Credentials must be passed in the environment variables: NAMECHEAP_API_USER // and NAMECHEAP_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - apiUser := os.Getenv("NAMECHEAP_API_USER") - apiKey := os.Getenv("NAMECHEAP_API_KEY") - return NewDNSProviderCredentials(apiUser, apiKey) + values, err := env.Get("NAMECHEAP_API_USER", "NAMECHEAP_API_KEY") + if err != nil { + return nil, fmt.Errorf("NameCheap: %v", err) + } + + return NewDNSProviderCredentials(values["NAMECHEAP_API_USER"], values["NAMECHEAP_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -60,7 +64,9 @@ func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { return nil, fmt.Errorf("Namecheap credentials missing") } - clientIP, err := getClientIP() + client := &http.Client{Timeout: 60 * time.Second} + + clientIP, err := getClientIP(client) if err != nil { return nil, err } @@ -70,6 +76,7 @@ func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) { apiUser: apiUser, apiKey: apiKey, clientIP: clientIP, + client: client, }, nil } @@ -99,8 +106,8 @@ type apierror struct { // getClientIP returns the client's public IP address. It uses namecheap's // IP discovery service to perform the lookup. -func getClientIP() (addr string, err error) { - resp, err := httpClient.Get(getIPURL) +func getClientIP(client *http.Client) (addr string, err error) { + resp, err := client.Get(getIPURL) if err != nil { return "", err } @@ -112,12 +119,12 @@ func getClientIP() (addr string, err error) { } if debug { - fmt.Println("Client IP:", string(clientIP)) + log.Println("Client IP:", string(clientIP)) } return string(clientIP), nil } -// A challenge repesents all the data needed to specify a dns-01 challenge +// A challenge represents all the data needed to specify a dns-01 challenge // to lets-encrypt. type challenge struct { domain string @@ -186,7 +193,7 @@ func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) { reqURL, _ := url.Parse(d.baseURL) reqURL.RawQuery = values.Encode() - resp, err := httpClient.Get(reqURL.String()) + resp, err := d.client.Get(reqURL.String()) if err != nil { return nil, err } @@ -235,7 +242,7 @@ func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) { reqURL, _ := url.Parse(d.baseURL) reqURL.RawQuery = values.Encode() - resp, err := httpClient.Get(reqURL.String()) + resp, err := d.client.Get(reqURL.String()) if err != nil { return nil, err } @@ -285,7 +292,7 @@ func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error { values.Add("TTL"+ind, h.TTL) } - resp, err := httpClient.PostForm(d.baseURL, values) + resp, err := d.client.PostForm(d.baseURL, values) if err != nil { return err } @@ -382,7 +389,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { if debug { for _, h := range hosts { - fmt.Printf( + log.Printf( "%-5.5s %-30.30s %-6s %-70.70s\n", h.Type, h.Name, h.TTL, h.Address) } 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 2df4a597a..15272c5f4 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go +++ b/vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go @@ -9,6 +9,7 @@ import ( "github.com/namedotcom/go/namecom" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface. @@ -19,11 +20,13 @@ type DNSProvider struct { // NewDNSProvider returns a DNSProvider instance configured for namedotcom. // Credentials must be passed in the environment variables: NAMECOM_USERNAME and NAMECOM_API_TOKEN func NewDNSProvider() (*DNSProvider, error) { - username := os.Getenv("NAMECOM_USERNAME") - apiToken := os.Getenv("NAMECOM_API_TOKEN") - server := os.Getenv("NAMECOM_SERVER") + values, err := env.Get("NAMECOM_USERNAME", "NAMECOM_API_TOKEN") + if err != nil { + return nil, fmt.Errorf("Name.com: %v", err) + } - return NewDNSProviderCredentials(username, apiToken, server) + server := os.Getenv("NAMECOM_SERVER") + return NewDNSProviderCredentials(values["NAMECOM_USERNAME"], values["NAMECOM_API_TOKEN"], server) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -46,30 +49,30 @@ func NewDNSProviderCredentials(username, apiToken, server string) (*DNSProvider, } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) request := &namecom.Record{ DomainName: domain, - Host: c.extractRecordName(fqdn, domain), + Host: d.extractRecordName(fqdn, domain), Type: "TXT", TTL: uint32(ttl), Answer: value, } - _, err := c.client.CreateRecord(request) + _, err := d.client.CreateRecord(request) if err != nil { - return fmt.Errorf("namedotcom API call failed: %v", err) + return fmt.Errorf("Name.com API call failed: %v", err) } return nil } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - records, err := c.getRecords(domain) + records, err := d.getRecords(domain) if err != nil { return err } @@ -80,7 +83,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { DomainName: domain, ID: rec.ID, } - _, err := c.client.DeleteRecord(request) + _, err := d.client.DeleteRecord(request) if err != nil { return err } @@ -90,7 +93,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { +func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { var ( err error records []*namecom.Record @@ -103,7 +106,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { } for request.Page > 0 { - response, err = c.client.ListRecords(request) + response, err = d.client.ListRecords(request) if err != nil { return nil, err } @@ -115,7 +118,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { return records, nil } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx] diff --git a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go new file mode 100644 index 000000000..86b6fa659 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/client.go @@ -0,0 +1,235 @@ +// Package nifcloud implements a DNS provider for solving the DNS-01 challenge +// using NIFCLOUD DNS. +package nifcloud + +import ( + "bytes" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "encoding/xml" + "errors" + "fmt" + "net/http" + "time" +) + +const ( + defaultEndpoint = "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. +type ChangeResourceRecordSetsRequest struct { + XMLNs string `xml:"xmlns,attr"` + ChangeBatch ChangeBatch `xml:"ChangeBatch"` +} + +// ChangeResourceRecordSetsResponse is a complex type containing the response for the request. +type ChangeResourceRecordSetsResponse struct { + ChangeInfo ChangeInfo `xml:"ChangeInfo"` +} + +// GetChangeResponse is a complex type that contains the ChangeInfo element. +type GetChangeResponse struct { + ChangeInfo ChangeInfo `xml:"ChangeInfo"` +} + +// ErrorResponse is the information for any errors. +type ErrorResponse struct { + Error struct { + Type string `xml:"Type"` + Message string `xml:"Message"` + Code string `xml:"Code"` + } `xml:"Error"` + RequestID string `xml:"RequestId"` +} + +// ChangeBatch is the information for a change request. +type ChangeBatch struct { + Changes Changes `xml:"Changes"` + Comment string `xml:"Comment"` +} + +// Changes is array of Change. +type Changes struct { + Change []Change `xml:"Change"` +} + +// Change is the information for each resource record set that you want to change. +type Change struct { + Action string `xml:"Action"` + ResourceRecordSet ResourceRecordSet `xml:"ResourceRecordSet"` +} + +// ResourceRecordSet is the information about the resource record set to create or delete. +type ResourceRecordSet struct { + Name string `xml:"Name"` + Type string `xml:"Type"` + TTL int `xml:"TTL"` + ResourceRecords ResourceRecords `xml:"ResourceRecords"` +} + +// ResourceRecords is array of ResourceRecord. +type ResourceRecords struct { + ResourceRecord []ResourceRecord `xml:"ResourceRecord"` +} + +// ResourceRecord is the information specific to the resource record. +type ResourceRecord struct { + Value string `xml:"Value"` +} + +// ChangeInfo is A complex type that describes change information about changes made to your hosted zone. +type ChangeInfo struct { + ID string `xml:"Id"` + Status string `xml:"Status"` + SubmittedAt string `xml:"SubmittedAt"` +} + +func newClient(httpClient *http.Client, accessKey string, secretKey string, endpoint string) *Client { + client := http.DefaultClient + if httpClient != nil { + client = httpClient + } + + return &Client{ + accessKey: accessKey, + secretKey: secretKey, + endpoint: endpoint, + client: client, + } +} + +// Client client of NIFCLOUD DNS +type Client struct { + accessKey string + secretKey string + endpoint string + client *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) + + body := &bytes.Buffer{} + body.Write([]byte(xml.Header)) + err := xml.NewEncoder(body).Encode(input) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, requestURL, body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "text/xml; charset=utf-8") + + err = c.sign(req) + if err != nil { + return nil, fmt.Errorf("an error occurred during the creation of the signature: %v", err) + } + + res, err := c.client.Do(req) + if err != nil { + return nil, err + } + if res.Body == nil { + return nil, errors.New("the response body is nil") + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + errResp := &ErrorResponse{} + err = xml.NewDecoder(res.Body).Decode(errResp) + if err != nil { + return nil, fmt.Errorf("an error occurred while unmarshaling the error body to XML: %v", err) + } + + return nil, fmt.Errorf("an error occurred: %s", errResp.Error.Message) + } + + output := &ChangeResourceRecordSetsResponse{} + err = xml.NewDecoder(res.Body).Decode(output) + if err != nil { + return nil, fmt.Errorf("an error occurred while unmarshaling the response body to XML: %v", err) + } + + return output, err +} + +// 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) + + req, err := http.NewRequest(http.MethodGet, requestURL, nil) + if err != nil { + return nil, err + } + + err = c.sign(req) + if err != nil { + return nil, fmt.Errorf("an error occurred during the creation of the signature: %v", err) + } + + res, err := c.client.Do(req) + if err != nil { + return nil, err + } + if res.Body == nil { + return nil, errors.New("the response body is nil") + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + errResp := &ErrorResponse{} + err = xml.NewDecoder(res.Body).Decode(errResp) + if err != nil { + return nil, fmt.Errorf("an error occurred while unmarshaling the error body to XML: %v", err) + } + + return nil, fmt.Errorf("an error occurred: %s", errResp.Error.Message) + } + + output := &GetChangeResponse{} + err = xml.NewDecoder(res.Body).Decode(output) + if err != nil { + return nil, fmt.Errorf("an error occurred while unmarshaling the response body to XML: %v", err) + } + + return output, nil +} + +func (c *Client) sign(req *http.Request) error { + if req.Header.Get("Date") == "" { + location, err := time.LoadLocation("GMT") + if err != nil { + return err + } + + req.Header.Set("Date", time.Now().In(location).Format(time.RFC1123)) + } + + if req.URL.Path == "" { + req.URL.Path += "/" + } + + mac := hmac.New(sha1.New, []byte(c.secretKey)) + _, err := mac.Write([]byte(req.Header.Get("Date"))) + if err != nil { + return err + } + + hashed := mac.Sum(nil) + signature := base64.StdEncoding.EncodeToString(hashed) + + auth := fmt.Sprintf("NIFTY3-HTTPS NiftyAccessKeyId=%s,Algorithm=HmacSHA1,Signature=%s", c.accessKey, signature) + req.Header.Set("X-Nifty-Authorization", auth) + + return nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go new file mode 100644 index 000000000..5a7fa6b8c --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/nifcloud/nifcloud.go @@ -0,0 +1,103 @@ +// Package nifcloud implements a DNS provider for solving the DNS-01 challenge +// using NIFCLOUD DNS. +package nifcloud + +import ( + "fmt" + "net/http" + "os" + "time" + + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" +) + +// DNSProvider implements the acme.ChallengeProvider interface +type DNSProvider struct { + client *Client +} + +// 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. +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) + } + + endpoint := os.Getenv("NIFCLOUD_DNS_ENDPOINT") + if endpoint == "" { + endpoint = defaultEndpoint + } + + httpClient := &http.Client{Timeout: 30 * time.Second} + + return NewDNSProviderCredentials(httpClient, endpoint, values["NIFCLOUD_ACCESS_KEY_ID"], values["NIFCLOUD_SECRET_ACCESS_KEY"]) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for NIFCLOUD. +func NewDNSProviderCredentials(httpClient *http.Client, endpoint, accessKey, secretKey string) (*DNSProvider, error) { + client := newClient(httpClient, accessKey, secretKey, endpoint) + + return &DNSProvider{ + client: client, + }, 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) +} + +// 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) +} + +func (d *DNSProvider) changeRecord(action, fqdn, value, domain string, ttl int) error { + name := acme.UnFqdn(fqdn) + + reqParams := ChangeResourceRecordSetsRequest{ + XMLNs: xmlNs, + ChangeBatch: ChangeBatch{ + Comment: "Managed by Lego", + Changes: Changes{ + Change: []Change{ + { + Action: action, + ResourceRecordSet: ResourceRecordSet{ + Name: name, + Type: "TXT", + TTL: ttl, + ResourceRecords: ResourceRecords{ + ResourceRecord: []ResourceRecord{ + { + Value: value, + }, + }, + }, + }, + }, + }, + }, + }, + } + + resp, err := d.client.ChangeResourceRecordSets(domain, reqParams) + if err != nil { + return fmt.Errorf("failed to change NIFCLOUD record set: %v", err) + } + + statusID := resp.ChangeInfo.ID + + return acme.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) { + resp, err := d.client.GetChange(statusID) + if err != nil { + return false, fmt.Errorf("failed to query NIFCLOUD DNS change status: %v", err) + } + return resp.ChangeInfo.Status == "INSYNC", nil + }) +} 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 105d73f89..d37da4cdb 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go @@ -5,10 +5,10 @@ package ns1 import ( "fmt" "net/http" - "os" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" "gopkg.in/ns1/ns1-go.v2/rest" "gopkg.in/ns1/ns1-go.v2/rest/model/dns" ) @@ -21,11 +21,12 @@ type DNSProvider struct { // NewDNSProvider returns a DNSProvider instance configured for NS1. // Credentials must be passed in the environment variables: NS1_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("NS1_API_KEY") - if key == "" { - return nil, fmt.Errorf("NS1 credentials missing") + values, err := env.Get("NS1_API_KEY") + if err != nil { + return nil, fmt.Errorf("NS1: %v", err) } - return NewDNSProviderCredentials(key) + + return NewDNSProviderCredentials(values["NS1_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -42,16 +43,16 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) { } // Present creates a TXT record to fulfil the dns-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } - record := c.newTxtRecord(zone, fqdn, value, ttl) - _, err = c.client.Records.Create(record) + record := d.newTxtRecord(zone, fqdn, value, ttl) + _, err = d.client.Records.Create(record) if err != nil && err != rest.ErrRecordExists { return err } @@ -60,21 +61,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(domain) + zone, err := d.getHostedZone(domain) if err != nil { return err } name := acme.UnFqdn(fqdn) - _, err = c.client.Records.Delete(zone.Zone, name, "TXT") + _, err = d.client.Records.Delete(zone.Zone, name, "TXT") return err } -func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { - zone, _, err := c.client.Zones.Get(domain) +func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { + zone, _, err := d.client.Zones.Get(domain) if err != nil { return nil, err } @@ -82,7 +83,7 @@ func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { return zone, nil } -func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record { +func (d *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record { name := acme.UnFqdn(fqdn) return &dns.Record{ diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go b/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go index babc2b3ab..127dd5cc7 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go +++ b/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go @@ -77,7 +77,7 @@ func (m *DNSMock) HandleListZonesSuccessfully() { }]} `) - assert.Equal(m.t, r.Method, "GET") + assert.Equal(m.t, r.Method, http.MethodGet) assert.Equal(m.t, r.URL.Path, "/v2/zones") assert.Equal(m.t, r.URL.RawQuery, "name=example.com.") assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") @@ -92,7 +92,7 @@ func (m *DNSMock) HandleListZonesEmpty() { ]} `) - assert.Equal(m.t, r.Method, "GET") + assert.Equal(m.t, r.Method, http.MethodGet) assert.Equal(m.t, r.URL.Path, "/v2/zones") assert.Equal(m.t, r.URL.RawQuery, "name=example.com.") assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") @@ -108,7 +108,7 @@ func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() { }]} `) - assert.Equal(m.t, r.Method, "DELETE") + assert.Equal(m.t, r.Method, http.MethodDelete) assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321") assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json") }) @@ -130,7 +130,7 @@ func (m *DNSMock) HandleListRecordsetsEmpty() { // HandleListRecordsetsSuccessfully Handle list recordsets successfully func (m *DNSMock) HandleListRecordsetsSuccessfully() { m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" { + if r.Method == http.MethodGet { fmt.Fprintf(w, `{ "recordsets":[{ "id":"321321" @@ -140,7 +140,7 @@ func (m *DNSMock) HandleListRecordsetsSuccessfully() { assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets") assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.") - } else if r.Method == "POST" { + } else if r.Method == http.MethodPost { body, err := ioutil.ReadAll(r.Body) assert.Nil(m.t, err) 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 869187307..30535f7ec 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go +++ b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go @@ -13,6 +13,7 @@ import ( "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface that uses @@ -31,12 +32,18 @@ type DNSProvider struct { // Credentials must be passed in the environment variables: OTC_USER_NAME, // OTC_DOMAIN_NAME, OTC_PASSWORD OTC_PROJECT_NAME and OTC_IDENTITY_ENDPOINT. func NewDNSProvider() (*DNSProvider, error) { - domainName := os.Getenv("OTC_DOMAIN_NAME") - userName := os.Getenv("OTC_USER_NAME") - password := os.Getenv("OTC_PASSWORD") - projectName := os.Getenv("OTC_PROJECT_NAME") - identityEndpoint := os.Getenv("OTC_IDENTITY_ENDPOINT") - return NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint) + 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 NewDNSProviderCredentials( + values["OTC_DOMAIN_NAME"], + values["OTC_USER_NAME"], + values["OTC_PASSWORD"], + values["OTC_PROJECT_NAME"], + os.Getenv("OTC_IDENTITY_ENDPOINT"), + ) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -163,7 +170,7 @@ func (d *DNSProvider) loginRequest() error { if err != nil { return err } - req, err := http.NewRequest("POST", d.identityEndpoint, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, d.identityEndpoint, bytes.NewReader(body)) if err != nil { return err } @@ -235,7 +242,7 @@ func (d *DNSProvider) getZoneID(zone string) (string, error) { } resource := fmt.Sprintf("zones?name=%s", zone) - resp, err := d.SendRequest("GET", resource, nil) + resp, err := d.SendRequest(http.MethodGet, resource, nil) if err != nil { return "", err } @@ -271,7 +278,7 @@ func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) } resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn) - resp, err := d.SendRequest("GET", resource, nil) + resp, err := d.SendRequest(http.MethodGet, resource, nil) if err != nil { return "", err } @@ -300,7 +307,7 @@ 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("DELETE", resource, nil) + _, err := d.SendRequest(http.MethodDelete, resource, nil) return err } @@ -344,7 +351,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { TTL: ttl, Records: []string{fmt.Sprintf("\"%s\"", value)}, } - _, err = d.SendRequest("POST", resource, r1) + _, err = d.SendRequest(http.MethodPost, resource, r1) return err } 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 a49fc7742..e1999ea50 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go +++ b/vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go @@ -4,12 +4,12 @@ package ovh import ( "fmt" - "os" "strings" "sync" "github.com/ovh/go-ovh/ovh" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // OVH API reference: https://eu.api.ovh.com/ @@ -30,11 +30,17 @@ type DNSProvider struct { // OVH_APPLICATION_SECRET // OVH_CONSUMER_KEY func NewDNSProvider() (*DNSProvider, error) { - apiEndpoint := os.Getenv("OVH_ENDPOINT") - applicationKey := os.Getenv("OVH_APPLICATION_KEY") - applicationSecret := os.Getenv("OVH_APPLICATION_SECRET") - consumerKey := os.Getenv("OVH_CONSUMER_KEY") - return NewDNSProviderCredentials(apiEndpoint, applicationKey, applicationSecret, consumerKey) + 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 NewDNSProviderCredentials( + values["OVH_ENDPOINT"], + values["OVH_APPLICATION_KEY"], + values["OVH_APPLICATION_SECRET"], + values["OVH_CONSUMER_KEY"], + ) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -96,16 +102,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // Create TXT record err = d.client.Post(reqURL, reqData, &respData) if err != nil { - fmt.Printf("Error when call OVH api to add record : %q \n", err) - return err + return fmt.Errorf("error when call OVH 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 { - fmt.Printf("Error when call OVH api to refresh zone : %q \n", err) - return err + return fmt.Errorf("error when call OVH api to refresh zone: %v", err) } d.recordIDsMu.Lock() @@ -138,8 +142,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { err = d.client.Delete(reqURL, nil) if err != nil { - fmt.Printf("Error when call OVH api to delete challenge record : %q \n", err) - return err + return fmt.Errorf("error when call OVH api to delete challenge record: %v", err) } // Delete record ID from map 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 9810245b9..3b9915ac2 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go +++ b/vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go @@ -9,12 +9,13 @@ import ( "io" "net/http" "net/url" - "os" "strconv" "strings" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/log" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface @@ -22,19 +23,24 @@ type DNSProvider struct { apiKey string host *url.URL apiVersion int + client *http.Client } // NewDNSProvider returns a DNSProvider instance configured for pdns. // Credentials must be passed in the environment variable: // PDNS_API_URL and PDNS_API_KEY. func NewDNSProvider() (*DNSProvider, error) { - key := os.Getenv("PDNS_API_KEY") - hostURL, err := url.Parse(os.Getenv("PDNS_API_URL")) + values, err := env.Get("PDNS_API_KEY", "PDNS_API_URL") if err != nil { - return nil, err + return nil, fmt.Errorf("PDNS: %v", err) } - return NewDNSProviderCredentials(hostURL, key) + hostURL, err := url.Parse(values["PDNS_API_URL"]) + if err != nil { + return nil, fmt.Errorf("PDNS: %v", err) + } + + return NewDNSProviderCredentials(hostURL, values["PDNS_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -48,25 +54,31 @@ func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error) return nil, fmt.Errorf("PDNS API URL missing") } - provider := &DNSProvider{ + d := &DNSProvider{ host: host, apiKey: key, + client: &http.Client{Timeout: 30 * time.Second}, } - provider.getAPIVersion() - return provider, nil + apiVersion, err := d.getAPIVersion() + if err != nil { + log.Warnf("PDNS: failed to get API version %v", err) + } + d.apiVersion = apiVersion + + return d, nil } // Timeout returns the timeout and interval to use when checking for DNS // propagation. Adjusting here to cope with spikes in propagation times. -func (c *DNSProvider) Timeout() (timeout, interval time.Duration) { +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return 120 * time.Second, 2 * time.Second } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(fqdn) + zone, err := d.getHostedZone(fqdn) if err != nil { return err } @@ -74,7 +86,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { name := fqdn // pre-v1 API wants non-fqdn - if c.apiVersion == 0 { + if d.apiVersion == 0 { name = acme.UnFqdn(fqdn) } @@ -106,20 +118,20 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) + _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) return err } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zone, err := c.getHostedZone(fqdn) + zone, err := d.getHostedZone(fqdn) if err != nil { return err } - set, err := c.findTxtRecord(fqdn) + set, err := d.findTxtRecord(fqdn) if err != nil { return err } @@ -138,11 +150,11 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) + _, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body)) return err } -func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { +func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { var zone hostedZone authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { @@ -150,12 +162,12 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { } url := "/servers/localhost/zones" - result, err := c.makeRequest("GET", url, nil) + result, err := d.makeRequest(http.MethodGet, url, nil) if err != nil { return nil, err } - zones := []hostedZone{} + var zones []hostedZone err = json.Unmarshal(result, &zones) if err != nil { return nil, err @@ -168,7 +180,7 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { } } - result, err = c.makeRequest("GET", url, nil) + result, err = d.makeRequest(http.MethodGet, url, nil) if err != nil { return nil, err } @@ -194,13 +206,13 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) { return &zone, nil } -func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { - zone, err := c.getHostedZone(fqdn) +func (d *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { + zone, err := d.getHostedZone(fqdn) if err != nil { return nil, err } - _, err = c.makeRequest("GET", zone.URL, nil) + _, err = d.makeRequest(http.MethodGet, zone.URL, nil) if err != nil { return nil, err } @@ -214,21 +226,21 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) { return nil, fmt.Errorf("no existing record found for %s", fqdn) } -func (c *DNSProvider) getAPIVersion() { +func (d *DNSProvider) getAPIVersion() (int, error) { type APIVersion struct { URL string `json:"url"` Version int `json:"version"` } - result, err := c.makeRequest("GET", "/api", nil) + result, err := d.makeRequest(http.MethodGet, "/api", nil) if err != nil { - return + return 0, err } var versions []APIVersion err = json.Unmarshal(result, &versions) if err != nil { - return + return 0, err } latestVersion := 0 @@ -237,41 +249,45 @@ func (c *DNSProvider) getAPIVersion() { latestVersion = v.Version } } - c.apiVersion = latestVersion + + return latestVersion, err } -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { type APIError struct { Error string `json:"error"` } + var path = "" - if c.host.Path != "/" { - path = c.host.Path + if d.host.Path != "/" { + path = d.host.Path } + if !strings.HasPrefix(uri, "/") { uri = "/" + uri } - if c.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { - uri = "/api/v" + strconv.Itoa(c.apiVersion) + uri + + if d.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") { + uri = "/api/v" + strconv.Itoa(d.apiVersion) + uri } - url := c.host.Scheme + "://" + c.host.Host + path + uri + + url := d.host.Scheme + "://" + d.host.Host + path + uri req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } - req.Header.Set("X-API-Key", c.apiKey) + req.Header.Set("X-API-Key", d.apiKey) - client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(req) + resp, err := d.client.Do(req) if err != nil { - return nil, fmt.Errorf("Error talking to PDNS API -> %v", err) + return nil, fmt.Errorf("error talking to PDNS API -> %v", err) } defer resp.Body.Close() - if resp.StatusCode != 422 && (resp.StatusCode < 200 || resp.StatusCode >= 300) { - return nil, fmt.Errorf("Unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url) + 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) } var msg json.RawMessage @@ -293,7 +309,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM return nil, err } if apiError.Error != "" { - return nil, fmt.Errorf("Error talking to PDNS API -> %v", apiError.Error) + return nil, fmt.Errorf("error talking to PDNS API -> %v", apiError.Error) } } return msg, nil 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 d30e582b8..358af69d0 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go @@ -8,10 +8,10 @@ import ( "fmt" "io" "net/http" - "os" "time" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // rackspaceAPIURL represents the Identity API endpoint to call @@ -22,15 +22,19 @@ var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens" type DNSProvider struct { 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. func NewDNSProvider() (*DNSProvider, error) { - user := os.Getenv("RACKSPACE_USER") - key := os.Getenv("RACKSPACE_API_KEY") - return NewDNSProviderCredentials(user, key) + values, err := env.Get("RACKSPACE_USER", "RACKSPACE_API_KEY") + if err != nil { + return nil, fmt.Errorf("Rackspace: %v", err) + } + + return NewDNSProviderCredentials(values["RACKSPACE_USER"], values["RACKSPACE_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a @@ -41,35 +45,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, fmt.Errorf("Rackspace credentials missing") } - type APIKeyCredentials struct { - Username string `json:"username"` - APIKey string `json:"apiKey"` - } - - type Auth struct { - APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"` - } - - type RackspaceAuthData struct { - Auth `json:"auth"` - } - - type RackspaceIdentity 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"` - } - - authData := RackspaceAuthData{ + authData := AuthData{ Auth: Auth{ APIKeyCredentials: APIKeyCredentials{ Username: user, @@ -83,13 +59,13 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, err } - req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body)) + req, err := http.NewRequest(http.MethodPost, rackspaceAPIURL, bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") - client := http.Client{Timeout: 30 * time.Second} + client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("error querying Rackspace Identity API: %v", err) @@ -100,7 +76,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode) } - var rackspaceIdentity RackspaceIdentity + var rackspaceIdentity Identity err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity) if err != nil { return nil, err @@ -121,13 +97,14 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) { return &DNSProvider{ token: rackspaceIdentity.Access.Token.ID, cloudDNSEndpoint: dnsEndpoint, + client: client, }, nil } // Present creates a TXT record to fulfil the dns-01 challenge -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } @@ -146,30 +123,30 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { return err } - _, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) + _, err = d.makeRequest(http.MethodPost, fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) return err } // CleanUp removes the TXT record matching the specified parameters -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneID, err := c.getHostedZoneID(fqdn) + zoneID, err := d.getHostedZoneID(fqdn) if err != nil { return err } - record, err := c.findTxtRecord(fqdn, zoneID) + record, err := d.findTxtRecord(fqdn, zoneID) if err != nil { return err } - _, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) + _, err = d.makeRequest(http.MethodDelete, fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) return err } // getHostedZoneID performs a lookup to get the DNS zone which needs // modifying for a given FQDN -func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { +func (d *DNSProvider) getHostedZoneID(fqdn string) (int, error) { // HostedZones represents the response when querying Rackspace DNS zones type ZoneSearchResponse struct { TotalEntries int `json:"totalEntries"` @@ -184,7 +161,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { return 0, err } - result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil) + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil) if err != nil { return 0, err } @@ -204,8 +181,8 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) { } // findTxtRecord searches a DNS zone for a TXT record with a specific name -func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { - result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil) +func (d *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { + result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil) if err != nil { return nil, err } @@ -229,14 +206,14 @@ func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) { } // makeRequest is a wrapper function used for making DNS API requests -func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { - url := c.cloudDNSEndpoint + uri +func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { + url := d.cloudDNSEndpoint + uri req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } - req.Header.Set("X-Auth-Token", c.token) + req.Header.Set("X-Auth-Token", d.token) req.Header.Set("Content-Type", "application/json") client := http.Client{Timeout: 30 * time.Second} @@ -260,6 +237,38 @@ func (c *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"` 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 8f4231df2..ee0b7c14e 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go +++ b/vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go @@ -38,6 +38,7 @@ func NewDNSProvider() (*DNSProvider, error) { tsigKey := os.Getenv("RFC2136_TSIG_KEY") tsigSecret := os.Getenv("RFC2136_TSIG_SECRET") timeout := os.Getenv("RFC2136_TIMEOUT") + return NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, timeout) } @@ -58,13 +59,14 @@ func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, t return nil, err } } - d := &DNSProvider{ - nameserver: nameserver, - } + + 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 diff --git a/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go b/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go new file mode 100644 index 000000000..17ca694a6 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/sakuracloud/sakuracloud.go @@ -0,0 +1,144 @@ +// Package sakuracloud implements a DNS provider for solving the DNS-01 challenge +// using sakuracloud DNS. +package sakuracloud + +import ( + "errors" + "fmt" + "net/http" + "strings" + + "github.com/sacloud/libsacloud/api" + "github.com/sacloud/libsacloud/sacloud" + "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 *api.Client +} + +// NewDNSProvider returns a DNSProvider instance configured for sakuracloud. +// Credentials must be passed in the environment variables: SAKURACLOUD_ACCESS_TOKEN & SAKURACLOUD_ACCESS_TOKEN_SECRET +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 NewDNSProviderCredentials(values["SAKURACLOUD_ACCESS_TOKEN"], values["SAKURACLOUD_ACCESS_TOKEN_SECRET"]) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for sakuracloud. +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") + } + + client := api.NewClient(token, secret, "tk1a") + client.UserAgent = acme.UserAgent + + 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) + + zone, err := d.getHostedZone(domain) + if err != nil { + return err + } + + name := d.extractRecordName(fqdn, zone.Name) + + zone.AddRecord(zone.CreateNewRecord(name, "TXT", value, ttl)) + _, err = d.client.GetDNSAPI().Update(zone.ID, zone) + if err != nil { + return fmt.Errorf("SakuraCloud 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) + + zone, err := d.getHostedZone(domain) + if err != nil { + return err + } + + records, err := d.findTxtRecords(fqdn, zone) + if err != nil { + return err + } + + for _, record := range records { + var updRecords []sacloud.DNSRecordSet + for _, r := range zone.Settings.DNS.ResourceRecordSets { + if !(r.Name == record.Name && r.Type == record.Type && r.RData == record.RData) { + updRecords = append(updRecords, r) + } + } + zone.Settings.DNS.ResourceRecordSets = updRecords + } + + _, err = d.client.GetDNSAPI().Update(zone.ID, zone) + if err != nil { + return fmt.Errorf("SakuraCloud API call failed: %v", err) + } + + return nil +} + +func (d *DNSProvider) getHostedZone(domain string) (*sacloud.DNS, error) { + authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) + if err != nil { + return nil, err + } + + zoneName := acme.UnFqdn(authZone) + + res, err := d.client.GetDNSAPI().WithNameLike(zoneName).Find() + if err != nil { + 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) + } + + for _, zone := range res.CommonServiceDNSItems { + if zone.Name == zoneName { + return &zone, nil + } + } + + return nil, fmt.Errorf("zone %s not found on SakuraCloud DNS", zoneName) +} + +func (d *DNSProvider) findTxtRecords(fqdn string, zone *sacloud.DNS) ([]sacloud.DNSRecordSet, error) { + recordName := d.extractRecordName(fqdn, zone.Name) + + var res []sacloud.DNSRecordSet + for _, record := range zone.Settings.DNS.ResourceRecordSets { + if record.Name == recordName && record.Type == "TXT" { + res = append(res, record) + } + } + return res, 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/vegadns/vegadns.go b/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go new file mode 100644 index 000000000..4d5371b85 --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/vegadns/vegadns.go @@ -0,0 +1,85 @@ +// Package vegadns implements a DNS provider for solving the DNS-01 +// challenge using VegaDNS. +package vegadns + +import ( + "fmt" + "os" + "strings" + "time" + + vegaClient "github.com/OpenDNS/vegadns2client" + "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" +) + +// DNSProvider describes a provider for VegaDNS +type DNSProvider struct { + client vegaClient.VegaDNSClient +} + +// NewDNSProvider returns a DNSProvider instance configured for VegaDNS. +// Credentials must be passed in the environment variables: +// VEGADNS_URL, SECRET_VEGADNS_KEY, SECRET_VEGADNS_SECRET. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get("VEGADNS_URL") + if err != nil { + return nil, fmt.Errorf("VegaDNS: %v", err) + } + + key := os.Getenv("SECRET_VEGADNS_KEY") + secret := os.Getenv("SECRET_VEGADNS_SECRET") + + return NewDNSProviderCredentials(values["VEGADNS_URL"], key, secret) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for VegaDNS. +func NewDNSProviderCredentials(vegaDNSURL string, key string, secret string) (*DNSProvider, error) { + vega := vegaClient.NewVegaDNSClient(vegaDNSURL) + vega.APIKey = key + vega.APISecret = secret + + return &DNSProvider{ + client: vega, + }, nil +} + +// 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 +} + +// Present creates a TXT record to fulfil the dns-01 challenge +func (r *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, _ := acme.DNS01Record(domain, keyAuth) + + _, domainID, err := r.client.GetAuthZone(fqdn) + if err != nil { + return fmt.Errorf("can't find Authoritative Zone for %s in Present: %v", fqdn, err) + } + + return r.client.CreateTXT(domainID, fqdn, value, 10) +} + +// CleanUp removes the TXT record matching the specified parameters +func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + + _, domainID, err := r.client.GetAuthZone(fqdn) + if err != nil { + return fmt.Errorf("can't find Authoritative Zone for %s in CleanUp: %v", fqdn, err) + } + + txt := strings.TrimSuffix(fqdn, ".") + + recordID, err := r.client.GetRecordID(domainID, txt, "TXT") + if err != nil { + return fmt.Errorf("couldn't get Record ID in CleanUp: %s", err) + } + + return r.client.DeleteRecord(recordID) +} 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 bc2067579..27cfa524a 100644 --- a/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go +++ b/vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go @@ -5,11 +5,11 @@ package vultr import ( "fmt" - "os" "strings" vultr "github.com/JamesClonk/vultr/lib" "github.com/xenolf/lego/acme" + "github.com/xenolf/lego/platform/config/env" ) // DNSProvider is an implementation of the acme.ChallengeProvider interface. @@ -20,8 +20,12 @@ type DNSProvider struct { // NewDNSProvider returns a DNSProvider instance with a configured Vultr client. // Authentication uses the VULTR_API_KEY environment variable. func NewDNSProvider() (*DNSProvider, error) { - apiKey := os.Getenv("VULTR_API_KEY") - return NewDNSProviderCredentials(apiKey) + values, err := env.Get("VULTR_API_KEY") + if err != nil { + return nil, fmt.Errorf("Vultr: %v", err) + } + + return NewDNSProviderCredentials(values["VULTR_API_KEY"]) } // NewDNSProviderCredentials uses the supplied credentials to return a DNSProvider @@ -31,25 +35,21 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) { return nil, fmt.Errorf("Vultr credentials missing") } - c := &DNSProvider{ - client: vultr.NewClient(apiKey, nil), - } - - return c, nil + return &DNSProvider{client: vultr.NewClient(apiKey, nil)}, nil } // Present creates a TXT record to fulfil the DNS-01 challenge. -func (c *DNSProvider) Present(domain, token, keyAuth string) error { +func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) - zoneDomain, err := c.getHostedZone(domain) + zoneDomain, err := d.getHostedZone(domain) if err != nil { return err } - name := c.extractRecordName(fqdn, zoneDomain) + name := d.extractRecordName(fqdn, zoneDomain) - err = c.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) + err = d.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) if err != nil { return fmt.Errorf("Vultr API call failed: %v", err) } @@ -58,16 +58,16 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error { } // CleanUp removes the TXT record matching the specified parameters. -func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) - zoneDomain, records, err := c.findTxtRecords(domain, fqdn) + zoneDomain, records, err := d.findTxtRecords(domain, fqdn) if err != nil { return err } for _, rec := range records { - err := c.client.DeleteDNSRecord(zoneDomain, rec.RecordID) + err := d.client.DeleteDNSRecord(zoneDomain, rec.RecordID) if err != nil { return err } @@ -75,17 +75,17 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func (c *DNSProvider) getHostedZone(domain string) (string, error) { - domains, err := c.client.GetDNSDomains() +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) } var hostedDomain vultr.DNSDomain - for _, d := range domains { - if strings.HasSuffix(domain, d.Domain) { - if len(d.Domain) > len(hostedDomain.Domain) { - hostedDomain = d + for _, dom := range domains { + if strings.HasSuffix(domain, dom.Domain) { + if len(dom.Domain) > len(hostedDomain.Domain) { + hostedDomain = dom } } } @@ -96,19 +96,19 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) { return hostedDomain.Domain, nil } -func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) { - zoneDomain, err := c.getHostedZone(domain) +func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) { + zoneDomain, err := d.getHostedZone(domain) if err != nil { return "", nil, err } var records []vultr.DNSRecord - result, err := c.client.GetDNSRecords(zoneDomain) + result, err := d.client.GetDNSRecords(zoneDomain) if err != nil { return "", records, fmt.Errorf("Vultr API call has failed: %v", err) } - recordName := c.extractRecordName(fqdn, zoneDomain) + recordName := d.extractRecordName(fqdn, zoneDomain) for _, record := range result { if record.Type == "TXT" && record.Name == recordName { records = append(records, record) @@ -118,7 +118,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRe return zoneDomain, records, nil } -func (c *DNSProvider) extractRecordName(fqdn, domain string) string { +func (d *DNSProvider) extractRecordName(fqdn, domain string) string { name := acme.UnFqdn(fqdn) if idx := strings.Index(name, "."+domain); idx != -1 { return name[:idx]