blob: b895181a8096e48406970097b19b7f364397c160 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#include "kudu/util/cloud/instance_metadata.h"
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "kudu/gutil/macros.h"
#include "kudu/util/curl_util.h"
#include "kudu/util/faststring.h"
#include "kudu/util/flag_tags.h"
#include "kudu/util/monotime.h"
#include "kudu/util/status.h"
// The timeout should be high enough to work effectively, but as low as possible
// to avoid slowing down detection of running in non-cloud environments. As of
// now, the metadata servers of major public cloud providers is robust enough
// to send the response in a fraction of a second.
DEFINE_uint32(cloud_metadata_server_request_timeout_ms, 500,
"Timeout for HTTP/HTTPS requests to the instance metadata server "
"(in milliseconds)");
TAG_FLAG(cloud_metadata_server_request_timeout_ms, advanced);
TAG_FLAG(cloud_metadata_server_request_timeout_ms, runtime);
// The flags below are very unlikely to be customized since they are a part
// of the public API provided by the cloud providers. They are here for
// the peace of mind to be able to adapt for the changes in the cloud
// environment without the need to recompile the binaries.
// See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/\
// ec2-instance-metadata.html#instancedata-data-retrieval
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/\
// ec2-instance-metadata.html#instancedata-data-categories
DEFINE_string(cloud_aws_instance_id_url,
"http://169.254.169.254/latest/meta-data/instance-id",
"The URL to fetch the identifier of an AWS instance");
TAG_FLAG(cloud_aws_instance_id_url, advanced);
TAG_FLAG(cloud_aws_instance_id_url, runtime);
// See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html
// for details.
DEFINE_string(cloud_aws_ntp_server, "169.254.169.123",
"IP address/FQDN of the internal NTP server to use from within "
"an AWS instance");
TAG_FLAG(cloud_aws_ntp_server, advanced);
TAG_FLAG(cloud_aws_ntp_server, runtime);
// See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ \
// instance-metadata-service#retrieving-metadata for details.
DEFINE_string(cloud_azure_instance_id_url,
"http://169.254.169.254/metadata/instance/compute/vmId?"
"api-version=2018-10-01&format=text",
"The URL to fetch the identifier of an Azure instance");
TAG_FLAG(cloud_azure_instance_id_url, advanced);
TAG_FLAG(cloud_azure_instance_id_url, runtime);
// See https://cloud.google.com/compute/docs/instances/managing-instances# \
// configure_ntp_for_your_instances for details.
DEFINE_string(cloud_gce_ntp_server, "metadata.google.internal",
"IP address/FQDN of the internal NTP server to use from within "
"a GCE instance");
TAG_FLAG(cloud_gce_ntp_server, advanced);
TAG_FLAG(cloud_gce_ntp_server, runtime);
// See https://cloud.google.com/compute/docs/storing-retrieving-metadata
// for details.
DEFINE_string(cloud_gce_instance_id_url,
"http://metadata.google.internal/computeMetadata/v1/instance/id",
"The URL to fetch the identifier of a GCE instance");
TAG_FLAG(cloud_gce_instance_id_url, advanced);
TAG_FLAG(cloud_gce_instance_id_url, runtime);
using std::string;
using std::vector;
namespace kudu {
namespace cloud {
const char* TypeToString(CloudType type) {
static const char* const kTypeAws = "AWS";
static const char* const kTypeAzure = "Azure";
static const char* const kTypeGce = "GCE";
switch (type) {
case CloudType::AWS:
return kTypeAws;
case CloudType::AZURE:
return kTypeAzure;
case CloudType::GCE:
return kTypeGce;
default:
LOG(FATAL) << static_cast<uint16_t>(type) << ": unknown cloud type";
break; // unreachable
}
}
InstanceMetadataBase::InstanceMetadataBase()
: is_initialized_(false) {
}
Status InstanceMetadataBase::Init() {
// As of now, fetching the instance identifier from metadata service is
// the criterion for successful initialization of the instance metadata.
DCHECK(!is_initialized_);
RETURN_NOT_OK(FetchInstanceId(&id_));
DCHECK(!id_.empty());
is_initialized_ = true;
return Status::OK();
}
const string& InstanceMetadataBase::id() const {
CHECK(is_initialized_);
return id_;
}
MonoDelta InstanceMetadataBase::request_timeout() const {
return MonoDelta::FromMilliseconds(
FLAGS_cloud_metadata_server_request_timeout_ms);
}
Status InstanceMetadataBase::Fetch(const string& url,
MonoDelta timeout,
const vector<string>& headers,
string* out) {
DCHECK(out);
EasyCurl curl;
curl.set_timeout(timeout);
faststring resp;
RETURN_NOT_OK(curl.FetchURL(url, &resp, headers));
*out = resp.ToString();
return Status::OK();
}
Status InstanceMetadataBase::FetchInstanceId(string* id) {
return Fetch(instance_id_url(), request_timeout(), request_headers(), id);
}
Status AwsInstanceMetadata::GetNtpServer(string* server) const {
DCHECK(server);
*server = FLAGS_cloud_aws_ntp_server;
return Status::OK();
}
const vector<string>& AwsInstanceMetadata::request_headers() const {
// EC2 doesn't require any specific headers supplied with a generic query
// to the metadata server.
static const vector<string> kRequestHeaders = {};
return kRequestHeaders;
}
const string& AwsInstanceMetadata::instance_id_url() const {
return FLAGS_cloud_aws_instance_id_url;
}
Status AzureInstanceMetadata::GetNtpServer(string* /* server */) const {
// An Azure instance doesn't have access to dedicated NTP servers: Azure
// doesn't provide such a service.
return Status::NotSupported("Azure doesn't provide a dedicated NTP server");
}
const vector<string>& AzureInstanceMetadata::request_headers() const {
static const vector<string> kRequestHeaders = { "Metadata:true" };
return kRequestHeaders;
}
const string& AzureInstanceMetadata::instance_id_url() const {
return FLAGS_cloud_azure_instance_id_url;
}
Status GceInstanceMetadata::GetNtpServer(string* server) const {
DCHECK(server);
*server = FLAGS_cloud_gce_ntp_server;
return Status::OK();
}
const vector<string>& GceInstanceMetadata::request_headers() const {
static const vector<string> kHeaders = { "Metadata-Flavor:Google" };
return kHeaders;
}
const string& GceInstanceMetadata::instance_id_url() const {
return FLAGS_cloud_gce_instance_id_url;
}
} // namespace cloud
} // namespace kudu