├── .gcloudignore ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build ├── x86_64_centos7 │ └── Dockerfile └── x86_64_xenial │ └── Dockerfile ├── pkg ├── cloudbuild.yaml ├── deb │ └── debian │ │ ├── control.base │ │ └── control.xenial ├── docker │ ├── Dockerfile │ ├── Dockerfile-trusty │ ├── Dockerfile-xenial │ └── entrypoint.sh └── rpm │ └── stackdriver-metadata.spec ├── src ├── .gitignore ├── Makefile ├── agent.cc ├── agent.h ├── api_server.cc ├── api_server.h ├── asio │ ├── local_resolve_op.hpp │ └── local_resolver_service.hpp ├── base64.cc ├── base64.h ├── configuration.cc ├── configuration.h ├── docker.cc ├── docker.h ├── environment.cc ├── environment.h ├── format.cc ├── format.h ├── health_checker.cc ├── health_checker.h ├── http │ ├── local_async_connection_base.hpp │ ├── local_async_connection_base.ipp │ ├── local_async_normal.hpp │ ├── local_client.hpp │ ├── local_connection_delegate.hpp │ ├── local_connection_delegate_factory.hpp │ ├── local_normal_delegate.hpp │ ├── local_normal_delegate.ipp │ ├── local_tags.hpp │ └── traits │ │ └── local_resolver.hpp ├── http_common.h ├── instance.cc ├── instance.h ├── json.cc ├── json.h ├── kubernetes.cc ├── kubernetes.h ├── local_stream_delegate.cc ├── local_stream_http.cc ├── local_stream_http.h ├── logging.cc ├── logging.h ├── metadatad.cc ├── oauth2.cc ├── oauth2.h ├── reporter.cc ├── reporter.h ├── resource.cc ├── resource.h ├── sample_agent_config.yaml ├── store.cc ├── store.h ├── time.cc ├── time.h ├── updater.cc ├── updater.h └── util.h └── test ├── .gitignore ├── Makefile ├── api_server_unittest.cc ├── base64_unittest.cc ├── configuration_unittest.cc ├── environment_unittest.cc ├── environment_util.h ├── fake_clock.cc ├── fake_clock.h ├── fake_http_server.cc ├── fake_http_server.h ├── format_unittest.cc ├── health_checker_unittest.cc ├── instance_unittest.cc ├── json_unittest.cc ├── kubernetes_unittest.cc ├── logging_unittest.cc ├── oauth2_unittest.cc ├── reporter_unittest.cc ├── resource_unittest.cc ├── store_unittest.cc ├── temp_file.h ├── time_unittest.cc ├── updater_unittest.cc └── util_unittest.cc /.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | # This file is intentionally left blank to include all git submodules. 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/cpp-netlib"] 2 | path = lib/cpp-netlib 3 | url = https://github.com/cpp-netlib/cpp-netlib 4 | ignore = dirty 5 | [submodule "lib/yaml-cpp"] 6 | path = lib/yaml-cpp 7 | url = https://github.com/jbeder/yaml-cpp 8 | ignore = dirty 9 | [submodule "lib/googletest"] 10 | path = lib/googletest 11 | url = https://github.com/google/googletest 12 | ignore = dirty 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | os: ubuntu 3 | dist: xenial 4 | compiler: 5 | - clang 6 | - gcc 7 | install: sudo apt-get update && sudo apt-get install cmake dpkg-dev libyajl-dev libssl-dev libboost1.58-dev libboost-system1.58-dev libboost-atomic1.58-dev libboost-chrono1.58-dev libboost-date-time1.58-dev libboost-filesystem1.58-dev libboost-program-options1.58-dev libboost-regex1.58-dev libboost-thread1.58-dev libboost-timer1.58-dev 8 | script: cd src && make test 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is the Stackdriver Metadata agent. 2 | 3 | # Prerequisites 4 | 5 | ## Ubuntu 14.04 (trusty) 6 | 7 | 1. Install runtime dependencies: 8 | 9 | $ sudo apt-get install -y libboost-filesystem1.55.0 \ 10 | libboost-program-options1.55.0 libboost-system1.55.0 \ 11 | libboost-thread1.55.0 libyajl2 12 | 13 | 2. Install build dependencies: 14 | 15 | $ sudo apt-get install -y cmake dpkg-dev g++ libboost-atomic1.55-dev \ 16 | libboost-chrono1.55-dev libboost-date-time1.55-dev \ 17 | libboost-filesystem1.55-dev libboost-program-options1.55-dev \ 18 | libboost-regex1.55-dev libboost-system1.55-dev \ 19 | libboost-thread1.55-dev libboost-timer1.55-dev libboost1.55-dev \ 20 | libssl-dev libyajl-dev 21 | 22 | ## Ubuntu 16.04 (xenial) 23 | 24 | 1. Install runtime dependencies: 25 | 26 | $ sudo apt-get install -y ca-certificates libboost-filesystem1.58.0 \ 27 | libboost-program-options1.58.0 libboost-system1.58.0 \ 28 | libboost-thread1.58.0 libssl1.0.0 libyajl2 29 | 30 | 2. Install build dependencies: 31 | 32 | $ sudo apt-get install -y cmake dpkg-dev g++ libboost-atomic1.58-dev \ 33 | libboost-chrono1.58-dev libboost-date-time1.58-dev \ 34 | libboost-filesystem1.58-dev libboost-program-options1.58-dev \ 35 | libboost-regex1.58-dev libboost-system1.58-dev \ 36 | libboost-thread1.58-dev libboost-timer1.58-dev libboost1.58-dev \ 37 | libssl-dev libyajl-dev 38 | 39 | ## Debian 9 (stretch) 40 | 41 | 1. Install runtime dependencies: 42 | 43 | $ sudo apt-get install -y ca-certificates libboost-filesystem1.62.0 \ 44 | libboost-program-options1.62.0 libboost-system1.62.0 \ 45 | libboost-thread1.62.0 libssl1.0.2 libyajl2 46 | 47 | 2. Install build dependencies: 48 | 49 | $ sudo apt-get install -y cmake dpkg-dev g++ libboost-atomic1.62-dev \ 50 | libboost-chrono1.62-dev libboost-date-time1.62-dev \ 51 | libboost-filesystem1.62-dev libboost-program-options1.62-dev \ 52 | libboost-regex1.62-dev libboost-system1.62-dev \ 53 | libboost-thread1.62-dev libboost-timer1.62-dev libboost1.62-dev \ 54 | libssl1.0-dev libyajl-dev 55 | 56 | ## CentOS 7 57 | 58 | 1. Prepare external boost repo: 59 | 60 | $ sudo tee /etc/yum.repos.d/puias-computational-x86_64.repo << EOM 61 | [puias-computational-x86_64] 62 | name=PUIAS Computational x86_64 63 | baseurl=http://springdale.math.ias.edu/data/puias/computational/7/x86_64 64 | enabled=1 65 | gpgcheck=1 66 | EOM 67 | $ sudo rpm --import http://springdale.math.ias.edu/data/puias/7/x86_64/os/RPM-GPG-KEY-puias 68 | 69 | 2. Install runtime dependencies: 70 | 71 | $ sudo yum install -y boost155-filesystem boost155-program-options \ 72 | boost155-system boost155-thread yajl 73 | 74 | 3. Install build dependencies: 75 | 76 | $ sudo yum install -y boost155-devel cmake gcc-c++ make openssl-devel \ 77 | rpm-build yajl-devel 78 | 79 | 4. Set up Boost root: 80 | 81 | $ sudo tee /etc/profile.d/boost.sh << EOM 82 | export BOOST_ROOT=/usr/local/boost/1.55.0 83 | export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$BOOST_ROOT/lib64" 84 | EOM 85 | $ . /etc/profile.d/boost.sh 86 | 87 | ## MacOS 10.12 88 | 89 | 1. Install runtime dependencies: 90 | 91 | *Note: this currently does not work with boost@1.67, which is the latest 92 | available from Homebrew as of 2018/05/17*. 93 | 94 | $ brew install openssl 95 | $ brew install boost\@1.60 -c++11 && brew link --force boost\@1.60 && \ 96 | (cd /usr/local/lib && ln -s libboost_thread-mt.a libboost_thread.a && \ 97 | ln -s libboost_thread-mt.dylib libboost_thread.dylib) 98 | $ brew install yajl 99 | 100 | 2. Install build dependencies: 101 | 102 | $ brew install cmake 103 | 104 | # Building 105 | 106 | 1. Build the metadata agent: 107 | 108 | $ cd src 109 | $ make -j10 110 | 111 | # Testing 112 | 113 | 1. Run all tests: 114 | 115 | $ cd src 116 | $ make test 117 | 118 | 2. Run individual tests: 119 | 120 | $ cd test 121 | $ make 122 | $ ./ 123 | 124 | # Packaging 125 | 126 | 1. Build the DEB package: 127 | 128 | $ cd src 129 | $ make deb 130 | 131 | If you want to embed the distro name (e.g., "xenial") into the package 132 | filename, use: 133 | 134 | $ cd src 135 | $ make DISTRO=xenial deb 136 | 137 | 2. Build the RPM package: 138 | 139 | $ cd src 140 | $ make rpm 141 | 142 | If you want to embed the distro name (e.g., "el7") into the package 143 | filename, use: 144 | 145 | $ cd src 146 | $ make DISTRO=el7 rpm 147 | 148 | # Running 149 | 150 | 1. Run the agent with default settings: 151 | 152 | $ cd src 153 | $ ./metadatad 154 | 155 | The agent will use the default credentials locations or the metadata server. 156 | 157 | 2. Run the agent with modified configuration: 158 | 159 | $ cd src 160 | $ ./metadatad sample_agent_config.yaml 161 | 162 | The default credentials location in the sample configuration is `/tmp/token.json`. 163 | -------------------------------------------------------------------------------- /build/x86_64_centos7/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | RUN yum update -y && yum install -y \ 4 | cmake \ 5 | curl \ 6 | expect \ 7 | gcc-c++ \ 8 | git \ 9 | make \ 10 | openssl-devel \ 11 | rpm-build \ 12 | rpm-sign \ 13 | tar \ 14 | yajl-devel \ 15 | && yum -y clean all \ 16 | && rm -rf /var/cache/yum /tmp/yum.log \ 17 | && cd /tmp \ 18 | && curl -sSLO https://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.tar.bz2 \ 19 | && tar xjf boost_1_58_0.tar.bz2 \ 20 | && cd /tmp/boost_1_58_0 \ 21 | && ./bootstrap.sh --with-libraries=atomic,chrono,date_time,filesystem,program_options,regex,system,thread,timer \ 22 | && ./b2 cxxflags="-std=c++11" linkflags="-std=c++11" install \ 23 | && rm -rf /tmp/boost_1_58_0 /tmp/boost_1_58_0.tar.bz2 24 | ENV BOOST_ROOT=/usr/local 25 | ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$BOOST_ROOT/lib" 26 | 27 | -------------------------------------------------------------------------------- /build/x86_64_xenial/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:xenial 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | RUN apt-get update -y && apt-get install -y \ 5 | cmake \ 6 | dpkg-dev \ 7 | g++ \ 8 | git \ 9 | libboost-atomic1.58-dev \ 10 | libboost-chrono1.58-dev \ 11 | libboost-date-time1.58-dev \ 12 | libboost-filesystem1.58-dev \ 13 | libboost-program-options1.58-dev \ 14 | libboost-regex1.58-dev \ 15 | libboost-system1.58-dev \ 16 | libboost-thread1.58-dev \ 17 | libboost-timer1.58-dev \ 18 | libboost1.58-dev \ 19 | libssl-dev \ 20 | libyajl-dev \ 21 | make \ 22 | && rm -rf /var/lib/apt/lists/* 23 | -------------------------------------------------------------------------------- /pkg/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: 4 | - build 5 | - -t 6 | - gcr.io/$PROJECT_ID/metadata-agent-xenial-build-container 7 | - build/x86_64_xenial 8 | - name: 'gcr.io/cloud-builders/docker' 9 | args: 10 | - run 11 | - -v 12 | - /workspace:/workspace 13 | - -w 14 | - /workspace/src/ 15 | - gcr.io/$PROJECT_ID/metadata-agent-xenial-build-container 16 | - /bin/bash 17 | - -c 18 | - make purge && make -j4 DEB_PKG=stackdriver-metadata.deb DISTRO=xenial deb 19 | # TODO: Remove the need for this `cp` build step. 20 | - name: 'ubuntu:xenial' 21 | args: 22 | - cp 23 | - /workspace/src/stackdriver-metadata.deb 24 | - /workspace/pkg/docker/stackdriver-metadata.deb 25 | - name: 'gcr.io/cloud-builders/docker' 26 | args: 27 | - build 28 | - --build-arg 29 | - package=stackdriver-metadata.deb 30 | - -t 31 | - gcr.io/$PROJECT_ID/metadata-agent-xenial:$BUILD_ID 32 | - -f 33 | - pkg/docker/Dockerfile-xenial 34 | - pkg/docker 35 | images: 36 | # The $BUILD_ID can be used to inspect the build logs to determine the version 37 | # of the metadata agent that is included in the build. 38 | - 'gcr.io/$PROJECT_ID/metadata-agent-xenial:$BUILD_ID' 39 | 40 | -------------------------------------------------------------------------------- /pkg/deb/debian/control.base: -------------------------------------------------------------------------------- 1 | Source: stackdriver-metadata 2 | Maintainer: ${maintainer} 3 | Section: misc 4 | Priority: optional 5 | Build-Depends: g++, cmake, dpkg-dev, libyajl-dev (>= 2.0), libssl-dev, libboost1.55-dev, libboost-system1.55-dev, libboost-atomic1.55-dev, libboost-chrono1.55-dev, libboost-date-time1.55-dev, libboost-filesystem1.55-dev, libboost-program-options1.55-dev, libboost-regex1.55-dev, libboost-thread1.55-dev, libboost-timer1.55-dev 6 | 7 | Package: stackdriver-metadata 8 | Architecture: amd64 9 | Depends: libyajl2, libboost-filesystem1.55.0, libboost-program-options1.55.0, libboost-system1.55.0, libboost-thread1.55.0, ca-certificates 10 | Description: Stackdriver metadata collection daemon 11 | The Stackdriver metadata daemon collects resource metadata and 12 | sends it to the Stackdriver service. 13 | -------------------------------------------------------------------------------- /pkg/deb/debian/control.xenial: -------------------------------------------------------------------------------- 1 | Source: stackdriver-metadata 2 | Maintainer: ${maintainer} 3 | Section: misc 4 | Priority: optional 5 | Build-Depends: g++, cmake, dpkg-dev, libyajl-dev (>= 2.0), libssl-dev, libboost1.58-dev, libboost-system1.58-dev, libboost-atomic1.58-dev, libboost-chrono1.58-dev, libboost-date-time1.58-dev, libboost-filesystem1.58-dev, libboost-program-options1.58-dev, libboost-regex1.58-dev, libboost-thread1.58-dev, libboost-timer1.58-dev 6 | 7 | Package: stackdriver-metadata 8 | Architecture: amd64 9 | Depends: libssl1.0.0, libyajl2, libboost-filesystem1.58.0, libboost-program-options1.58.0, libboost-system1.58.0, libboost-thread1.58.0, ca-certificates 10 | Description: Stackdriver metadata collection daemon 11 | The Stackdriver metadata daemon collects resource metadata and 12 | sends it to the Stackdriver service. 13 | -------------------------------------------------------------------------------- /pkg/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | Dockerfile-xenial -------------------------------------------------------------------------------- /pkg/docker/Dockerfile-trusty: -------------------------------------------------------------------------------- 1 | # To use directly, run: docker build --build-arg package=.trusty.deb -t -f Dockerfile-trusty 2 | FROM ubuntu:trusty 3 | 4 | ARG package 5 | ADD ${package} /stackdriver-metadata.deb 6 | RUN apt-get update \ 7 | && (dpkg -i /stackdriver-metadata.deb || true) \ 8 | && apt-get install -f -y \ 9 | && rm -rf /var/lib/apt/lists/* \ 10 | && rm -rf /stackdriver-metadata.deb 11 | 12 | CMD /opt/stackdriver/metadata/sbin/metadatad 13 | 14 | EXPOSE 8000 15 | -------------------------------------------------------------------------------- /pkg/docker/Dockerfile-xenial: -------------------------------------------------------------------------------- 1 | # To use directly, run: docker build --build-arg package=.xenial.deb -t -f Dockerfile-xenial 2 | FROM ubuntu:xenial 3 | 4 | EXPOSE 8000 5 | 6 | ARG version=0.0.21-1 7 | ARG package=https://storage.googleapis.com/stackdriver-container-alpha/deb/xenial/stackdriver-metadata_${version}.xenial.deb 8 | ADD ${package} /stackdriver-metadata.deb 9 | RUN apt-get update \ 10 | && (dpkg -i /stackdriver-metadata.deb || true) \ 11 | && apt-get install -f -y \ 12 | && rm -rf /var/lib/apt/lists/* \ 13 | && rm -rf /stackdriver-metadata.deb 14 | COPY entrypoint.sh /entrypoint.sh 15 | 16 | ENTRYPOINT ["/entrypoint.sh"] 17 | CMD ["/opt/stackdriver/metadata/sbin/metadatad"] 18 | -------------------------------------------------------------------------------- /pkg/docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -e 18 | 19 | # This docker image supports sending either a flag or a command as the docker 20 | # command. When a flag is sent, it will be passed on to the metadata agent 21 | # process. Anything else will be interpreted as the command to be run. 22 | # 23 | # Passing a flag. 24 | # $ docker run -it {image:tag} -v 25 | # 26 | # Passing a command. 27 | # $ docker run -it {image:tag} /bin/bash 28 | # 29 | # Default behavior uses CMD defined in Dockerfile. 30 | # $ docker run -it {image:tag} 31 | 32 | # Note: substring substitution is a bash-ism. 33 | if [ "${1:0:1}" = '-' ]; then 34 | set -- /opt/stackdriver/metadata/sbin/metadatad "$@" 35 | fi 36 | 37 | exec "$@" 38 | -------------------------------------------------------------------------------- /pkg/rpm/stackdriver-metadata.spec: -------------------------------------------------------------------------------- 1 | %define _prefix /opt/stackdriver/metadata 2 | %define _initddir /etc/rc.d/init.d 3 | 4 | Summary: Stackdriver metadata collection daemon 5 | Name: stackdriver-metadata 6 | Version: %{version} 7 | Release: %{release} 8 | License: Apache Software License 2.0 9 | Group: System Environment/Daemons 10 | Requires: yajl, boost-system >= 1.54.0, boost-thread >= 1.54.0 11 | 12 | %description 13 | The Stackdriver metadata daemon collects resource metadata and 14 | sends it to the Stackdriver service. 15 | 16 | %files 17 | %{_sbindir}/metadatad 18 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | init-submodules 2 | build-cpp-netlib 3 | build-yaml-cpp 4 | metadatad 5 | -------------------------------------------------------------------------------- /src/agent.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "agent.h" 18 | 19 | #include "configuration.h" 20 | #include "api_server.h" 21 | #include "reporter.h" 22 | #include "health_checker.h" 23 | 24 | namespace google { 25 | 26 | MetadataAgent::MetadataAgent(const Configuration& config) 27 | : config_(config), store_(config_), health_checker_(config, store_) {} 28 | 29 | MetadataAgent::~MetadataAgent() {} 30 | 31 | void MetadataAgent::Start() { 32 | metadata_api_server_.reset(new MetadataApiServer( 33 | config_, &health_checker_, store_, config_.MetadataApiNumThreads(), 34 | config_.MetadataApiBindAddress(), config_.MetadataApiPort())); 35 | reporter_.reset(new MetadataReporter( 36 | config_, &store_, config_.MetadataReporterIntervalSeconds())); 37 | } 38 | 39 | void MetadataAgent::Stop() { 40 | metadata_api_server_->Stop(); 41 | // TODO: Notify the metadata reporter as well. 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/agent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef AGENT_H_ 17 | #define AGENT_H_ 18 | 19 | #include 20 | 21 | #include "store.h" 22 | #include "health_checker.h" 23 | 24 | namespace google { 25 | 26 | // Configuration object. 27 | class Configuration; 28 | 29 | // A server that implements the metadata agent API. 30 | class MetadataApiServer; 31 | 32 | // A periodic reporter of metadata to Stackdriver. 33 | class MetadataReporter; 34 | 35 | // Runs the metadata tasks. 36 | class MetadataAgent { 37 | public: 38 | MetadataAgent(const Configuration& config); 39 | ~MetadataAgent(); 40 | 41 | // Starts serving. 42 | void Start(); 43 | 44 | // Stops serving. 45 | void Stop(); 46 | 47 | const Configuration& config() const { 48 | return config_; 49 | } 50 | 51 | const MetadataStore& store() const { 52 | return store_; 53 | } 54 | 55 | MetadataStore* mutable_store() { 56 | return &store_; 57 | } 58 | 59 | HealthChecker* health_checker() { 60 | return &health_checker_; 61 | } 62 | 63 | private: 64 | const Configuration& config_; 65 | 66 | // The store for the metadata. 67 | MetadataStore store_; 68 | 69 | HealthChecker health_checker_; 70 | 71 | // The Metadata API server. 72 | std::unique_ptr metadata_api_server_; 73 | // The metadata reporter. 74 | std::unique_ptr reporter_; 75 | }; 76 | 77 | } 78 | 79 | #endif // AGENT_H_ 80 | -------------------------------------------------------------------------------- /src/api_server.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "api_server.h" 18 | 19 | #include 20 | 21 | #include "configuration.h" 22 | #include "health_checker.h" 23 | #include "http_common.h" 24 | #include "logging.h" 25 | #include "store.h" 26 | 27 | namespace google { 28 | 29 | MetadataApiServer::Dispatcher::Dispatcher( 30 | const HandlerMap& handlers, bool verbose) 31 | : handlers_(handlers), verbose_(verbose) {} 32 | 33 | void MetadataApiServer::Dispatcher::operator()( 34 | const HttpServer::request& request, 35 | std::shared_ptr conn) const { 36 | if (verbose_) { 37 | LOG(INFO) << "Dispatcher called: " << request.method 38 | << " " << request.destination 39 | << " headers: " << request.headers 40 | << " body: " << request.body; 41 | } 42 | // Look for the longest match first. This means going backwards through 43 | // the map, since strings are sorted in lexicographical order. 44 | for (auto it = handlers_.crbegin(); it != handlers_.crend(); ++it) { 45 | const std::string& method = it->first.first; 46 | const std::string& prefix = it->first.second; 47 | #ifdef VERBOSE 48 | LOG(DEBUG) << "Checking " << method << " " << prefix; 49 | #endif 50 | if (request.method != method || request.destination.find(prefix) != 0) { 51 | #ifdef VERBOSE 52 | LOG(DEBUG) << "No match; skipping " << method << " " << prefix; 53 | #endif 54 | continue; 55 | } 56 | #ifdef VERBOSE 57 | LOG(DEBUG) << "Handler found for " << request.method 58 | << " " << request.destination; 59 | #endif 60 | const Handler& handler = it->second; 61 | handler(request, conn); 62 | } 63 | } 64 | 65 | void MetadataApiServer::Dispatcher::log(const HttpServer::string_type& info) const { 66 | LOG(ERROR) << info; 67 | } 68 | 69 | 70 | MetadataApiServer::MetadataApiServer(const Configuration& config, 71 | const HealthChecker* health_checker, 72 | const MetadataStore& store, 73 | int server_threads, 74 | const std::string& host, int port) 75 | : config_(config), health_checker_(health_checker), store_(store), 76 | dispatcher_({ 77 | {{"GET", "/monitoredResource/"}, 78 | [=](const HttpServer::request& request, 79 | std::shared_ptr conn) { 80 | HandleMonitoredResource(request, conn); 81 | }}, 82 | {{"GET", "/healthz"}, 83 | [=](const HttpServer::request& request, 84 | std::shared_ptr conn) { 85 | HandleHealthz(request, conn); 86 | }}, 87 | }, config_.VerboseLogging()), 88 | server_( 89 | HttpServer::options(dispatcher_) 90 | .address(host) 91 | .port(std::to_string(port)) 92 | .reuse_address(true)), 93 | server_pool_() 94 | { 95 | for (int i : boost::irange(0, server_threads)) { 96 | server_pool_.emplace_back([=]() { server_.run(); }); 97 | } 98 | } 99 | 100 | void MetadataApiServer::Stop() { 101 | server_.stop(); 102 | LOG(INFO) << "API server stopped"; 103 | } 104 | 105 | MetadataApiServer::~MetadataApiServer() { 106 | Stop(); 107 | for (auto& thread : server_pool_) { 108 | thread.join(); 109 | } 110 | } 111 | 112 | void MetadataApiServer::HandleMonitoredResource( 113 | const HttpServer::request& request, 114 | std::shared_ptr conn) { 115 | // The format for the local metadata API request is: 116 | // {host}:{port}/monitoredResource/{id} 117 | static const std::string kPrefix = "/monitoredResource/";; 118 | const std::string id = request.destination.substr(kPrefix.size()); 119 | if (config_.VerboseLogging()) { 120 | LOG(INFO) << "Handler called for " << id; 121 | } 122 | try { 123 | const MonitoredResource& resource = store_.LookupResource(id); 124 | if (config_.VerboseLogging()) { 125 | LOG(INFO) << "Found resource for " << id << ": " << resource; 126 | } 127 | conn->set_status(HttpServer::connection::ok); 128 | 129 | std::string response = resource.ToJSON()->ToString(); 130 | 131 | conn->set_headers(std::map({ 132 | {"Connection", "close"}, 133 | {"Content-Length", std::to_string(response.size())}, 134 | {"Content-Type", "application/json"}, 135 | })); 136 | conn->write(response); 137 | } catch (const std::out_of_range& e) { 138 | // TODO: This could be considered log spam. 139 | // As we add more resource mappings, these will become less and less 140 | // frequent, and could be promoted to ERROR. 141 | if (config_.VerboseLogging()) { 142 | LOG(WARNING) << "No matching resource for " << id; 143 | } 144 | conn->set_status(HttpServer::connection::not_found); 145 | json::value json_response = json::object({ 146 | {"status_code", json::number(404)}, 147 | {"error", json::string("Not found")}, 148 | }); 149 | 150 | std::string response = json_response->ToString(); 151 | 152 | conn->set_headers(std::map({ 153 | {"Connection", "close"}, 154 | {"Content-Length", std::to_string(response.size())}, 155 | {"Content-Type", "application/json"}, 156 | })); 157 | conn->write(response); 158 | } 159 | } 160 | 161 | void MetadataApiServer::HandleHealthz( 162 | const HttpServer::request& request, 163 | std::shared_ptr conn) { 164 | std::set unhealthy_components; 165 | if (health_checker_ != nullptr) { 166 | unhealthy_components = health_checker_->UnhealthyComponents(); 167 | } 168 | if (unhealthy_components.empty()) { 169 | if (config_.VerboseLogging()) { 170 | LOG(INFO) << "/healthz returning 200"; 171 | } 172 | conn->set_status(HttpServer::connection::ok); 173 | 174 | std::string response = "healthy"; 175 | 176 | conn->set_headers(std::map({ 177 | {"Connection", "close"}, 178 | {"Content-Length", std::to_string(response.size())}, 179 | {"Content-Type", "text/plain"}, 180 | })); 181 | conn->write(response); 182 | } else { 183 | LOG(WARNING) << "/healthz returning 500; unhealthy components: " 184 | << boost::algorithm::join(unhealthy_components, ", "); 185 | conn->set_status(HttpServer::connection::internal_server_error); 186 | 187 | std::ostringstream response_stream("unhealthy components:\n"); 188 | for (const auto& component : unhealthy_components) { 189 | response_stream << component << "\n"; 190 | } 191 | 192 | std::string response = response_stream.str(); 193 | 194 | conn->set_headers(std::map({ 195 | {"Connection", "close"}, 196 | {"Content-Length", std::to_string(response.size())}, 197 | {"Content-Type", "text/plain"}, 198 | })); 199 | conn->write(response); 200 | } 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/api_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef API_SERVER_H_ 17 | #define API_SERVER_H_ 18 | 19 | #define BOOST_NETWORK_ENABLE_HTTPS 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace http = boost::network::http; 30 | 31 | namespace google { 32 | 33 | // Configuration object. 34 | class Configuration; 35 | 36 | class HealthChecker; 37 | 38 | // Storage for the metadata mapping. 39 | class MetadataStore; 40 | 41 | // A server that implements the metadata agent API. 42 | class MetadataApiServer { 43 | public: 44 | MetadataApiServer(const Configuration& config, 45 | const HealthChecker* health_checker, 46 | const MetadataStore& store, int server_threads, 47 | const std::string& host, int port); 48 | ~MetadataApiServer(); 49 | 50 | // Stops the server and closes the listening socket. 51 | void Stop(); 52 | 53 | private: 54 | friend class ApiServerTest; 55 | 56 | class Dispatcher; 57 | using HttpServer = http::server; 58 | using Handler = std::function)>; 60 | 61 | class Dispatcher { 62 | public: 63 | using HandlerMap = std::map, Handler>; 64 | Dispatcher(const HandlerMap& handlers, bool verbose); 65 | void operator()(const HttpServer::request& request, 66 | std::shared_ptr conn) const; 67 | void log(const HttpServer::string_type& info) const; 68 | private: 69 | // A mapping from a method/prefix pair to the handler function. 70 | // Order matters: later entries override earlier ones. 71 | const HandlerMap handlers_; 72 | bool verbose_; 73 | }; 74 | 75 | // Handler functions. 76 | void HandleMonitoredResource(const HttpServer::request& request, 77 | std::shared_ptr conn); 78 | void HandleHealthz(const HttpServer::request& request, 79 | std::shared_ptr conn); 80 | 81 | const Configuration& config_; 82 | const HealthChecker* health_checker_; 83 | const MetadataStore& store_; 84 | Dispatcher dispatcher_; 85 | HttpServer server_; 86 | std::vector server_pool_; 87 | }; 88 | 89 | } 90 | 91 | #endif // API_SERVER_H_ 92 | -------------------------------------------------------------------------------- /src/asio/local_resolve_op.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // detail/local_resolve_op.hpp 3 | // ~~~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // Copyright 2017 Igor Peshansky (igorp@google.com). 7 | // Copyright 2017 Google, Inc. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 11 | // 12 | 13 | #ifndef BOOST_ASIO_DETAIL_LOCAL_RESOLVE_OP_HPP 14 | #define BOOST_ASIO_DETAIL_LOCAL_RESOLVE_OP_HPP 15 | 16 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 17 | # pragma once 18 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "../logging.h" 39 | 40 | #include 41 | 42 | namespace boost { 43 | namespace asio { 44 | namespace detail { 45 | 46 | #define Protocol boost::asio::local::stream_protocol 47 | 48 | template 49 | class resolve_op : public operation 50 | { 51 | public: 52 | BOOST_ASIO_DEFINE_HANDLER_PTR(resolve_op); 53 | 54 | typedef boost::asio::ip::basic_resolver_query query_type; 55 | typedef boost::asio::ip::basic_resolver_iterator iterator_type; 56 | 57 | resolve_op(socket_ops::weak_cancel_token_type cancel_token, 58 | const query_type& query, io_service_impl& ios, Handler& handler) 59 | : operation(&resolve_op::do_complete), 60 | cancel_token_(cancel_token), 61 | query_(query), 62 | io_service_impl_(ios), 63 | handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)) 64 | { 65 | } 66 | 67 | ~resolve_op() 68 | { 69 | } 70 | 71 | static void do_complete(io_service_impl* owner, operation* base, 72 | const boost::system::error_code& /*ec*/, 73 | std::size_t /*bytes_transferred*/) 74 | { 75 | // Take ownership of the operation object. 76 | resolve_op* o(static_cast(base)); 77 | ptr p = { boost::asio::detail::addressof(o->handler_), o, o }; 78 | 79 | if (owner && owner != &o->io_service_impl_) 80 | { 81 | // The operation is being run on the worker io_service. Time to perform 82 | // the resolver operation. 83 | 84 | // Perform the blocking host resolution operation. 85 | if (o->cancel_token_.expired()) 86 | o->ec_ = boost::asio::error::operation_aborted; 87 | else { 88 | //LOG(ERROR) << "async_resolve() " << o->query_.host_name(); 89 | std::string path = ::network::detail::decode(o->query_.host_name()); 90 | //LOG(ERROR) << "decoded " << path; 91 | struct stat buffer; 92 | int result = stat(path.c_str(), &buffer); 93 | if (result || !S_ISSOCK(buffer.st_mode)) { 94 | LOG(ERROR) << "resolve: unable to stat or not a socket " << path; 95 | o->ec_ = boost::asio::error::host_not_found; 96 | } else { 97 | //LOG(ERROR) << "resolve: everything ok with " << path; 98 | o->ec_ = boost::system::error_code(); 99 | } 100 | } 101 | 102 | // Pass operation back to main io_service for completion. 103 | o->io_service_impl_.post_deferred_completion(o); 104 | p.v = p.p = 0; 105 | } 106 | else 107 | { 108 | // The operation has been returned to the main io_service. The completion 109 | // handler is ready to be delivered. 110 | 111 | BOOST_ASIO_HANDLER_COMPLETION((o)); 112 | 113 | // Make a copy of the handler so that the memory can be deallocated 114 | // before the upcall is made. Even if we're not about to make an upcall, 115 | // a sub-object of the handler may be the true owner of the memory 116 | // associated with the handler. Consequently, a local copy of the handler 117 | // is required to ensure that any owning sub-object remains valid until 118 | // after we have deallocated the memory here. 119 | detail::binder2 120 | handler(o->handler_, o->ec_, iterator_type()); 121 | p.h = boost::asio::detail::addressof(handler.handler_); 122 | if (!o->ec_) 123 | { 124 | std::string path = ::network::detail::decode(o->query_.host_name()); 125 | handler.arg2_ = iterator_type::create( 126 | Protocol::endpoint(path), 127 | path, o->query_.service_name()); 128 | } 129 | p.reset(); 130 | 131 | if (owner) 132 | { 133 | fenced_block b(fenced_block::half); 134 | BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, "...")); 135 | boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); 136 | BOOST_ASIO_HANDLER_INVOCATION_END; 137 | } 138 | } 139 | } 140 | 141 | private: 142 | socket_ops::weak_cancel_token_type cancel_token_; 143 | query_type query_; 144 | io_service_impl& io_service_impl_; 145 | Handler handler_; 146 | boost::system::error_code ec_; 147 | }; 148 | 149 | #undef Protocol 150 | 151 | } // namespace detail 152 | } // namespace asio 153 | } // namespace boost 154 | 155 | #include 156 | 157 | #endif // BOOST_ASIO_DETAIL_LOCAL_RESOLVE_OP_HPP 158 | -------------------------------------------------------------------------------- /src/asio/local_resolver_service.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // detail/local_resolver_service.hpp 3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 | // 5 | // Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 | // Copyright 2017 Igor Peshansky (igorp@google.com). 7 | // Copyright 2017 Google, Inc. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 10 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 11 | // 12 | 13 | #ifndef BOOST_ASIO_DETAIL_LOCAL_RESOLVER_SERVICE_HPP 14 | #define BOOST_ASIO_DETAIL_LOCAL_RESOLVER_SERVICE_HPP 15 | 16 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) 17 | # pragma once 18 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 19 | 20 | #include 21 | 22 | #if !defined(BOOST_ASIO_WINDOWS_RUNTIME) 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "../logging.h" 37 | 38 | #include 39 | 40 | namespace boost { 41 | namespace asio { 42 | namespace detail { 43 | 44 | #define Protocol boost::asio::local::stream_protocol 45 | 46 | template<> 47 | class resolver_service : public resolver_service_base 48 | { 49 | public: 50 | // The implementation type of the resolver. A cancellation token is used to 51 | // indicate to the background thread that the operation has been cancelled. 52 | typedef socket_ops::shared_cancel_token_type implementation_type; 53 | 54 | // The endpoint type. 55 | typedef typename Protocol::endpoint endpoint_type; 56 | 57 | // The query type. 58 | typedef boost::asio::ip::basic_resolver_query query_type; 59 | 60 | // The iterator type. 61 | typedef boost::asio::ip::basic_resolver_iterator iterator_type; 62 | 63 | // Constructor. 64 | resolver_service(boost::asio::io_service& io_service) 65 | : resolver_service_base(io_service) 66 | { 67 | //LOG(ERROR) << "Constructing resolver_service()"; 68 | } 69 | 70 | // Resolve a query to a list of entries. 71 | iterator_type resolve(implementation_type&, const query_type& query, 72 | boost::system::error_code& ec) 73 | { 74 | //LOG(ERROR) << "resolve() " << query.host_name(); 75 | std::string path = ::network::detail::decode(query.host_name()); 76 | //LOG(ERROR) << "decoded " << path; 77 | struct stat buffer; 78 | int result = stat(path.c_str(), &buffer); 79 | if (result || !S_ISSOCK(buffer.st_mode)) { 80 | LOG(ERROR) << "resolve: unable to stat or not a socket " << path; 81 | ec = boost::asio::error::host_not_found; 82 | } else { 83 | //LOG(ERROR) << "resolve: everything ok with " << path; 84 | ec = boost::system::error_code(); 85 | } 86 | return ec ? iterator_type() : iterator_type::create( 87 | endpoint_type(path), path, query.service_name()); 88 | } 89 | 90 | // Asynchronously resolve a query to a list of entries. 91 | template 92 | void async_resolve(implementation_type& impl, 93 | const query_type& query, Handler& handler) 94 | { 95 | // Allocate and construct an operation to wrap the handler. 96 | typedef resolve_op op; 97 | typename op::ptr p = { boost::asio::detail::addressof(handler), 98 | boost_asio_handler_alloc_helpers::allocate( 99 | sizeof(op), handler), 0 }; 100 | p.p = new (p.v) op(impl, query, io_service_impl_, handler); 101 | 102 | BOOST_ASIO_HANDLER_CREATION((p.p, "resolver", &impl, "async_resolve")); 103 | 104 | start_resolve_op(p.p); 105 | p.v = p.p = 0; 106 | } 107 | 108 | // Resolve an endpoint to a list of entries. 109 | iterator_type resolve(implementation_type&, 110 | const endpoint_type& endpoint, boost::system::error_code& ec) 111 | { 112 | // An existing endpoint always resolves. 113 | return iterator_type::create(endpoint, "", ""); 114 | } 115 | 116 | // Asynchronously resolve an endpoint to a list of entries. 117 | template 118 | void async_resolve(implementation_type& impl, 119 | const endpoint_type& endpoint, Handler& handler) 120 | { 121 | // Allocate and construct an operation to wrap the handler. 122 | typedef resolve_endpoint_op op; 123 | typename op::ptr p = { boost::asio::detail::addressof(handler), 124 | boost_asio_handler_alloc_helpers::allocate( 125 | sizeof(op), handler), 0 }; 126 | p.p = new (p.v) op(impl, endpoint, io_service_impl_, handler); 127 | 128 | BOOST_ASIO_HANDLER_CREATION((p.p, "resolver", &impl, "async_resolve")); 129 | 130 | start_resolve_op(p.p); 131 | p.v = p.p = 0; 132 | } 133 | }; 134 | 135 | #undef Protocol 136 | 137 | } // namespace detail 138 | } // namespace asio 139 | } // namespace boost 140 | 141 | #include 142 | 143 | #endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME) 144 | 145 | #endif // BOOST_ASIO_DETAIL_LOCAL_RESOLVER_SERVICE_HPP 146 | -------------------------------------------------------------------------------- /src/base64.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "base64.h" 18 | 19 | #include 20 | 21 | namespace base64 { 22 | 23 | namespace { 24 | constexpr const unsigned char base64url_chars[] = 25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 26 | } 27 | 28 | // See http://stackoverflow.com/questions/180947. 29 | std::string Encode(const std::string &source) { 30 | std::string result; 31 | #if 0 32 | const std::size_t remainder = (source.size() - 1) % 3; 33 | for (std::size_t i = 0; i < source.size(); i += 3) { 34 | const bool one_more = i < source.size() - 3 || remainder > 0; 35 | const bool two_more = i < source.size() - 3 || remainder > 1; 36 | const unsigned char c0 = source[i]; 37 | const unsigned char c1 = one_more ? source[i + 1] : 0u; 38 | const unsigned char c2 = two_more ? source[i + 2] : 0u; 39 | result.push_back(base64url_chars[0x3f & c0 >> 2]); 40 | result.push_back(base64url_chars[(0x3f & c0 << 4) | c1 >> 4]); 41 | if (one_more) { 42 | result.push_back(base64url_chars[(0x3f & c1 << 2) | c2 >> 6]); 43 | } 44 | if (two_more) { 45 | result.push_back(base64url_chars[0x3f & c2]); 46 | } 47 | } 48 | #else 49 | unsigned int code_buffer = 0; 50 | int code_buffer_size = -6; 51 | for (unsigned char c : source) { 52 | code_buffer = (code_buffer << 8) | c; 53 | code_buffer_size += 8; 54 | while (code_buffer_size >= 0) { 55 | result.push_back(base64url_chars[(code_buffer >> code_buffer_size) & 0x3f]); 56 | code_buffer_size -= 6; 57 | } 58 | } 59 | if (code_buffer_size > -6) { 60 | code_buffer = (code_buffer << 8); 61 | code_buffer_size += 8; 62 | result.push_back(base64url_chars[(code_buffer >> code_buffer_size) & 0x3f]); 63 | } 64 | #endif 65 | // No padding needed. 66 | return result; 67 | } 68 | 69 | std::string Decode(const std::string &source) { 70 | std::string result; 71 | 72 | std::vector T(256, -1); 73 | for (int i = 0; i < 64; i++) T[base64url_chars[i]] = i; 74 | 75 | int val = 0, shift = -8; 76 | for (char c : source) { 77 | if (T[c] == -1) break; 78 | val = (val << 6) + T[c]; 79 | shift += 6; 80 | if (shift >= 0) { 81 | result.push_back(char((val >> shift) & 0xFF)); 82 | shift -= 8; 83 | } 84 | } 85 | return result; 86 | } 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef BASE64_H_ 17 | #define BASE64_H_ 18 | 19 | #include 20 | 21 | namespace base64 { 22 | 23 | std::string Encode(const std::string &source); 24 | 25 | std::string Decode(const std::string &source); 26 | 27 | } 28 | 29 | #endif // BASE64_H_ 30 | -------------------------------------------------------------------------------- /src/docker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef DOCKER_H_ 17 | #define DOCKER_H_ 18 | 19 | #include 20 | 21 | #include "environment.h" 22 | #include "updater.h" 23 | 24 | namespace google { 25 | 26 | // Configuration object. 27 | class Configuration; 28 | 29 | // Storage for the metadata mapping. 30 | class MetadataStore; 31 | 32 | class DockerReader { 33 | public: 34 | DockerReader(const Configuration& config); 35 | // A Docker metadata query function. 36 | std::vector MetadataQuery() const; 37 | 38 | // Validates the relevant dynamic configuration and throws if it's incorrect. 39 | void ValidateDynamicConfiguration() const 40 | throw(MetadataUpdater::ConfigurationValidationError); 41 | 42 | private: 43 | // A representation of all query-related errors. 44 | class QueryException { 45 | public: 46 | QueryException(const std::string& what) : explanation_(what) {} 47 | const std::string& what() const { return explanation_; } 48 | private: 49 | std::string explanation_; 50 | }; 51 | class NonRetriableError; 52 | 53 | // Issues a Docker API query at a given path and returns a parsed 54 | // JSON response. The path has to start with "/". 55 | json::value QueryDocker(const std::string& path) const 56 | throw(QueryException, json::Exception); 57 | 58 | // Given a container object, return the associated metadata. 59 | MetadataUpdater::ResourceMetadata GetContainerMetadata( 60 | const json::Object* container, Timestamp collected_at) const 61 | throw(json::Exception); 62 | 63 | const Configuration& config_; 64 | Environment environment_; 65 | }; 66 | 67 | class DockerUpdater : public PollingMetadataUpdater { 68 | public: 69 | DockerUpdater(const Configuration& config, MetadataStore* store) 70 | : reader_(config), PollingMetadataUpdater( 71 | config, store, "DockerUpdater", 72 | config.DockerUpdaterIntervalSeconds(), 73 | [=]() { return reader_.MetadataQuery(); }) { } 74 | 75 | protected: 76 | void ValidateDynamicConfiguration() const throw(ConfigurationValidationError); 77 | 78 | private: 79 | DockerReader reader_; 80 | }; 81 | 82 | } 83 | 84 | #endif // DOCKER_H_ 85 | -------------------------------------------------------------------------------- /src/environment.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef ENVIRONMENT_H_ 17 | #define ENVIRONMENT_H_ 18 | 19 | #include 20 | #include 21 | 22 | #include "configuration.h" 23 | #include "json.h" 24 | 25 | namespace google { 26 | 27 | namespace testing { 28 | class EnvironmentUtil; 29 | } // testing 30 | 31 | class Environment { 32 | public: 33 | Environment(const Configuration& config); 34 | 35 | const std::string& ProjectId() const; 36 | const std::string& InstanceResourceType() const; 37 | const std::string& InstanceId() const; 38 | const std::string& InstanceZone() const; 39 | const std::string& KubernetesClusterName() const; 40 | const std::string& KubernetesClusterLocation() const; 41 | const std::string& CredentialsClientEmail() const; 42 | const std::string& CredentialsPrivateKey() const; 43 | 44 | std::string GetMetadataString(const std::string& path) const; 45 | 46 | const Configuration& config() const { 47 | return config_; 48 | } 49 | 50 | private: 51 | friend class EnvironmentTest; 52 | friend class testing::EnvironmentUtil; 53 | 54 | void ReadApplicationDefaultCredentials() const; 55 | 56 | // The url must end in a '/'. 57 | void SetMetadataServerUrlForTest(const std::string& url) { 58 | metadata_server_url_ = url; 59 | } 60 | 61 | const Configuration& config_; 62 | 63 | // Cached data. 64 | mutable std::mutex mutex_; 65 | mutable std::string project_id_; 66 | mutable std::string zone_; 67 | mutable std::string instance_id_; 68 | mutable std::string instance_resource_type_; 69 | mutable std::string kubernetes_cluster_name_; 70 | mutable std::string kubernetes_cluster_location_; 71 | mutable std::string client_email_; 72 | mutable std::string private_key_; 73 | mutable std::string credentials_project_id_; 74 | mutable std::string metadata_server_url_; 75 | mutable bool application_default_credentials_read_; 76 | }; 77 | 78 | } // google 79 | 80 | #endif // ENVIRONMENT_H_ 81 | -------------------------------------------------------------------------------- /src/format.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "format.h" 18 | 19 | #include 20 | 21 | namespace format { 22 | 23 | namespace { 24 | template 25 | std::string AsString(T v) { 26 | std::ostringstream out; 27 | out << v; 28 | return out.str(); 29 | } 30 | } 31 | 32 | std::string str(int i) { return AsString(i); } 33 | std::string str(double d) { return AsString(d); } 34 | std::string str(bool b) { return AsString(b); } 35 | 36 | std::string Substitute(const std::string& format, 37 | const std::map&& params) 38 | throw(Exception) { 39 | static constexpr const char kStart[] = "{{"; 40 | static constexpr const std::size_t kStartLen = sizeof(kStart) - 1; 41 | static constexpr const char kEnd[] = "}}"; 42 | static constexpr const std::size_t kEndLen = sizeof(kEnd) - 1; 43 | std::stringstream result; 44 | 45 | std::string::size_type pos = 0; 46 | for (std::string::size_type brace = format.find(kStart, pos); 47 | brace != std::string::npos; 48 | brace = format.find(kStart, pos)) { 49 | result << format.substr(pos, brace - pos); 50 | std::string::size_type open = brace + kStartLen; 51 | std::string::size_type close = format.find(kEnd, open); 52 | if (close == std::string::npos) { 53 | throw Exception("Unterminated placeholder at " + std::to_string(brace)); 54 | } 55 | std::string param = format.substr(open, close - open); 56 | auto value_it = params.find(param); 57 | if (value_it == params.end()) { 58 | throw Exception( 59 | "Unknown parameter '" + param + "' at " + std::to_string(pos)); 60 | } 61 | const std::string& value = value_it->second; 62 | result << value; 63 | pos = close + kEndLen; 64 | } 65 | result << format.substr(pos); 66 | return result.str(); 67 | } 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/format.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef FORMAT_H_ 17 | #define FORMAT_H_ 18 | 19 | #include 20 | #include 21 | 22 | namespace format { 23 | 24 | // A representation of format substitution errors. 25 | class Exception { 26 | public: 27 | Exception(const std::string& what) : explanation_(what) {} 28 | const std::string& what() const { return explanation_; } 29 | private: 30 | std::string explanation_; 31 | }; 32 | 33 | std::string str(int); 34 | std::string str(double); 35 | std::string str(bool); 36 | 37 | // Format string substitution. 38 | // Placeholder format is '{{param}}'. 39 | // All instances of each placeholder will be substituted by the value of the 40 | // corresponding parameter. 41 | std::string Substitute(const std::string& format, 42 | const std::map&& params) 43 | throw(Exception); 44 | 45 | } // format 46 | 47 | #endif // FORMAT_H_ 48 | -------------------------------------------------------------------------------- /src/health_checker.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "health_checker.h" 18 | 19 | #include "store.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace google { 27 | 28 | HealthChecker::HealthChecker(const Configuration& config, 29 | const MetadataStore& store) 30 | : config_(config), store_(store) {} 31 | 32 | void HealthChecker::SetUnhealthy(const std::string& component) { 33 | std::lock_guard lock(mutex_); 34 | unhealthy_components_.insert(component); 35 | } 36 | 37 | std::set HealthChecker::UnhealthyComponents() const { 38 | std::lock_guard lock(mutex_); 39 | std::set result(unhealthy_components_); 40 | 41 | Timestamp cutoff = std::chrono::system_clock::now() 42 | - std::chrono::seconds(config_.HealthCheckMaxDataAgeSeconds()); 43 | auto last_collection_times = store_.GetLastCollectionTimes(); 44 | for (const auto& kv : last_collection_times) { 45 | const std::string& object_type = kv.first; 46 | const Timestamp& collected_at = kv.second; 47 | if (collected_at < cutoff) { 48 | result.insert(object_type); 49 | } 50 | } 51 | return result; 52 | } 53 | 54 | } // namespace google 55 | -------------------------------------------------------------------------------- /src/health_checker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef HEALTH_CHECKER_H_ 17 | #define HEALTH_CHECKER_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "configuration.h" 25 | 26 | namespace google { 27 | 28 | // Storage for the metadata mapping. 29 | class MetadataStore; 30 | 31 | // Collects and reports health information about the metadata agent. 32 | class HealthChecker { 33 | public: 34 | HealthChecker(const Configuration& config, const MetadataStore& store); 35 | void SetUnhealthy(const std::string& component); 36 | std::set UnhealthyComponents() const; 37 | 38 | private: 39 | friend class HealthCheckerUnittest; 40 | 41 | const Configuration& config_; 42 | const MetadataStore& store_; 43 | std::set unhealthy_components_; 44 | mutable std::mutex mutex_; 45 | }; 46 | 47 | } // namespace google 48 | 49 | #endif // HEALTH_CHECKER_H_ 50 | -------------------------------------------------------------------------------- /src/http/local_async_connection_base.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_IMPL_LOCAL_ASYNC_CONNECTION_BASE_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_IMPL_LOCAL_ASYNC_CONNECTION_BASE_20170307 3 | 4 | // Note: This file is mostly a copy of 5 | // boost/network/protocol/http/client/connection/async_base.hpp, 6 | // except for the specialized bits. 7 | 8 | // Copyright 2017 Igor Peshansky (igorp@google.com). 9 | // Copyright 2013-2017 Google, Inc. 10 | // Copyright 2010 Dean Michael Berris 11 | // Copyright 2010 (C) Sinefunc, Inc. 12 | // Distributed under the Boost Software License, Version 1.0. 13 | // (See accompanying file LICENSE_1_0.txt or copy at 14 | // http://www.boost.org/LICENSE_1_0.txt) 15 | 16 | #include 17 | #include 18 | #include "traits/local_resolver.hpp" 19 | #include 20 | #include 21 | 22 | namespace boost { 23 | namespace network { 24 | namespace http { 25 | namespace impl { 26 | 27 | template 28 | struct async_connection_base; 29 | 30 | template 31 | struct http_async_connection; 32 | 33 | #define Tag tags::http_async_8bit_local_resolve 34 | #define version_major 1 35 | #define version_minor 1 36 | 37 | template<> struct async_connection_base { 38 | typedef async_connection_base this_type; 39 | typedef typename resolver_policy::type resolver_base; 40 | typedef typename resolver_base::resolver_type resolver_type; 41 | typedef typename resolver_base::resolve_function resolve_function; 42 | typedef typename string::type string_type; 43 | typedef basic_request request; 44 | typedef basic_response response; 45 | typedef typename std::array::type, 1024>::const_iterator const_iterator; 46 | typedef iterator_range char_const_range; 47 | typedef std::function 48 | body_callback_function_type; 49 | typedef std::function body_generator_function_type; 50 | typedef std::shared_ptr connection_ptr; 51 | 52 | // This is the factory function which constructs the appropriate async 53 | // connection implementation with the correct delegate chosen based on the 54 | // tag. 55 | static connection_ptr new_connection( 56 | resolve_function resolve, resolver_type &resolver, bool follow_redirect, 57 | bool always_verify_peer, bool https, int timeout, 58 | bool remove_chunk_markers, 59 | optional certificate_filename = optional(), 60 | optional const &verify_path = optional(), 61 | optional certificate_file = optional(), 62 | optional private_key_file = optional(), 63 | optional ciphers = optional(), 64 | optional sni_hostname = optional(), 65 | long ssl_options = 0); 66 | 67 | // This is the pure virtual entry-point for all asynchronous 68 | // connections. 69 | virtual response start(request const &request, string_type const &method, 70 | bool get_body, body_callback_function_type callback, 71 | body_generator_function_type generator) = 0; 72 | 73 | virtual ~async_connection_base() = default; 74 | }; 75 | 76 | #undef version_minor 77 | #undef version_major 78 | #undef Tag 79 | 80 | } // namespace impl 81 | } // namespace http 82 | } // namespace network 83 | } // namespace boost 84 | 85 | #endif // BOOST_NETWORK_PROTOCOL_HTTP_IMPL_LOCAL_ASYNC_CONNECTION_BASE_20170307 86 | -------------------------------------------------------------------------------- /src/http/local_async_connection_base.ipp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_IMPL_LOCAL_ASYNC_CONNECTION_BASE_IPP_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_IMPL_LOCAL_ASYNC_CONNECTION_BASE_IPP_20170307 3 | 4 | // Note: This file is mostly a copy of 5 | // boost/network/protocol/http/client/connection/async_base.hpp, 6 | // except for the specialized bits. 7 | 8 | // Copyright 2017 Igor Peshansky (igorp@google.com). 9 | // Copryight 2013-2017 Google, Inc. 10 | // Copyright 2010 Dean Michael Berris 11 | // Copyright 2010 (C) Sinefunc, Inc. 12 | // Distributed under the Boost Software License, Version 1.0. 13 | // (See accompanying file LICENSE_1_0.txt or copy at 14 | // http://www.boost.org/LICENSE_1_0.txt) 15 | 16 | #include 17 | #include "local_async_normal.hpp" 18 | #include "local_async_connection_base.hpp" 19 | 20 | namespace boost { 21 | namespace network { 22 | namespace http { 23 | namespace impl { 24 | 25 | #define Tag tags::http_async_8bit_local_resolve 26 | #define version_major 1 27 | #define version_minor 1 28 | 29 | using acb = async_connection_base; 30 | 31 | acb::connection_ptr acb::new_connection( 32 | acb::resolve_function resolve, acb::resolver_type &resolver, 33 | bool follow_redirect, bool always_verify_peer, bool https, int timeout, 34 | bool remove_chunk_markers, 35 | optional certificate_filename, 36 | optional const &verify_path, 37 | optional certificate_file, 38 | optional private_key_file, 39 | optional ciphers, 40 | optional sni_hostname, 41 | long ssl_options) { 42 | typedef http_async_connection 43 | async_connection; 44 | typedef typename delegate_factory::type delegate_factory_type; 45 | auto delegate = delegate_factory_type::new_connection_delegate( 46 | resolver.get_io_service(), https, always_verify_peer, 47 | certificate_filename, verify_path, certificate_file, private_key_file, 48 | ciphers, sni_hostname, ssl_options); 49 | auto temp = std::make_shared( 50 | resolver, resolve, follow_redirect, timeout, remove_chunk_markers, 51 | std::move(delegate)); 52 | BOOST_ASSERT(temp != nullptr); 53 | return temp; 54 | } 55 | 56 | #undef version_minor 57 | #undef version_major 58 | #undef Tag 59 | 60 | } // namespace impl 61 | } // namespace http 62 | } // namespace network 63 | } // namespace boost 64 | 65 | #endif // BOOST_NETWORK_PROTOCOL_HTTP_IMPL_LOCAL_ASYNC_CONNECTION_BASE_IPP_20170307 66 | -------------------------------------------------------------------------------- /src/http/local_client.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_LOCAL_CLIENT_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_LOCAL_CLIENT_20170307 3 | 4 | // Copyright 2017 Igor Peshansky (igorp@google.com). 5 | // Copyright 2017 Google, Inc. 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #include 11 | #include 12 | 13 | #include "traits/local_resolver.hpp" 14 | #include "local_tags.hpp" 15 | 16 | namespace boost { 17 | namespace network { 18 | namespace http { 19 | 20 | typedef basic_client local_client; 21 | 22 | } // namespace http 23 | 24 | } // namespace network 25 | 26 | } // namespace boost 27 | 28 | #endif // BOOST_NETWORK_PROTOCOL_HTTP_LOCAL_CLIENT_20170307 29 | -------------------------------------------------------------------------------- /src/http/local_connection_delegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_CONNECTION_DELEGATE_HPP_ 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_CONNECTION_DELEGATE_HPP_ 3 | 4 | // Note: This file is mostly a copy of 5 | // boost/network/protocol/http/client/connection/connection_delegate.hpp, 6 | // except for the specialized bits. 7 | 8 | // Copyright 2011 Dean Michael Berris (dberris@google.com). 9 | // Copyright 2017 Igor Peshansky (igorp@google.com). 10 | // Copyright 2011-2017 Google, Inc. 11 | // Distributed under the Boost Software License, Version 1.0. 12 | // (See accompanying file LICENSE_1_0.txt or copy at 13 | // http://www.boost.org/LICENSE_1_0.txt) 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace boost { 20 | namespace network { 21 | namespace http { 22 | namespace impl { 23 | 24 | struct local_connection_delegate { 25 | // TODO(igorp): this is similar enough to connection_delegate that it may be possible to refactor. 26 | virtual void connect(boost::asio::local::stream_protocol::endpoint &endpoint, 27 | std::function handler) = 0; 28 | virtual void write( 29 | boost::asio::streambuf &command_streambuf, 30 | std::function handler) = 0; 31 | virtual void read_some( 32 | boost::asio::mutable_buffers_1 const &read_buffer, 33 | std::function handler) = 0; 34 | virtual void disconnect() = 0; 35 | virtual ~local_connection_delegate() = default; 36 | }; 37 | 38 | } // namespace impl 39 | } // namespace http 40 | } // namespace network 41 | } // namespace boost 42 | 43 | #endif /* BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_CONNECTION_DELEGATE_HPP_ \ 44 | */ 45 | -------------------------------------------------------------------------------- /src/http/local_connection_delegate_factory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_LOCAL_CONNECTION_DELEGATE_FACTORY_HPP_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_LOCAL_CONNECTION_DELEGATE_FACTORY_HPP_20170307 3 | 4 | // Note: This file is mostly a copy of 5 | // boost/network/protocol/http/client/connection/connection_delegate_factory.hpp, 6 | // except for the specialized bits. 7 | 8 | // Copyright 2011 Dean Michael Berris (dberris@google.com). 9 | // Copyright 2017 Igor Peshansky (igorp@google.com). 10 | // Copyright 2011-2017 Google, Inc. 11 | // Distributed under the Boost Software License, Version 1.0. 12 | // (See accompanying file LICENSE_1_0.txt or copy at 13 | // http://www.boost.org/LICENSE_1_0.txt) 14 | 15 | #include 16 | #include 17 | #include "local_connection_delegate.hpp" 18 | #include "local_normal_delegate.hpp" 19 | #include "local_tags.hpp" 20 | 21 | namespace boost { 22 | namespace network { 23 | namespace http { 24 | namespace impl { 25 | 26 | template struct connection_delegate_factory; 27 | struct local_normal_delegate; 28 | 29 | #define Tag tags::http_async_8bit_local_resolve 30 | 31 | template<> struct connection_delegate_factory { 32 | typedef std::shared_ptr connection_delegate_ptr; 33 | typedef typename string::type string_type; 34 | 35 | // This is the factory method that actually returns the delegate instance. 36 | // TODO(dberris): Support passing in proxy settings when crafting connections. 37 | static connection_delegate_ptr new_connection_delegate( 38 | boost::asio::io_service& service, bool https, bool always_verify_peer, 39 | optional certificate_filename, 40 | optional verify_path, optional certificate_file, 41 | optional private_key_file, optional ciphers, 42 | optional sni_hostname, long ssl_options) { 43 | connection_delegate_ptr delegate; 44 | delegate = std::make_shared(service); 45 | return delegate; 46 | } 47 | }; 48 | 49 | #undef Tag 50 | 51 | } // namespace impl 52 | } // namespace http 53 | } // namespace network 54 | } // namespace boost 55 | 56 | #endif /* BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_LOCAL_CONNECTION_DELEGATE_FACTORY_HPP_20170307 \ 57 | */ 58 | -------------------------------------------------------------------------------- /src/http/local_normal_delegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_NORMAL_DELEGATE_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_NORMAL_DELEGATE_20170307 3 | 4 | // Note: This file is mostly a copy of 5 | // boost/network/protocol/http/client/connection/normal_delegate.hpp, 6 | // except for the specialized bits. 7 | 8 | // Copyright 2011 Dean Michael Berris (dberris@google.com). 9 | // Copyright 2017 Igor Peshansky (igorp@google.com). 10 | // Copyright 2011-2017 Google, Inc. 11 | // Distributed under the Boost Software License, Version 1.0. 12 | // (See accompanying file LICENSE_1_0.txt or copy at 13 | // http://www.boost.org/LICENSE_1_0.txt) 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "local_connection_delegate.hpp" 23 | 24 | namespace boost { 25 | namespace network { 26 | namespace http { 27 | namespace impl { 28 | 29 | struct local_normal_delegate : local_connection_delegate { 30 | explicit local_normal_delegate(boost::asio::io_service &service); 31 | 32 | void connect(boost::asio::local::stream_protocol::endpoint &endpoint, 33 | std::function handler) override; 34 | void write(boost::asio::streambuf &command_streambuf, 35 | std::function handler) 36 | override; 37 | void read_some(boost::asio::mutable_buffers_1 const &read_buffer, 38 | std::function handler) 39 | override; 40 | void disconnect() override; 41 | ~local_normal_delegate() override = default; 42 | 43 | local_normal_delegate(local_normal_delegate const &) = delete; 44 | local_normal_delegate &operator=(local_normal_delegate) = delete; 45 | 46 | private: 47 | boost::asio::io_service &service_; 48 | std::unique_ptr socket_; 49 | }; 50 | 51 | } // namespace impl 52 | } // namespace http 53 | } // namespace network 54 | } // namespace boost 55 | 56 | #ifdef BOOST_NETWORK_NO_LIB 57 | #include "local_normal_delegate.ipp" 58 | #endif /* BOOST_NETWORK_NO_LIB */ 59 | 60 | #endif // BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_NORMAL_DELEGATE_20110819 61 | -------------------------------------------------------------------------------- /src/http/local_normal_delegate.ipp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_NORMAL_DELEGATE_IPP_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_NORMAL_DELEGATE_IPP_20170307 3 | 4 | // Copyright 2011 Dean Michael Berris (dberris@google.com). 5 | // Copyright 2017 Igor Peshansky (igorp@google.com). 6 | // Copyright 2011-2017 Google, Inc. 7 | // Distributed under the Boost Software License, Version 1.0. 8 | // (See accompanying file LICENSE_1_0.txt or copy at 9 | // http://www.boost.org/LICENSE_1_0.txt) 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "local_normal_delegate.hpp" 18 | 19 | boost::network::http::impl::local_normal_delegate::local_normal_delegate( 20 | boost::asio::io_service &service) 21 | : service_(service) {} 22 | 23 | void boost::network::http::impl::local_normal_delegate::connect( 24 | boost::asio::local::stream_protocol::endpoint &endpoint, 25 | std::function handler) { 26 | 27 | socket_.reset(new boost::asio::local::stream_protocol::socket(service_)); 28 | socket_->async_connect(endpoint, handler); 29 | } 30 | 31 | void boost::network::http::impl::local_normal_delegate::write( 32 | boost::asio::streambuf &command_streambuf, 33 | std::function handler) { 34 | boost::asio::async_write(*socket_, command_streambuf, handler); 35 | } 36 | 37 | void boost::network::http::impl::local_normal_delegate::read_some( 38 | boost::asio::mutable_buffers_1 const &read_buffer, 39 | std::function handler) { 40 | socket_->async_read_some(read_buffer, handler); 41 | } 42 | 43 | void boost::network::http::impl::local_normal_delegate::disconnect() { 44 | if (socket_.get() && socket_->is_open()) { 45 | boost::system::error_code ignored; 46 | socket_->shutdown(boost::asio::local::stream_protocol::socket::shutdown_both, ignored); 47 | if (!ignored) { 48 | socket_->close(ignored); 49 | } 50 | } 51 | } 52 | 53 | #endif // BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_LOCAL_NORMAL_DELEGATE_IPP_20170307 54 | -------------------------------------------------------------------------------- /src/http/local_tags.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_LOCAL_TAGS_HPP_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_LOCAL_TAGS_HPP_20170307 3 | 4 | // Copyright 2017 Igor Peshansky (igorp@google.com). 5 | // Copyright 2017 Google, Inc. 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #include 11 | #include 12 | 13 | namespace boost { 14 | namespace network { 15 | namespace http { 16 | namespace tags { 17 | 18 | struct local { 19 | typedef mpl::true_::type is_local; 20 | }; 21 | 22 | using namespace boost::network::tags; 23 | 24 | template 25 | struct components; 26 | 27 | typedef mpl::vector 28 | http_async_8bit_local_resolve_tags; 29 | BOOST_NETWORK_DEFINE_TAG(http_async_8bit_local_resolve); 30 | 31 | } /* tags */ 32 | 33 | } /* http */ 34 | 35 | } /* network */ 36 | 37 | } /* boost */ 38 | 39 | #endif /* BOOST_NETWORK_PROTOCOL_HTTP_LOCAL_TAGS_HPP_20170307 */ 40 | -------------------------------------------------------------------------------- /src/http/traits/local_resolver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_LOCAL_RESOLVER_20170307 2 | #define BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_LOCAL_RESOLVER_20170307 3 | 4 | // Copyright 2017 Igor Peshansky (igorp@google.com). 5 | // Copyright 2017 Google, Inc. 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #include "../local_tags.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace boost { 18 | namespace network { 19 | namespace http { 20 | 21 | template<> struct resolver { 22 | typedef boost::asio::ip::basic_resolver type; 23 | }; 24 | 25 | } // namespace http 26 | 27 | } // namespace network 28 | 29 | } // namespace boost 30 | 31 | #endif // BOOST_NETWORK_PROTOCOL_HTTP_TRAITS_LOCAL_RESOLVER_20170307 32 | -------------------------------------------------------------------------------- /src/http_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef HTTP_COMMON_H_ 17 | #define HTTP_COMMON_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace google { 24 | 25 | // Allow logging request headers. 26 | inline std::ostream& operator<<( 27 | std::ostream& o, 28 | const boost::network::http::basic_request::headers_container_type& hv) { 29 | o << "["; 30 | for (const auto& h : hv) { 31 | o << " " << h.name << ": " << h.value; 32 | } 33 | o << " ]"; 34 | return o; 35 | } 36 | 37 | // Allow logging response headers. 38 | inline std::ostream& operator<<( 39 | std::ostream& o, 40 | const boost::network::http::basic_response::headers_container_type& hv) { 41 | o << "["; 42 | for (const auto& h : hv) { 43 | o << " " << h.first << ": " << h.second; 44 | } 45 | o << " ]"; 46 | return o; 47 | } 48 | 49 | } 50 | 51 | #endif // HTTP_COMMON_H_ 52 | -------------------------------------------------------------------------------- /src/instance.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "instance.h" 18 | 19 | #include 20 | #include 21 | 22 | #include "configuration.h" 23 | #include "json.h" 24 | #include "logging.h" 25 | #include "store.h" 26 | #include "time.h" 27 | 28 | namespace http = boost::network::http; 29 | 30 | namespace google { 31 | 32 | InstanceReader::InstanceReader(const Configuration& config) 33 | : config_(config), environment_(config) {} 34 | 35 | /*static*/ 36 | MonitoredResource InstanceReader::InstanceResource(const Environment& environment) 37 | throw(std::out_of_range) { 38 | const std::string resource_type = environment.InstanceResourceType(); 39 | const std::string instance_id = environment.InstanceId(); 40 | const std::string zone = environment.InstanceZone(); 41 | if (instance_id.empty() || zone.empty()) { 42 | throw std::out_of_range("No instance information"); 43 | } 44 | return {resource_type, { 45 | {"instance_id", instance_id}, 46 | {"zone", zone}, 47 | }}; 48 | } 49 | 50 | std::vector 51 | InstanceReader::MetadataQuery() const { 52 | if (config_.VerboseLogging()) { 53 | LOG(INFO) << "Instance Query called"; 54 | } 55 | 56 | MonitoredResource instance_resource = InstanceResource(environment_); 57 | 58 | std::vector result; 59 | result.emplace_back( 60 | std::vector{"", environment_.InstanceId()}, 61 | instance_resource, 62 | // TODO: Send actual instance metadata. 63 | MetadataStore::Metadata::IGNORED() 64 | ); 65 | return result; 66 | } 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/instance.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef INSTANCE_H_ 17 | #define INSTANCE_H_ 18 | 19 | #include 20 | 21 | #include "environment.h" 22 | #include "resource.h" 23 | #include "updater.h" 24 | 25 | namespace google { 26 | 27 | // Configuration object. 28 | class Configuration; 29 | 30 | // Storage for the metadata mapping. 31 | class MetadataStore; 32 | 33 | class InstanceReader { 34 | public: 35 | InstanceReader(const Configuration& config); 36 | // A Instance metadata query function. 37 | std::vector MetadataQuery() const; 38 | 39 | // Gets the monitored resource of the instance the agent is running on. 40 | // Throws std::out_of_range if no instance information is available. 41 | static MonitoredResource InstanceResource(const Environment& environment) 42 | throw(std::out_of_range); 43 | 44 | private: 45 | const Configuration& config_; 46 | Environment environment_; 47 | }; 48 | 49 | class InstanceUpdater : public PollingMetadataUpdater { 50 | public: 51 | InstanceUpdater(const Configuration& config, MetadataStore* store) 52 | : reader_(config), PollingMetadataUpdater( 53 | config, store, "InstanceUpdater", 54 | config.InstanceUpdaterIntervalSeconds(), 55 | [=]() { return reader_.MetadataQuery(); }) { } 56 | private: 57 | InstanceReader reader_; 58 | }; 59 | 60 | } 61 | 62 | #endif // INSTANCE_H_ 63 | -------------------------------------------------------------------------------- /src/local_stream_delegate.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Igor Peshansky (igorp@google.com). 2 | // Copyright 2017 Google, Inc. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "http/local_normal_delegate.ipp" 8 | -------------------------------------------------------------------------------- /src/local_stream_http.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Igor Peshansky (igorp@google.com). 2 | // Copyright 2017 Google, Inc. 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at 5 | // http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #include "http/local_async_connection_base.ipp" 8 | -------------------------------------------------------------------------------- /src/local_stream_http.h: -------------------------------------------------------------------------------- 1 | #ifndef LOCAL_STREAM_HTTP 2 | #define LOCAL_STREAM_HTTP 3 | 4 | // Copyright 2017 Igor Peshansky (igorp@google.com). 5 | // Copyright 2017 Google, Inc. 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See accompanying file LICENSE_1_0.txt or copy at 8 | // http://www.boost.org/LICENSE_1_0.txt) 9 | 10 | #include "http/local_async_normal.hpp" 11 | #include "http/local_client.hpp" 12 | 13 | #endif // LOCAL_STREAM_HTTP 14 | -------------------------------------------------------------------------------- /src/logging.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "logging.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "time.h" 25 | 26 | namespace google { 27 | 28 | Logger::Logger(const char* file, int line, Severity severity, LogStream* stream) 29 | : file_(file), line_(line), severity_(severity), stream_(stream) 30 | { 31 | const std::time_t now_c = 32 | std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 33 | std::tm local_time = time::safe_localtime(&now_c); 34 | // GCC 4.x does not implement std::put_time. Sigh. 35 | char time_val[20]; 36 | std::strftime(time_val, sizeof(time_val), "%m%d %H:%M:%S ", &local_time); 37 | std::hash hasher; 38 | (*this) << kSeverities_[severity] 39 | << time_val 40 | << static_cast(hasher(std::this_thread::get_id())) << " " 41 | << file_ << ":" << line_ << "] "; 42 | } 43 | 44 | Logger::~Logger() { 45 | flush(); 46 | } 47 | 48 | Logger& Logger::flush() { 49 | stream_->write(str()); 50 | return *this; 51 | } 52 | 53 | LogStream default_log_stream(std::cerr); 54 | 55 | //constexpr char Logger::kSeverities_[] = "DIWE"; 56 | 57 | constexpr char Logger::kSeverities_[]; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/logging.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef LOGGING_H_ 17 | #define LOGGING_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace google { 24 | 25 | class LogStream { 26 | public: 27 | LogStream(std::ostream& out) 28 | : out_(&out) {} 29 | ~LogStream() {} 30 | void set_stream(std::ostream& out) { 31 | out_->flush(); 32 | out_ = &out; 33 | } 34 | void write(const std::string& line) { 35 | std::lock_guard lock(mutex_); 36 | (*out_) << line << std::endl; 37 | } 38 | 39 | private: 40 | std::mutex mutex_; 41 | std::ostream* out_; 42 | }; 43 | 44 | class Logger : public std::ostringstream { 45 | public: 46 | enum Severity { DEBUG, INFO, WARNING, ERROR }; 47 | Logger(const char* file, int line, Severity severity, LogStream* stream); 48 | // For better experience, only call flush() once per logger. 49 | Logger& flush(); 50 | ~Logger(); 51 | 52 | private: 53 | const char* file_; 54 | int line_; 55 | Severity severity_; 56 | LogStream* stream_; 57 | 58 | static constexpr char kSeverities_[] = "DIWE"; 59 | }; 60 | 61 | // Global default log stream; 62 | extern LogStream default_log_stream; 63 | 64 | } 65 | 66 | #define LOG(SEVERITY) ::google::Logger(__FILE__, __LINE__, \ 67 | ::google::Logger::SEVERITY, \ 68 | &::google::default_log_stream) 69 | 70 | #endif // LOGGING_H_ 71 | -------------------------------------------------------------------------------- /src/metadatad.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "agent.h" 23 | #include "configuration.h" 24 | #include "docker.h" 25 | #include "instance.h" 26 | #include "kubernetes.h" 27 | #include "time.h" 28 | 29 | namespace google { 30 | namespace { 31 | 32 | class CleanupState { 33 | public: 34 | CleanupState( 35 | std::initializer_list updaters, MetadataAgent* server) 36 | : updaters_(updaters), server_(server) { server_wait_mutex_.lock(); } 37 | 38 | void StartShutdown() const { 39 | std::cerr << "Stopping server" << std::endl; 40 | server_->Stop(); 41 | std::cerr << "Stopping updaters" << std::endl; 42 | for (MetadataUpdater* updater : updaters_) { 43 | updater->NotifyStop(); 44 | } 45 | server_wait_mutex_.unlock(); 46 | // Give the notifications some time to propagate. 47 | std::this_thread::sleep_for(time::seconds(0.1)); 48 | } 49 | 50 | void Wait() const { 51 | std::lock_guard await_server_shutdown(server_wait_mutex_); 52 | } 53 | 54 | private: 55 | mutable std::mutex server_wait_mutex_; 56 | std::vector updaters_; 57 | MetadataAgent* server_; 58 | }; 59 | const CleanupState* cleanup_state; 60 | 61 | } // namespace 62 | } // google 63 | 64 | extern "C" [[noreturn]] void handle_sigterm(int signum) { 65 | std::cerr << "Caught SIGTERM; shutting down" << std::endl; 66 | google::cleanup_state->StartShutdown(); 67 | std::cerr << "Exiting" << std::endl; 68 | std::exit(0); // SIGTERM means graceful shutdown, so report success. 69 | } 70 | 71 | int main(int ac, char** av) { 72 | google::Configuration config; 73 | int parse_result = config.ParseArguments(ac, av); 74 | if (parse_result) { 75 | return parse_result < 0 ? 0 : parse_result; 76 | } 77 | 78 | google::MetadataAgent server(config); 79 | 80 | google::InstanceUpdater instance_updater(config, server.mutable_store()); 81 | google::DockerUpdater docker_updater(config, server.mutable_store()); 82 | google::KubernetesUpdater kubernetes_updater(config, server.health_checker(), server.mutable_store()); 83 | 84 | google::cleanup_state = new google::CleanupState( 85 | {&instance_updater, &docker_updater, &kubernetes_updater}, 86 | &server); 87 | std::signal(SIGTERM, handle_sigterm); 88 | 89 | instance_updater.Start(); 90 | docker_updater.Start(); 91 | kubernetes_updater.Start(); 92 | 93 | server.Start(); 94 | 95 | // Wait for the server to shut down. 96 | google::cleanup_state->Wait(); 97 | } 98 | -------------------------------------------------------------------------------- /src/oauth2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef OAUTH2_H_ 17 | #define OAUTH2_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "environment.h" 24 | #include "json.h" 25 | #include "time.h" 26 | 27 | namespace google { 28 | 29 | namespace { 30 | constexpr const char kDefaultTokenEndpoint[] = 31 | "https://www.googleapis.com/oauth2/v3/token"; 32 | } 33 | 34 | class OAuth2 { 35 | public: 36 | OAuth2(const Environment& environment) 37 | : OAuth2(environment, ExpirationImpl::New()) {} 38 | 39 | std::string GetAuthHeaderValue(); 40 | 41 | protected: 42 | OAuth2(const Environment& environment, std::unique_ptr expiration) 43 | : environment_(environment), 44 | token_expiration_(std::move(expiration)), 45 | token_endpoint_(kDefaultTokenEndpoint) {} 46 | 47 | private: 48 | friend class OAuth2Test; 49 | 50 | json::value ComputeTokenFromCredentials() const; 51 | json::value GetMetadataToken() const; 52 | 53 | void SetTokenEndpointForTest(const std::string& endpoint) { 54 | token_endpoint_ = endpoint; 55 | } 56 | 57 | const Environment& environment_; 58 | std::string auth_header_value_; 59 | std::unique_ptr token_expiration_; 60 | std::string token_endpoint_; 61 | }; 62 | 63 | } 64 | 65 | #endif // OAUTH2_H_ 66 | -------------------------------------------------------------------------------- /src/reporter.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "reporter.h" 18 | 19 | #define BOOST_NETWORK_ENABLE_HTTPS 20 | #include 21 | 22 | #include "configuration.h" 23 | #include "format.h" 24 | #include "json.h" 25 | #include "logging.h" 26 | #include "time.h" 27 | 28 | namespace http = boost::network::http; 29 | 30 | namespace google { 31 | 32 | namespace { 33 | constexpr const int kDefaultInitialWaitSeconds = 3; 34 | } 35 | 36 | MetadataReporter::MetadataReporter(const Configuration& config, 37 | MetadataStore* store, double period_s) 38 | : MetadataReporter( 39 | config, store, period_s, kDefaultInitialWaitSeconds, 40 | TimerImpl::New( 41 | config.VerboseLogging(), "metadata reporter")) {} 42 | 43 | MetadataReporter::MetadataReporter( 44 | const Configuration& config, MetadataStore* store, double period_s, 45 | double initial_wait_s, std::unique_ptr timer) 46 | : store_(store), 47 | config_(config), 48 | environment_(config_), 49 | auth_(environment_), 50 | period_(period_s), 51 | initial_wait_(initial_wait_s), 52 | timer_(std::move(timer)), 53 | reporter_thread_([=]() { ReportMetadata(); }) {} 54 | 55 | MetadataReporter::~MetadataReporter() { 56 | NotifyStopReporter(); 57 | if (reporter_thread_.joinable()) { 58 | reporter_thread_.join(); 59 | } 60 | } 61 | 62 | void MetadataReporter::NotifyStopReporter() { 63 | timer_->Cancel(); 64 | } 65 | 66 | void MetadataReporter::ReportMetadata() { 67 | LOG(INFO) << "Metadata reporter started"; 68 | timer_->Init(); 69 | LOG(INFO) << "Initialized timer"; 70 | // Wait for the first collection to complete. 71 | // TODO: Come up with a more robust synchronization mechanism. 72 | timer_->Wait(time::seconds(initial_wait_)); 73 | LOG(INFO) << "Finished waiting " << initial_wait_.count() << " seconds"; 74 | do { 75 | if (config_.VerboseLogging()) { 76 | LOG(INFO) << "Sending metadata request to server"; 77 | } 78 | try { 79 | SendMetadata(store_->GetMetadataMap()); 80 | if (config_.VerboseLogging()) { 81 | LOG(INFO) << "Metadata request sent successfully"; 82 | } 83 | if (config_.MetadataReporterPurgeDeleted()) { 84 | store_->PurgeDeletedEntries(); 85 | } 86 | } catch (const boost::system::system_error& e) { 87 | LOG(ERROR) << "Metadata request unsuccessful: " << e.what(); 88 | } 89 | } while (timer_->Wait(period_)); 90 | if (config_.VerboseLogging()) { 91 | LOG(INFO) << "Metadata reporter exiting"; 92 | } 93 | } 94 | 95 | namespace { 96 | 97 | void SendMetadataRequest(std::vector&& entries, 98 | const std::string& endpoint, 99 | const std::string& auth_header, 100 | const std::string& user_agent, 101 | bool verbose_logging) 102 | throw (boost::system::system_error) { 103 | json::value update_metadata_request = json::object({ 104 | {"entries", json::array(std::move(entries))}, 105 | }); 106 | 107 | if (verbose_logging) { 108 | LOG(INFO) << "About to send request: POST " << endpoint 109 | << " User-Agent: " << user_agent 110 | << " " << *update_metadata_request; 111 | } 112 | 113 | http::client client; 114 | http::client::request request(endpoint); 115 | std::string request_body = update_metadata_request->ToString(); 116 | request << boost::network::header("User-Agent", user_agent); 117 | request << boost::network::header("Content-Length", 118 | std::to_string(request_body.size())); 119 | request << boost::network::header("Content-Type", "application/json"); 120 | request << boost::network::header("Authorization", auth_header); 121 | request << boost::network::body(request_body); 122 | http::client::response response = client.post(request); 123 | if (status(response) >= 300) { 124 | throw boost::system::system_error( 125 | boost::system::errc::make_error_code(boost::system::errc::not_connected), 126 | format::Substitute("Server responded with '{{message}}' ({{code}})", 127 | {{"message", status_message(response)}, 128 | {"code", format::str(status(response))}})); 129 | } 130 | if (verbose_logging) { 131 | LOG(INFO) << "Server responded with " << body(response); 132 | } 133 | // TODO: process response. 134 | } 135 | 136 | } 137 | 138 | void MetadataReporter::SendMetadata( 139 | std::map&& metadata) 140 | throw (boost::system::system_error) { 141 | if (metadata.empty()) { 142 | if (config_.VerboseLogging()) { 143 | LOG(INFO) << "No data to send"; 144 | } 145 | return; 146 | } 147 | 148 | if (config_.VerboseLogging()) { 149 | LOG(INFO) << "Sending request to the server"; 150 | } 151 | const std::string project_id = environment_.ProjectId(); 152 | // The endpoint template is expected to be of the form 153 | // "https://stackdriver.googleapis.com/.../projects/{{project_id}}/...". 154 | const std::string endpoint = 155 | format::Substitute(config_.MetadataIngestionEndpointFormat(), 156 | {{"project_id", project_id}}); 157 | const std::string auth_header = auth_.GetAuthHeaderValue(); 158 | const std::string user_agent = config_.MetadataReporterUserAgent(); 159 | 160 | const json::value empty_request = json::object({ 161 | {"entries", json::array({})}, 162 | }); 163 | const int empty_size = empty_request->ToString().size(); 164 | 165 | const int limit_bytes = config_.MetadataIngestionRequestSizeLimitBytes(); 166 | const int limit_count = config_.MetadataIngestionRequestSizeLimitCount(); 167 | int total_size = empty_size; 168 | 169 | std::vector entries; 170 | for (auto& entry : metadata) { 171 | const MonitoredResource& resource = entry.first; 172 | MetadataStore::Metadata& metadata = entry.second; 173 | json::value metadata_entry = 174 | json::object({ // MonitoredResourceMetadata 175 | {"resource", resource.ToJSON()}, 176 | {"rawContentVersion", json::string(metadata.version)}, 177 | {"rawContent", std::move(metadata.metadata)}, 178 | {"state", json::string(metadata.is_deleted ? "DELETED" : "ACTIVE")}, 179 | {"createTime", json::string(time::rfc3339::ToString(metadata.created_at))}, 180 | {"collectTime", json::string(time::rfc3339::ToString(metadata.collected_at))}, 181 | }); 182 | // TODO: This is probably all kinds of inefficient... 183 | const int size = metadata_entry->ToString().size(); 184 | if (empty_size + size > limit_bytes) { 185 | LOG(ERROR) << "Individual entry too large: " << size 186 | << "B is greater than the limit " << limit_bytes 187 | << "B, dropping; entry " << *metadata_entry; 188 | continue; 189 | } 190 | if (entries.size() == limit_count || total_size + size > limit_bytes) { 191 | SendMetadataRequest(std::move(entries), endpoint, auth_header, user_agent, 192 | config_.VerboseLogging()); 193 | entries.clear(); 194 | total_size = empty_size; 195 | } 196 | entries.emplace_back(std::move(metadata_entry)); 197 | total_size += size; 198 | } 199 | if (!entries.empty()) { 200 | SendMetadataRequest(std::move(entries), endpoint, auth_header, user_agent, 201 | config_.VerboseLogging()); 202 | } 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /src/reporter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef REPORTER_H_ 17 | #define REPORTER_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "environment.h" 25 | #include "json.h" 26 | #include "oauth2.h" 27 | #include "resource.h" 28 | #include "store.h" 29 | #include "time.h" 30 | 31 | namespace google { 32 | 33 | // Configuration object. 34 | class Configuration; 35 | 36 | // A periodic reporter of metadata to Stackdriver. 37 | class MetadataReporter { 38 | public: 39 | MetadataReporter(const Configuration& config, MetadataStore* store, 40 | double period_s); 41 | ~MetadataReporter(); 42 | 43 | // Stop reporting metadata to Stackdriver. 44 | void NotifyStopReporter(); 45 | 46 | protected: 47 | MetadataReporter( 48 | const Configuration& config, MetadataStore* store, double period_s, 49 | double initial_wait_s, std::unique_ptr timer); 50 | 51 | private: 52 | // Reporter loop. 53 | void ReportMetadata(); 54 | 55 | // Send the given set of metadata. 56 | void SendMetadata( 57 | std::map&& metadata) 58 | throw (boost::system::system_error); 59 | 60 | const Configuration& config_; 61 | MetadataStore* store_; 62 | Environment environment_; 63 | OAuth2 auth_; 64 | // The reporting period in seconds. 65 | time::seconds period_; 66 | time::seconds initial_wait_; 67 | std::unique_ptr timer_; 68 | std::thread reporter_thread_; 69 | }; 70 | 71 | } 72 | 73 | #endif // REPORTER_H_ 74 | -------------------------------------------------------------------------------- /src/resource.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "resource.h" 18 | 19 | #include 20 | 21 | namespace google { 22 | 23 | std::ostream& operator<<(std::ostream& o, const MonitoredResource& r) { 24 | o << "{ type: '" << r.type() << "' labels: {"; 25 | for (const auto& label : r.labels()) { 26 | o << " " << label.first << ": '" << label.second << "'"; 27 | } 28 | o << " } }"; 29 | return o; 30 | } 31 | 32 | json::value MonitoredResource::ToJSON() const { 33 | std::map labels; 34 | for (const auto& kv : labels_) { 35 | labels.emplace(kv.first, json::string(kv.second)); 36 | } 37 | return json::object({ 38 | {"type", json::string(type_)}, 39 | {"labels", json::object(std::move(labels))}, 40 | }); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/resource.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef RESOURCE_H_ 17 | #define RESOURCE_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "json.h" 24 | 25 | namespace google { 26 | 27 | class MonitoredResource { 28 | public: 29 | MonitoredResource(const std::string& type, 30 | const std::map labels) 31 | : type_(type), labels_(labels) {} 32 | const std::string& type() const { return type_; } 33 | const std::map& labels() const { return labels_; } 34 | bool operator==(const MonitoredResource& other) const { 35 | return other.type_ == type_ && other.labels_ == labels_; 36 | } 37 | bool operator!=(const MonitoredResource& other) const { 38 | return !(other == *this); 39 | } 40 | bool operator<(const MonitoredResource& other) const { 41 | return type_ < other.type_ 42 | || (type_ == other.type_ && labels_ < other.labels_); 43 | } 44 | 45 | json::value ToJSON() const; 46 | static MonitoredResource FromJSON(const json::Object* json); 47 | 48 | private: 49 | std::string type_; 50 | std::map labels_; 51 | }; 52 | 53 | std::ostream& operator<<(std::ostream&, const MonitoredResource&); 54 | 55 | } 56 | 57 | #endif // RESOURCE_H_ 58 | -------------------------------------------------------------------------------- /src/sample_agent_config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | MetadataIngestionEndpointFormat: "https://staging-stackdriver.sandbox.googleapis.com/v1beta2/projects/{{project_id}}/resourceMetadata:batchUpdate" 16 | #CredentialsFile: /tmp/token.json 17 | #ProjectId: "1234567890" 18 | #InstanceZone: "us-central1-a" 19 | #DockerContainerFilter: "limit=1" 20 | -------------------------------------------------------------------------------- /src/store.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "store.h" 18 | 19 | #include "configuration.h" 20 | #include "logging.h" 21 | #include "time.h" 22 | 23 | namespace google { 24 | 25 | MetadataStore::Metadata MetadataStore::Metadata::IGNORED() { 26 | return MetadataStore::Metadata(); 27 | } 28 | 29 | MetadataStore::MetadataStore(const Configuration& config) : config_(config) {} 30 | 31 | const MonitoredResource& MetadataStore::LookupResource( 32 | const std::string& resource_id) const throw(std::out_of_range) { 33 | std::lock_guard lock(resource_mu_); 34 | return resource_map_.at(resource_id); 35 | } 36 | 37 | void MetadataStore::UpdateResource(const std::vector& resource_ids, 38 | const MonitoredResource& resource) { 39 | std::lock_guard lock(resource_mu_); 40 | // TODO: How do we handle deleted resources? 41 | // TODO: Do we care if the value was already there? 42 | for (const std::string& id : resource_ids) { 43 | if (config_.VerboseLogging()) { 44 | LOG(INFO) << "Updating resource map '" << id << "'->" << resource; 45 | } 46 | resource_map_.emplace(id, resource); 47 | } 48 | } 49 | 50 | void MetadataStore::UpdateMetadata(const MonitoredResource& resource, 51 | Metadata&& entry) { 52 | if (entry.ignore) { 53 | return; 54 | } 55 | std::lock_guard lock(metadata_mu_); 56 | if (config_.VerboseLogging()) { 57 | LOG(INFO) << "Updating metadata map " << resource << "->{" 58 | << "version: " << entry.version << ", " 59 | << "is_deleted: " << entry.is_deleted << ", " 60 | << "created_at: " << time::rfc3339::ToString(entry.created_at) << ", " 61 | << "collected_at: " << time::rfc3339::ToString(entry.collected_at) 62 | << ", " 63 | << "metadata: " << *entry.metadata << ", " 64 | << "ignore: " << entry.ignore 65 | << "}"; 66 | } 67 | // Force value update. The repeated search is inefficient, but shouldn't 68 | // be a huge deal. 69 | metadata_map_.erase(resource); 70 | metadata_map_.emplace(resource, std::move(entry)); 71 | 72 | auto found = last_collection_times_.emplace(resource.type(), 73 | entry.collected_at); 74 | // Force timestamp update for existing entries. 75 | if (!found.second && found.first->second < entry.collected_at) { 76 | found.first->second = entry.collected_at; 77 | } 78 | } 79 | 80 | std::map 81 | MetadataStore::GetMetadataMap() const { 82 | std::lock_guard lock(metadata_mu_); 83 | 84 | std::map result; 85 | for (const auto& kv : metadata_map_) { 86 | const MonitoredResource& resource = kv.first; 87 | const Metadata& metadata = kv.second; 88 | result.emplace(resource, metadata.Clone()); 89 | } 90 | return result; 91 | } 92 | 93 | std::map MetadataStore::GetLastCollectionTimes() const { 94 | std::lock_guard lock(metadata_mu_); 95 | return last_collection_times_; 96 | } 97 | 98 | void MetadataStore::PurgeDeletedEntries() { 99 | std::lock_guard lock(metadata_mu_); 100 | 101 | for (auto it = metadata_map_.begin(); it != metadata_map_.end(); ) { 102 | const MonitoredResource& resource = it->first; 103 | const Metadata& entry = it->second; 104 | if (entry.is_deleted) { 105 | if (config_.VerboseLogging()) { 106 | LOG(INFO) << "Purging metadata entry " << resource << "->{" 107 | << "version: " << entry.version << ", " 108 | << "is_deleted: " << entry.is_deleted << ", " 109 | << "created_at: " << time::rfc3339::ToString(entry.created_at) 110 | << ", " 111 | << "collected_at: " << time::rfc3339::ToString(entry.collected_at) 112 | << ", " 113 | << "metadata: " << *entry.metadata << ", " 114 | << "ignore: " << entry.ignore 115 | << "}"; 116 | } 117 | it = metadata_map_.erase(it); 118 | } else { 119 | ++it; 120 | } 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/store.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef STORE_H_ 17 | #define STORE_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "json.h" 25 | #include "resource.h" 26 | #include "time.h" 27 | 28 | namespace google { 29 | 30 | // Configuration object. 31 | class Configuration; 32 | 33 | // A timestamp type. 34 | using Timestamp = time_point; 35 | 36 | // Stores the metadata mapping. 37 | class MetadataStore { 38 | public: 39 | struct Metadata { 40 | Metadata(const std::string& version_, 41 | bool is_deleted_, 42 | const Timestamp& created_at_, 43 | const Timestamp& collected_at_, 44 | json::value metadata_) 45 | : version(version_), is_deleted(is_deleted_), created_at(created_at_), 46 | collected_at(collected_at_), metadata(std::move(metadata_)), 47 | ignore(false) {} 48 | Metadata(Metadata&& other) 49 | : version(other.version), is_deleted(other.is_deleted), 50 | created_at(other.created_at), collected_at(other.collected_at), 51 | metadata(std::move(other.metadata)), ignore(other.ignore) {} 52 | 53 | Metadata Clone() const { 54 | if (ignore) { 55 | return IGNORED(); 56 | } 57 | return {version, is_deleted, created_at, collected_at, metadata->Clone()}; 58 | } 59 | 60 | static Metadata IGNORED(); 61 | 62 | const std::string version; 63 | const bool is_deleted; 64 | const Timestamp created_at; 65 | const Timestamp collected_at; 66 | json::value metadata; 67 | const bool ignore; 68 | 69 | private: 70 | Metadata() 71 | : version(), is_deleted(false), created_at(), collected_at(), 72 | metadata(json::object({})), ignore(true) {} 73 | }; 74 | 75 | MetadataStore(const Configuration& config); 76 | 77 | // Returns a copy of the mapping from a monitored resource to the metadata 78 | // associated with that resource. 79 | std::map GetMetadataMap() const; 80 | 81 | // Returns a copy of the mapping from a monitored resource type to 82 | // its last collection time. 83 | std::map GetLastCollectionTimes() const; 84 | 85 | // Looks up the local resource map entry for a given resource id. 86 | // Throws an exception if the resource is not found. 87 | const MonitoredResource& LookupResource(const std::string& resource_id) const 88 | throw(std::out_of_range); 89 | 90 | // Updates the local resource map entry for a given resource. 91 | // Each local id in `resource_ids` is effectively an alias for `resource`. 92 | // Adds a resource mapping from each of the `resource_ids` to the `resource`. 93 | void UpdateResource(const std::vector& resource_ids, 94 | const MonitoredResource& resource); 95 | 96 | // Updates metadata for a given resource. 97 | // Adds a metadata mapping from the `resource` to the metadata `entry`. 98 | void UpdateMetadata(const MonitoredResource& resource, 99 | Metadata&& entry); 100 | 101 | private: 102 | friend class MetadataReporter; 103 | friend class MetadataStoreTest; 104 | 105 | void PurgeDeletedEntries(); 106 | 107 | const Configuration& config_; 108 | 109 | // A lock that guards access to the local resource map. 110 | mutable std::mutex resource_mu_; 111 | // A map from a locally unique id to MonitoredResource. 112 | std::map resource_map_; 113 | // A lock that guards access to the metadata and last collection times maps. 114 | mutable std::mutex metadata_mu_; 115 | // A map from MonitoredResource to (JSON) resource metadata. 116 | std::map metadata_map_; 117 | // A map from monitored resource type to last collection time. 118 | std::map last_collection_times_; 119 | }; 120 | 121 | } 122 | 123 | #endif // STORE_H_ 124 | -------------------------------------------------------------------------------- /src/time.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "time.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace google { 28 | 29 | namespace time { 30 | 31 | namespace rfc3339 { 32 | 33 | namespace { 34 | 35 | // See http://stackoverflow.com/questions/4137748. 36 | int UTCOffset() { 37 | const std::time_t local = std::time(nullptr); 38 | std::tm utc_time = safe_gmtime(&local); 39 | // Since we're only using this for conversion to UTC, always turn off DST. 40 | utc_time.tm_isdst = 0; 41 | const std::time_t utc = std::mktime(&utc_time); 42 | return local - utc; 43 | } 44 | 45 | const int kUtcOffset = UTCOffset(); 46 | 47 | } 48 | 49 | std::string ToString(const time_point& t) { 50 | std::stringstream out; 51 | const std::chrono::system_clock::time_point t_sec = 52 | std::chrono::time_point_cast(t); 53 | const std::time_t time = std::chrono::system_clock::to_time_t(t_sec); 54 | std::tm utc_time = safe_gmtime(&time); 55 | // GCC 4.x does not implement std::put_time. Sigh. 56 | char dt[64]; 57 | std::strftime(dt, sizeof(dt), "%Y-%m-%dT%H:%M:%S", &utc_time); 58 | time_point t_ns = std::chrono::time_point_cast(t); 59 | std::chrono::time_point 60 | t_s = std::chrono::time_point_cast(t_ns); 61 | const std::chrono::nanoseconds ns = t_ns - t_s; 62 | out << dt << "." << std::setw(9) << std::setfill('0') << ns.count() << "Z"; 63 | return out.str(); 64 | } 65 | 66 | time_point FromString(const std::string& s) { 67 | std::tm tm; 68 | char* const end = strptime(s.c_str(), "%Y-%m-%dT%H:%M:%S", &tm); 69 | if (end == nullptr || !std::isdigit(*(end - 2))) { 70 | // TODO: Invalid time format. 71 | return time_point(); 72 | } 73 | char* zone; 74 | if (*end == '.') { 75 | (void)std::strtol(end + 1, &zone, 10); 76 | if (zone <= end + 1) { 77 | // TODO: Missing nanoseconds. 78 | return time_point(); 79 | } 80 | } else { 81 | zone = end; 82 | } 83 | if (*zone != 'Z' || *(zone+1) != '\0') { 84 | // TODO: Invalid timezone. 85 | return time_point(); 86 | } 87 | char* d_end; 88 | double seconds = std::strtod(end - 2, &d_end); 89 | if (d_end != zone) { 90 | // TODO: Internal error. 91 | return time_point(); 92 | } 93 | static_assert(sizeof(long) == 8, "long is too small"); 94 | // Truncate to 9 digits by rounding to 10 and discarding the last one. 95 | long ns = std::lround((seconds - tm.tm_sec) * 10000000000) / 10; 96 | // Our UTC offset constant assumes no DST. 97 | tm.tm_isdst = 0; 98 | const std::time_t local_time = std::mktime(&tm); 99 | const std::time_t utc_time = local_time + kUtcOffset; 100 | time_point sec = std::chrono::system_clock::from_time_t(utc_time); 101 | return sec + std::chrono::nanoseconds(ns); 102 | } 103 | 104 | } 105 | 106 | namespace { 107 | 108 | std::mutex localtime_mutex; 109 | std::mutex gmtime_mutex; 110 | 111 | } 112 | 113 | std::tm safe_localtime(const std::time_t* t) { 114 | std::lock_guard l(localtime_mutex); 115 | return *std::localtime(t); 116 | } 117 | 118 | std::tm safe_gmtime(const std::time_t* t) { 119 | std::lock_guard l(gmtime_mutex); 120 | return *std::gmtime(t); 121 | } 122 | 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/time.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef TIME_H_ 17 | #define TIME_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "logging.h" 27 | 28 | namespace google { 29 | 30 | using time_point = std::chrono::time_point; 32 | 33 | namespace time { 34 | 35 | using seconds = std::chrono::duration; 36 | 37 | inline double SecondsSinceEpoch(const time_point& t) { 38 | return std::chrono::duration_cast( 39 | t.time_since_epoch()).count(); 40 | } 41 | 42 | namespace rfc3339 { 43 | 44 | // Time conversions. 45 | std::string ToString(const time_point& t); 46 | time_point FromString(const std::string& s); 47 | 48 | } 49 | 50 | // Thread-safe versions of std:: functions. 51 | std::tm safe_localtime(const std::time_t* t); 52 | std::tm safe_gmtime(const std::time_t* t); 53 | 54 | } 55 | 56 | // Abstract class for a timer. 57 | class Timer { 58 | public: 59 | virtual ~Timer() = default; 60 | 61 | // Initializes the timer. 62 | virtual void Init() = 0; 63 | 64 | // Waits for one duration to pass. Returns false if the timer was 65 | // canceled while waiting. 66 | virtual bool Wait(time::seconds duration) = 0; 67 | 68 | // Cancels the timer. 69 | virtual void Cancel() = 0; 70 | }; 71 | 72 | // Implementation of a timer parameterized over a clock type. 73 | template 74 | class TimerImpl : public Timer { 75 | public: 76 | static std::unique_ptr New(bool verbose, const std::string& name) { 77 | return std::unique_ptr(new TimerImpl(verbose, name)); 78 | } 79 | 80 | TimerImpl(bool verbose, const std::string& name) 81 | : verbose_(verbose), name_(name) {} 82 | 83 | ~TimerImpl() { 84 | Cancel(); 85 | } 86 | 87 | void Init() override { 88 | timer_.lock(); 89 | if (verbose_) { 90 | LOG(INFO) << "Locked timer for " << name_; 91 | } 92 | } 93 | 94 | bool Wait(time::seconds duration) override { 95 | // An unlocked timer means the wait is cancelled. 96 | auto start = Clock::now(); 97 | auto wakeup = start + duration; 98 | if (verbose_) { 99 | LOG(INFO) << "Trying to unlock the timer for " << name_; 100 | } 101 | while (!timer_.try_lock_until(wakeup)) { 102 | auto now = Clock::now(); 103 | // Detect spurious wakeups. 104 | if (now < wakeup) { 105 | continue; 106 | } 107 | if (verbose_) { 108 | LOG(INFO) << " Timer unlock timed out after " 109 | << std::chrono::duration_cast(now-start).count() 110 | << "s (good) for " << name_; 111 | } 112 | return true; 113 | } 114 | return false; 115 | } 116 | 117 | void Cancel() override { 118 | timer_.unlock(); 119 | if (verbose_) { 120 | LOG(INFO) << "Unlocked timer for " << name_; 121 | } 122 | } 123 | 124 | private: 125 | std::timed_mutex timer_; 126 | bool verbose_; 127 | std::string name_; 128 | }; 129 | 130 | // Abstract class for tracking an expiration time. 131 | class Expiration { 132 | public: 133 | virtual ~Expiration() = default; 134 | 135 | // Returns true if the expiration time has passed. 136 | virtual bool IsExpired() = 0; 137 | 138 | // Resets the expiration time to the given number of seconds from 139 | // now. 140 | virtual void Reset(std::chrono::seconds duration) = 0; 141 | }; 142 | 143 | // Implementation of an expiration parameterized over a clock type. 144 | template 145 | class ExpirationImpl : public Expiration { 146 | public: 147 | static std::unique_ptr New() { 148 | return std::unique_ptr(new ExpirationImpl()); 149 | } 150 | 151 | bool IsExpired() override { 152 | return token_expiration_ < Clock::now(); 153 | } 154 | 155 | void Reset(std::chrono::seconds duration) override { 156 | token_expiration_ = Clock::now() + duration; 157 | } 158 | 159 | private: 160 | typename Clock::time_point token_expiration_; 161 | }; 162 | 163 | // Abstract class for performing an asynchronous action after a delay. 164 | class DelayTimer { 165 | public: 166 | virtual ~DelayTimer() = default; 167 | virtual void RunAsyncAfter( 168 | time::seconds duration, 169 | std::function handler) = 0; 170 | }; 171 | 172 | // Implementation of DelayTimer parameterized over a clock type. 173 | template 174 | class DelayTimerImpl : public DelayTimer { 175 | public: 176 | DelayTimerImpl(boost::asio::io_service& service) : timer_(service) {} 177 | void RunAsyncAfter( 178 | time::seconds duration, 179 | std::function handler) override { 180 | timer_.expires_from_now( 181 | std::chrono::duration_cast(duration)); 182 | timer_.async_wait(handler); 183 | } 184 | private: 185 | boost::asio::basic_waitable_timer timer_; 186 | }; 187 | 188 | // Abstract class for a factory of DelayTimer types. 189 | class DelayTimerFactory { 190 | public: 191 | virtual ~DelayTimerFactory() = default; 192 | virtual std::unique_ptr CreateTimer( 193 | boost::asio::io_service& service) = 0; 194 | }; 195 | 196 | // Implementation of a DelayTimer parameterized over a clock type. 197 | template 198 | class DelayTimerFactoryImpl : public DelayTimerFactory { 199 | public: 200 | static std::unique_ptr New() { 201 | return std::unique_ptr( 202 | new DelayTimerFactoryImpl()); 203 | } 204 | std::unique_ptr CreateTimer( 205 | boost::asio::io_service& service) override { 206 | return std::unique_ptr(new DelayTimerImpl(service)); 207 | } 208 | }; 209 | 210 | } 211 | 212 | #endif // TIME_H_ 213 | -------------------------------------------------------------------------------- /src/updater.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "updater.h" 18 | 19 | #include 20 | 21 | #include "configuration.h" 22 | #include "format.h" 23 | #include "logging.h" 24 | 25 | namespace google { 26 | 27 | MetadataUpdater::MetadataUpdater(const Configuration& config, 28 | MetadataStore* store, const std::string& name) 29 | : config_(config), store_(store), name_(name) {} 30 | 31 | MetadataUpdater::~MetadataUpdater() {} 32 | 33 | void MetadataUpdater::Start() throw(ConfigurationValidationError) { 34 | ValidateStaticConfiguration(); 35 | 36 | if (ShouldStartUpdater()) { 37 | ValidateDynamicConfiguration(); 38 | StartUpdater(); 39 | } else { 40 | LOG(INFO) << "Not starting " << name_; 41 | } 42 | } 43 | 44 | void MetadataUpdater::NotifyStop() { 45 | NotifyStopUpdater(); 46 | } 47 | 48 | PollingMetadataUpdater::PollingMetadataUpdater( 49 | const Configuration& config, MetadataStore* store, 50 | const std::string& name, double period_s, 51 | std::function()> query_metadata) 52 | : PollingMetadataUpdater( 53 | config, store, name, period_s, query_metadata, 54 | TimerImpl::New( 55 | config.VerboseLogging(), name)) {} 56 | 57 | PollingMetadataUpdater::PollingMetadataUpdater( 58 | const Configuration& config, MetadataStore* store, 59 | const std::string& name, double period_s, 60 | std::function()> query_metadata, 61 | std::unique_ptr timer) 62 | : MetadataUpdater(config, store, name), 63 | period_(period_s), 64 | query_metadata_(query_metadata), 65 | timer_(std::move(timer)), 66 | reporter_thread_() {} 67 | 68 | PollingMetadataUpdater::~PollingMetadataUpdater() { 69 | if (reporter_thread_.joinable()) { 70 | reporter_thread_.join(); 71 | } 72 | } 73 | 74 | void PollingMetadataUpdater::ValidateStaticConfiguration() const 75 | throw(ConfigurationValidationError) { 76 | if (period_ < time::seconds::zero()) { 77 | throw ConfigurationValidationError( 78 | format::Substitute("Polling period {{period}}s cannot be negative", 79 | {{"period", format::str(period_.count())}})); 80 | } 81 | } 82 | 83 | bool PollingMetadataUpdater::ShouldStartUpdater() const { 84 | return period_ > time::seconds::zero(); 85 | } 86 | 87 | void PollingMetadataUpdater::StartUpdater() { 88 | timer_->Init(); 89 | reporter_thread_ = std::thread([=]() { PollForMetadata(); }); 90 | } 91 | 92 | void PollingMetadataUpdater::NotifyStopUpdater() { 93 | timer_->Cancel(); 94 | } 95 | 96 | void PollingMetadataUpdater::PollForMetadata() { 97 | do { 98 | std::vector result_vector = query_metadata_(); 99 | for (ResourceMetadata& result : result_vector) { 100 | UpdateResourceCallback(result); 101 | UpdateMetadataCallback(std::move(result)); 102 | } 103 | } while (timer_->Wait(period_)); 104 | if (config().VerboseLogging()) { 105 | LOG(INFO) << "Timer unlocked (stop polling) for " << name(); 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/updater.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef UPDATER_H_ 17 | #define UPDATER_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "resource.h" 26 | #include "store.h" 27 | #include "time.h" 28 | 29 | namespace google { 30 | 31 | // Configuration object. 32 | class Configuration; 33 | 34 | // An abstract class for asynchronous updates of the metadata mapping. 35 | class MetadataUpdater { 36 | public: 37 | struct ResourceMetadata { 38 | ResourceMetadata(const std::vector& ids, 39 | const MonitoredResource& resource, 40 | MetadataStore::Metadata&& metadata) 41 | : ids_(ids), resource_(resource), metadata_(std::move(metadata)) {} 42 | ResourceMetadata(ResourceMetadata&& other) 43 | : ResourceMetadata(other.ids_, other.resource_, 44 | std::move(other.metadata_)) {} 45 | 46 | const MetadataStore::Metadata& metadata() const { return metadata_; } 47 | const MonitoredResource& resource() const { return resource_; } 48 | const std::vector& ids() const { return ids_; } 49 | 50 | private: 51 | friend class MetadataUpdater; // Needs write access to metadata_. 52 | 53 | std::vector ids_; 54 | MonitoredResource resource_; 55 | MetadataStore::Metadata metadata_; 56 | }; 57 | 58 | // A representation of all validation errors. 59 | class ConfigurationValidationError { 60 | public: 61 | ConfigurationValidationError(const std::string& what) 62 | : explanation_(what) {} 63 | const std::string& what() const { return explanation_; } 64 | private: 65 | std::string explanation_; 66 | }; 67 | 68 | MetadataUpdater(const Configuration& config, MetadataStore* store, 69 | const std::string& name); 70 | virtual ~MetadataUpdater(); 71 | 72 | // Starts updating. 73 | void Start() throw(ConfigurationValidationError); 74 | 75 | // Notifies the updater to stop updating. 76 | void NotifyStop(); 77 | 78 | using UpdateCallback = 79 | std::function&&)>; 80 | 81 | protected: 82 | friend class UpdaterTest; 83 | 84 | // Validates static properties of the relevant configuration. 85 | // If the configuration is invalid, throws a ConfigurationValidationError, 86 | // which is generally expected to pass through and terminate the program. 87 | virtual void ValidateStaticConfiguration() const 88 | throw(ConfigurationValidationError) {} 89 | 90 | // Decides whether the updater logic should be started based on the current 91 | // configuration. 92 | virtual bool ShouldStartUpdater() const { 93 | return true; 94 | } 95 | 96 | // Validates dynamic properties of the relevant configuration. 97 | // If the configuration is invalid, throws a ConfigurationValidationError, 98 | // which is generally expected to pass through and terminate the program. 99 | // This should only run when we know the updater is about to be started. 100 | virtual void ValidateDynamicConfiguration() const 101 | throw(ConfigurationValidationError) {} 102 | 103 | // Internal method for starting the updater's logic. 104 | virtual void StartUpdater() = 0; 105 | 106 | // Internal method for notifying the updater's to stop its logic. 107 | // This method should not perform any blocking operations (e.g., wait). 108 | virtual void NotifyStopUpdater() = 0; 109 | 110 | // Updates the resource map in the store. 111 | void UpdateResourceCallback(const ResourceMetadata& result) { 112 | store_->UpdateResource(result.ids_, result.resource_); 113 | } 114 | 115 | // Updates the metadata in the store. Consumes result. 116 | void UpdateMetadataCallback(ResourceMetadata&& result) { 117 | store_->UpdateMetadata(result.resource_, std::move(result.metadata_)); 118 | } 119 | 120 | const std::string& name() const { 121 | return name_; 122 | } 123 | 124 | const Configuration& config() const { 125 | return config_; 126 | } 127 | 128 | private: 129 | // The name of the updater provided by subclasses. 130 | std::string name_; 131 | 132 | const Configuration& config_; 133 | 134 | // The store for the metadata. 135 | MetadataStore* store_; 136 | }; 137 | 138 | // A class for all periodic updates of the metadata mapping. 139 | class PollingMetadataUpdater : public MetadataUpdater { 140 | public: 141 | PollingMetadataUpdater( 142 | const Configuration& config, MetadataStore* store, 143 | const std::string& name, double period_s, 144 | std::function()> query_metadata); 145 | ~PollingMetadataUpdater(); 146 | 147 | protected: 148 | friend class UpdaterTest; 149 | 150 | PollingMetadataUpdater( 151 | const Configuration& config, MetadataStore* store, 152 | const std::string& name, double period_s, 153 | std::function()> query_metadata, 154 | std::unique_ptr timer); 155 | 156 | void ValidateStaticConfiguration() const throw(ConfigurationValidationError); 157 | using MetadataUpdater::ValidateDynamicConfiguration; 158 | bool ShouldStartUpdater() const; 159 | void StartUpdater(); 160 | void NotifyStopUpdater(); 161 | 162 | private: 163 | friend class InstanceTest; 164 | 165 | // Metadata poller. 166 | void PollForMetadata(); 167 | 168 | // The polling period in seconds. 169 | time::seconds period_; 170 | 171 | // The function to actually query for metadata. 172 | std::function()> query_metadata_; 173 | 174 | // The timer. 175 | std::unique_ptr timer_; 176 | 177 | // The thread that polls for new metadata. 178 | std::thread reporter_thread_; 179 | }; 180 | 181 | } 182 | 183 | #endif // UPDATER_H_ 184 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef UTIL_H_ 17 | #define UTIL_H_ 18 | 19 | #include 20 | #include 21 | 22 | #include "time.h" 23 | 24 | namespace google { 25 | 26 | namespace util { 27 | 28 | // Executes the work function while it throws exception Fail, up to a given 29 | // number of times, with a delay between executions. If it throws exception 30 | // Terminate, which may be a subclass of Fail, pass it through (no retries). 31 | // Any other exception outside of the Fail hierarchy will be passed through. 32 | template 33 | void Retry(int max_retries, time::seconds delay, 34 | std::function work); 35 | 36 | template 37 | void Retry(int max_retries, time::seconds delay, 38 | std::function work) { 39 | for (int i = 0; true; ++i) { 40 | try { 41 | work(); 42 | return; 43 | } catch (const Terminate& e) { // Catch first due to inheritance. 44 | throw e; 45 | } catch (const Fail& e) { 46 | if (i < max_retries - 1) { 47 | std::this_thread::sleep_for(delay); 48 | } else { 49 | throw e; 50 | } 51 | } 52 | } 53 | } 54 | 55 | } // util 56 | 57 | } // google 58 | 59 | #endif // UTIL_H_ 60 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | init-submodules 2 | *_unittest 3 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | LIBDIR=../lib 16 | GTEST_MODULE=$(LIBDIR)/googletest 17 | GTEST_DIR=$(LIBDIR)/googletest/googletest 18 | GTEST_SOURCEDIR=$(GTEST_DIR)/src 19 | GTEST_HEADERS=$(GTEST_DIR)/include/gtest/*.h \ 20 | $(GTEST_DIR)/include/gtest/internal/*.h 21 | GTEST_SRCS_=$(GTEST_SOURCEDIR)/*.cc $(GTEST_SOURCEDIR)/*.h $(GTEST_HEADERS) 22 | GMOCK_DIR=$(LIBDIR)/googletest/googlemock 23 | GMOCK_SOURCEDIR=$(GMOCK_DIR)/src 24 | GMOCK_HEADERS=$(GMOCK_DIR)/include/gtest/*.h \ 25 | $(GMOCK_DIR)/include/gtest/internal/*.h 26 | GMOCK_SRCS_=$(GMOCK_SOURCEDIR)/*.cc $(GMOCK_SOURCEDIR)/*.h $(GMOCK_HEADERS) 27 | 28 | # TODO: Factor out the common variables. 29 | CPP_NETLIB_DIR=$(LIBDIR)/cpp-netlib 30 | CPP_NETLIB_LIBDIR=$(CPP_NETLIB_DIR)/libs/network/src 31 | NETWORK_URI_DIR=$(CPP_NETLIB_DIR)/deps/uri 32 | NETWORK_URI_LIBDIR=$(NETWORK_URI_DIR)/src 33 | YAML_CPP_DIR=$(LIBDIR)/yaml-cpp 34 | YAML_CPP_LIBDIR=$(YAML_CPP_DIR) 35 | 36 | CPP_NETLIB_LIBS=\ 37 | $(CPP_NETLIB_LIBDIR)/libcppnetlib-client-connections.a \ 38 | $(CPP_NETLIB_LIBDIR)/libcppnetlib-server-parsers.a \ 39 | $(NETWORK_URI_LIBDIR)/libnetwork-uri.a 40 | YAML_CPP_LIBS=$(YAML_CPP_LIBDIR)/libyaml-cpp.a 41 | 42 | CPPFLAGS+= \ 43 | -isystem $(GTEST_DIR)/include -I$(GMOCK_DIR)/include \ 44 | -I$(CPP_NETLIB_DIR) -I$(NETWORK_URI_DIR)/include -I$(YAML_CPP_DIR)/include 45 | CXXFLAGS=\ 46 | -std=c++11 -g -pthread -Wno-write-strings -Wno-deprecated 47 | LDFLAGS=-L$(CPP_NETLIB_LIBDIR) -L$(NETWORK_URI_LIBDIR) -L$(YAML_CPP_LIBDIR) 48 | LDLIBS=\ 49 | -lcppnetlib-client-connections -lcppnetlib-server-parsers -lnetwork-uri \ 50 | -lboost_program_options -lboost_system -lboost_thread -lboost_filesystem \ 51 | -lboost_regex -lpthread -lyajl -lssl -lcrypto -lyaml-cpp 52 | 53 | UNAME_S=$(shell uname -s) 54 | ifeq ($(UNAME_S),Darwin) 55 | CPPFLAGS+= -I/usr/local/include -I/usr/local/opt/openssl/include 56 | CXXFLAGS+= -Wno-deprecated-declarations -Wno-c++14-extensions 57 | LDFLAGS+= -L/usr/local/lib -L/usr/local/opt/openssl/lib 58 | endif 59 | 60 | ifneq ($(BOOST_ROOT),) 61 | CPPFLAGS+= -I$(BOOST_ROOT)/include 62 | LDFLAGS+= -L$(BOOST_ROOT)/lib64 63 | endif 64 | 65 | # Where to find code under test. 66 | SRC_DIR=../src 67 | 68 | TEST_DIR=. 69 | TEST_SOURCES=$(wildcard $(TEST_DIR)/*_unittest.cc) 70 | TEST_OBJS=$(TEST_SOURCES:$(TEST_DIR)/%.cc=%.o) 71 | TESTS=\ 72 | api_server_unittest \ 73 | base64_unittest \ 74 | configuration_unittest \ 75 | environment_unittest \ 76 | format_unittest \ 77 | health_checker_unittest \ 78 | instance_unittest \ 79 | json_unittest \ 80 | kubernetes_unittest \ 81 | logging_unittest \ 82 | oauth2_unittest \ 83 | reporter_unittest \ 84 | resource_unittest \ 85 | store_unittest \ 86 | time_unittest \ 87 | updater_unittest \ 88 | util_unittest 89 | UTIL_SOURCES=$(TEST_DIR)/fake_clock.cc $(TEST_DIR)/fake_http_server.cc 90 | UTIL_OBJS=$(UTIL_SOURCES:$(TEST_DIR)/%.cc=%.o) 91 | 92 | GTEST_LIB=gtest_lib.a 93 | 94 | all: $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(TESTS) 95 | 96 | # TODO: Running the test prints "Running main() from gtest_main.cc". 97 | # Figure out how to fix this. 98 | test: $(TESTS) 99 | @for t in $(TESTS); do \ 100 | echo "Running $$t"; \ 101 | ./$$t || exit 1; \ 102 | done 103 | 104 | clean: 105 | $(RM) $(TESTS) $(GTEST_LIB) $(TEST_OBJS) $(UTIL_OBJS) 106 | 107 | purge: clean 108 | $(RM) init-submodules 109 | (cd .. && git submodule deinit -f $(GTEST_MODULE:../%=%)) 110 | 111 | init-submodules: 112 | (cd .. && git submodule update --init $(GTEST_MODULE:../%=%)) 113 | touch init-submodules 114 | 115 | $(SRC_DIR)/init-submodules: 116 | cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) 117 | 118 | $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp: $(SRC_DIR)/init-submodules 119 | cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) 120 | 121 | $(CPP_NETLIB_LIBS): $(SRC_DIR)/build-cpp-netlib 122 | 123 | $(YAML_CPP_LIBS): $(SRC_DIR)/build-yaml-cpp 124 | 125 | $(SRC_DIR)/%.o: $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(SRC_DIR)/%.cc 126 | cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) 127 | 128 | $(GTEST_SOURCEDIR)/gtest-all.cc: init-submodules 129 | $(GMOCK_SOURCEDIR)/gmock-all.cc $(GMOCK_SOURCEDIR)/gmock_main.cc: init-submodules 130 | 131 | gtest-all.o: $(GTEST_SOURCEDIR)/gtest-all.cc 132 | $(CXX) -c $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -o $@ $^ 133 | gmock-all.o: $(GMOCK_SOURCEDIR)/gmock-all.cc 134 | $(CXX) -c $(CPPFLAGS) -I$(GMOCK_DIR) $(CXXFLAGS) -o $@ $^ 135 | 136 | gmock_main.o: $(GMOCK_SOURCEDIR)/gmock_main.cc 137 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $^ 138 | 139 | $(GTEST_LIB): gtest-all.o gmock-all.o gmock_main.o 140 | $(AR) $(ARFLAGS) $@ $^ 141 | 142 | $(TESTS): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) 143 | 144 | # All unittest objects depend on GTEST_LIB. 145 | # Some headers need CPP_NETLIB_LIBS and YAML_CPP_LIBS. 146 | $(TESTS:%=%.o): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) 147 | 148 | api_server_unittest: api_server_unittest.o $(SRC_DIR)/api_server.o $(SRC_DIR)/configuration.o $(SRC_DIR)/store.o $(SRC_DIR)/json.o $(SRC_DIR)/resource.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(SRC_DIR)/health_checker.o 149 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 150 | base64_unittest: base64_unittest.o $(SRC_DIR)/base64.o 151 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 152 | configuration_unittest: configuration_unittest.o $(SRC_DIR)/configuration.o 153 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 154 | environment_unittest: environment_unittest.o fake_http_server.o $(SRC_DIR)/environment.o $(SRC_DIR)/configuration.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o 155 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 156 | format_unittest: format_unittest.o $(SRC_DIR)/format.o 157 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 158 | health_checker_unittest: health_checker_unittest.o $(SRC_DIR)/health_checker.o $(SRC_DIR)/configuration.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/resource.o $(SRC_DIR)/store.o $(SRC_DIR)/time.o 159 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -lboost_filesystem -lboost_system -o $@ 160 | instance_unittest: instance_unittest.o $(SRC_DIR)/instance.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/resource.o $(SRC_DIR)/store.o $(SRC_DIR)/time.o 161 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 162 | json_unittest: json_unittest.o $(SRC_DIR)/json.o 163 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 164 | kubernetes_unittest: kubernetes_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/kubernetes.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/environment.o $(SRC_DIR)/time.o $(SRC_DIR)/store.o $(SRC_DIR)/resource.o $(SRC_DIR)/updater.o $(SRC_DIR)/instance.o $(SRC_DIR)/format.o $(SRC_DIR)/configuration.o $(SRC_DIR)/health_checker.o 165 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 166 | logging_unittest: logging_unittest.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o 167 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 168 | oauth2_unittest: oauth2_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/oauth2.o $(SRC_DIR)/base64.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o 169 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 170 | resource_unittest: resource_unittest.o $(SRC_DIR)/resource.o $(SRC_DIR)/json.o 171 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 172 | reporter_unittest: reporter_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/reporter.o $(SRC_DIR)/base64.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/oauth2.o $(SRC_DIR)/resource.o $(SRC_DIR)/store.o $(SRC_DIR)/time.o 173 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 174 | store_unittest: store_unittest.o $(SRC_DIR)/store.o $(SRC_DIR)/resource.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(SRC_DIR)/configuration.o 175 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 176 | time_unittest: time_unittest.o $(SRC_DIR)/time.o 177 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 178 | updater_unittest: updater_unittest.o fake_clock.o $(SRC_DIR)/updater.o $(SRC_DIR)/resource.o $(SRC_DIR)/store.o $(SRC_DIR)/configuration.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(SRC_DIR)/json.o $(SRC_DIR)/format.o 179 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 180 | util_unittest: util_unittest.o 181 | $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ 182 | 183 | .PHONY: all test clean purge 184 | -------------------------------------------------------------------------------- /test/api_server_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/api_server.h" 18 | #include "../src/configuration.h" 19 | #include "../src/store.h" 20 | #include "gtest/gtest.h" 21 | 22 | namespace google { 23 | 24 | class ApiServerTest : public ::testing::Test { 25 | protected: 26 | using Dispatcher = MetadataApiServer::Dispatcher; 27 | std::unique_ptr CreateDispatcher( 28 | const std::map, 29 | std::function>& handlers) { 30 | Dispatcher::HandlerMap handler_map; 31 | for (const auto& element : handlers) { 32 | std::function handler = element.second; 33 | handler_map.emplace(element.first, [handler]( 34 | const MetadataApiServer::HttpServer::request& request, 35 | std::shared_ptr conn) { 36 | handler(); 37 | }); 38 | } 39 | return std::unique_ptr(new Dispatcher(handler_map, false)); 40 | } 41 | 42 | void InvokeDispatcher( 43 | const std::unique_ptr& dispatcher, 44 | const std::string& method, const std::string& path) { 45 | MetadataApiServer::HttpServer::request request; 46 | request.method = method; 47 | request.destination = path; 48 | (*dispatcher)(request, nullptr); 49 | } 50 | }; 51 | 52 | TEST_F(ApiServerTest, DispatcherMatchesFullPath) { 53 | bool handler_called = false; 54 | bool bad_handler_called = false; 55 | std::unique_ptr dispatcher = CreateDispatcher({ 56 | {{"GET", "/testPath/"}, [&handler_called]() { 57 | handler_called = true; 58 | }}, 59 | {{"GET", "/badPath/"}, [&bad_handler_called]() { 60 | bad_handler_called = true; 61 | }}, 62 | }); 63 | 64 | InvokeDispatcher(dispatcher, "GET", "/testPath/"); 65 | EXPECT_TRUE(handler_called); 66 | EXPECT_FALSE(bad_handler_called); 67 | } 68 | 69 | TEST_F(ApiServerTest, DispatcherUnmatchedMethod) { 70 | bool handler_called = false; 71 | std::unique_ptr dispatcher = CreateDispatcher({ 72 | {{"GET", "/testPath/"}, [&handler_called]() { 73 | handler_called = true; 74 | }}, 75 | }); 76 | 77 | InvokeDispatcher(dispatcher, "POST", "/testPath/"); 78 | EXPECT_FALSE(handler_called); 79 | } 80 | 81 | TEST_F(ApiServerTest, DispatcherSubstringCheck) { 82 | bool handler_called = false; 83 | std::unique_ptr dispatcher = CreateDispatcher({ 84 | {{"GET", "/testPath/"}, [&handler_called]() { 85 | handler_called = true; 86 | }}, 87 | }); 88 | 89 | InvokeDispatcher(dispatcher, "GET", "/testPathFoo/"); 90 | EXPECT_FALSE(handler_called); 91 | InvokeDispatcher(dispatcher, "GET", "/test/"); 92 | EXPECT_FALSE(handler_called); 93 | InvokeDispatcher(dispatcher, "GET", "/testFooPath/"); 94 | EXPECT_FALSE(handler_called); 95 | InvokeDispatcher(dispatcher, "GET", "/testPath/subPath/"); 96 | EXPECT_TRUE(handler_called); 97 | } 98 | } // namespace google 99 | -------------------------------------------------------------------------------- /test/base64_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/base64.h" 18 | #include "gtest/gtest.h" 19 | 20 | namespace { 21 | 22 | TEST(EncodeTest, EmptyEncode) { 23 | EXPECT_EQ("", base64::Encode("")); 24 | } 25 | 26 | TEST(EncodeTest, SimpleEncode) { 27 | EXPECT_EQ("dGVz", base64::Encode("tes")); 28 | } 29 | 30 | // Base64 encoders typically pad messages to ensure output length % 4 == 0. To 31 | // achieve this, encoders will pad messages with either one or two "=". Our 32 | // implementation does not do this. The following two tests ensure that 33 | // base64::Encode does not append one or two "=". 34 | TEST(EncodeTest, OnePhantom) { 35 | EXPECT_EQ("dGVzdDA", base64::Encode("test0")); 36 | } 37 | 38 | TEST(EncodeTest, TwoPhantom) { 39 | EXPECT_EQ("dGVzdA", base64::Encode("test")); 40 | } 41 | 42 | TEST(DecodeTest, EmptyDecode) { 43 | EXPECT_EQ("", base64::Decode("")); 44 | } 45 | 46 | TEST(DecodeTest, SimpleDecode) { 47 | EXPECT_EQ("tes", base64::Decode("dGVz")); 48 | } 49 | 50 | TEST(DecodeTest, OnePadding) { 51 | EXPECT_EQ("test0", base64::Decode("dGVzdDA=")); 52 | } 53 | 54 | TEST(DecodeTest, OnePhantom) { 55 | EXPECT_EQ("test0", base64::Decode("dGVzdDA")); 56 | } 57 | 58 | TEST(DecodeTest, TwoPadding) { 59 | EXPECT_EQ("test", base64::Decode("dGVzdA==")); 60 | } 61 | 62 | TEST(DecodeTest, TwoPhantom) { 63 | EXPECT_EQ("test", base64::Decode("dGVzdA")); 64 | } 65 | 66 | TEST(RoundTripTest, FullString) { 67 | EXPECT_EQ("tes", base64::Decode(base64::Encode("tes"))); 68 | } 69 | 70 | TEST(RoundTripTest, OnePhantom) { 71 | EXPECT_EQ("test0", base64::Decode(base64::Encode("test0"))); 72 | } 73 | 74 | TEST(RoundTripTest, TwoPhantoms) { 75 | EXPECT_EQ("test", base64::Decode(base64::Encode("test"))); 76 | } 77 | } // namespace 78 | -------------------------------------------------------------------------------- /test/configuration_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/configuration.h" 18 | #include "gtest/gtest.h" 19 | #include "gmock/gmock.h" 20 | 21 | namespace google { 22 | 23 | void VerifyDefaultConfig(const Configuration& config) { 24 | EXPECT_EQ("", config.ProjectId()); 25 | EXPECT_EQ("", config.CredentialsFile()); 26 | EXPECT_EQ(3, config.MetadataApiNumThreads()); 27 | EXPECT_EQ(8000, config.MetadataApiPort()); 28 | EXPECT_EQ("0.0.0.0", config.MetadataApiBindAddress()); 29 | EXPECT_EQ(".", config.MetadataApiResourceTypeSeparator()); 30 | EXPECT_EQ(60, config.MetadataReporterIntervalSeconds()); 31 | EXPECT_EQ(false, config.MetadataReporterPurgeDeleted()); 32 | EXPECT_THAT(config.MetadataReporterUserAgent(), 33 | ::testing::StartsWith("metadata-agent/")); 34 | EXPECT_EQ("https://stackdriver.googleapis.com/" 35 | "v1beta2/projects/{{project_id}}/resourceMetadata:batchUpdate", 36 | config.MetadataIngestionEndpointFormat()); 37 | EXPECT_EQ(8*1024*1024, config.MetadataIngestionRequestSizeLimitBytes()); 38 | EXPECT_EQ(1000, config.MetadataIngestionRequestSizeLimitCount()); 39 | EXPECT_EQ("0.1", config.MetadataIngestionRawContentVersion()); 40 | EXPECT_EQ(60*60, config.InstanceUpdaterIntervalSeconds()); 41 | EXPECT_EQ("", config.InstanceResourceType()); 42 | EXPECT_EQ(0, config.DockerUpdaterIntervalSeconds()); 43 | EXPECT_EQ("unix://%2Fvar%2Frun%2Fdocker.sock/", config.DockerEndpointHost()); 44 | EXPECT_EQ("1.23", config.DockerApiVersion()); 45 | EXPECT_EQ("limit=30", config.DockerContainerFilter()); 46 | EXPECT_EQ(0, config.KubernetesUpdaterIntervalSeconds()); 47 | EXPECT_EQ("https://kubernetes.default.svc", config.KubernetesEndpointHost()); 48 | EXPECT_EQ("", config.KubernetesPodLabelSelector()); 49 | EXPECT_EQ("", config.KubernetesClusterName()); 50 | EXPECT_EQ("", config.KubernetesClusterLocation()); 51 | EXPECT_EQ("", config.KubernetesNodeName()); 52 | EXPECT_EQ(false, config.KubernetesUseWatch()); 53 | EXPECT_EQ(false, config.KubernetesClusterLevelMetadata()); 54 | EXPECT_EQ(true, config.KubernetesServiceMetadata()); 55 | EXPECT_EQ("", config.InstanceId()); 56 | EXPECT_EQ("", config.InstanceZone()); 57 | } 58 | 59 | TEST(ConfigurationTest, NoConfig) { 60 | Configuration config; 61 | VerifyDefaultConfig(config); 62 | } 63 | 64 | TEST(ConfigurationTest, EmptyConfig) { 65 | Configuration config(std::istringstream("")); 66 | VerifyDefaultConfig(config); 67 | } 68 | 69 | TEST(ConfigurationTest, PopulatedConfig) { 70 | Configuration config(std::istringstream( 71 | "ProjectId: TestProjectId\n" 72 | "MetadataApiNumThreads: 13\n" 73 | "MetadataReporterPurgeDeleted: true\n" 74 | "MetadataReporterUserAgent: \"foobar/foobaz\"\n" 75 | "MetadataIngestionRequestSizeLimitCount: 500\n" 76 | )); 77 | EXPECT_EQ("TestProjectId", config.ProjectId()); 78 | EXPECT_EQ(13, config.MetadataApiNumThreads()); 79 | EXPECT_EQ(true, config.MetadataReporterPurgeDeleted()); 80 | EXPECT_EQ("foobar/foobaz", config.MetadataReporterUserAgent()); 81 | EXPECT_EQ(500, config.MetadataIngestionRequestSizeLimitCount()); 82 | } 83 | 84 | TEST(ConfigurationTest, CommentSkipped) { 85 | Configuration config(std::istringstream( 86 | "ProjectId: TestProjectId\n" 87 | "#MetadataApiNumThreads: 13\n" 88 | "MetadataReporterPurgeDeleted: true\n" 89 | )); 90 | EXPECT_EQ(3, config.MetadataApiNumThreads()); 91 | } 92 | 93 | TEST(ConfigurationTest, BlankLine) { 94 | Configuration config(std::istringstream( 95 | "ProjectId: TestProjectId\n" 96 | "\n" 97 | "\n" 98 | "MetadataReporterPurgeDeleted: true\n" 99 | )); 100 | EXPECT_EQ("TestProjectId", config.ProjectId()); 101 | EXPECT_EQ(true, config.MetadataReporterPurgeDeleted()); 102 | } 103 | 104 | class ConfigurationArgumentParserTest : public ::testing::Test { 105 | protected: 106 | static int ParseArguments(Configuration* config, int ac, char** av) { 107 | return config->ParseArguments(ac, av); 108 | } 109 | }; 110 | 111 | TEST_F(ConfigurationArgumentParserTest, CommandLineOverride) { 112 | Configuration config(std::istringstream( 113 | "ProjectId: TestProjectId\n" 114 | "MetadataApiNumThreads: 13\n" 115 | )); 116 | // First, a sanity check. 117 | EXPECT_EQ("TestProjectId", config.ProjectId()); 118 | EXPECT_EQ(13, config.MetadataApiNumThreads()); 119 | EXPECT_EQ(false, config.MetadataReporterPurgeDeleted()); 120 | 121 | char* arguments[] = { 122 | "/path/to/metadatad", 123 | "-o", 124 | "ProjectId=NewProjectId", 125 | "-o", 126 | "MetadataReporterPurgeDeleted=true", 127 | }; 128 | ParseArguments(&config, sizeof(arguments) / sizeof(char*), arguments); 129 | 130 | EXPECT_EQ("NewProjectId", config.ProjectId()); 131 | EXPECT_EQ(13, config.MetadataApiNumThreads()); 132 | EXPECT_EQ(true, config.MetadataReporterPurgeDeleted()); 133 | } 134 | 135 | } // namespace google 136 | -------------------------------------------------------------------------------- /test/environment_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/environment.h" 18 | #include "environment_util.h" 19 | #include "fake_http_server.h" 20 | #include "gtest/gtest.h" 21 | #include "temp_file.h" 22 | 23 | #include 24 | 25 | namespace google { 26 | 27 | class EnvironmentTest : public ::testing::Test { 28 | protected: 29 | static void ReadApplicationDefaultCredentials(const Environment& environment) { 30 | environment.ReadApplicationDefaultCredentials(); 31 | } 32 | }; 33 | 34 | TEST(TemporaryFile, Basic) { 35 | boost::filesystem::path path; 36 | { 37 | testing::TemporaryFile f("foo", "bar"); 38 | path = f.FullPath(); 39 | EXPECT_TRUE(boost::filesystem::exists(path)); 40 | std::string contents; 41 | { 42 | std::ifstream in(path.native()); 43 | in >> contents; 44 | } 45 | EXPECT_EQ("bar", contents); 46 | f.SetContents("xyz"); 47 | { 48 | std::ifstream in(path.native()); 49 | in >> contents; 50 | } 51 | EXPECT_EQ("xyz", contents); 52 | } 53 | EXPECT_FALSE(boost::filesystem::exists(path)); 54 | } 55 | 56 | // 57 | // Tests for values that can be set in configuration. 58 | // 59 | TEST_F(EnvironmentTest, ValuesFromConfig) { 60 | Configuration config(std::istringstream( 61 | "InstanceId: some-instance-id\n" 62 | "InstanceResourceType: some-instance-resource-type\n" 63 | "InstanceZone: some-instance-zone\n" 64 | "KubernetesClusterLocation: some-kubernetes-cluster-location\n" 65 | "KubernetesClusterName: some-kubernetes-cluster-name\n" 66 | )); 67 | Environment environment(config); 68 | EXPECT_EQ("some-instance-id", environment.InstanceId()); 69 | EXPECT_EQ("some-instance-resource-type", environment.InstanceResourceType()); 70 | EXPECT_EQ("some-instance-zone", environment.InstanceZone()); 71 | EXPECT_EQ("some-kubernetes-cluster-location", 72 | environment.KubernetesClusterLocation()); 73 | EXPECT_EQ("some-kubernetes-cluster-name", 74 | environment.KubernetesClusterName()); 75 | } 76 | 77 | TEST_F(EnvironmentTest, ProjectIdFromConfigNewStyleCredentialsProjectId) { 78 | testing::TemporaryFile credentials_file( 79 | std::string(test_info_->name()) + "_creds.json", 80 | "{\"client_email\":\"user@email-project.iam.gserviceaccount.com\"," 81 | "\"private_key\":\"some_key\",\"project_id\":\"my-project\"}"); 82 | Configuration config(std::istringstream( 83 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 84 | )); 85 | Environment environment(config); 86 | EXPECT_EQ("my-project", environment.ProjectId()); 87 | } 88 | 89 | TEST_F(EnvironmentTest, ProjectIdFromConfigNewStyleCredentialsEmail) { 90 | testing::TemporaryFile credentials_file( 91 | std::string(test_info_->name()) + "_creds.json", 92 | "{\"client_email\":\"user@my-project.iam.gserviceaccount.com\"," 93 | "\"private_key\":\"some_key\"}"); 94 | Configuration config(std::istringstream( 95 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 96 | )); 97 | Environment environment(config); 98 | EXPECT_EQ("my-project", environment.ProjectId()); 99 | } 100 | 101 | TEST_F(EnvironmentTest, ProjectIdFromConfigOldStyleCredentialsEmailFails) { 102 | testing::FakeServer server; 103 | testing::TemporaryFile credentials_file( 104 | std::string(test_info_->name()) + "_creds.json", 105 | "{\"client_email\":\"12345-hash@developer.gserviceaccount.com\"," 106 | "\"private_key\":\"some_key\"}"); 107 | Configuration config(std::istringstream( 108 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 109 | )); 110 | Environment environment(config); 111 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 112 | &environment, server.GetUrl() + "/"); 113 | EXPECT_EQ("", environment.ProjectId()); 114 | } 115 | 116 | TEST_F(EnvironmentTest, ReadApplicationDefaultCredentialsSucceeds) { 117 | testing::TemporaryFile credentials_file( 118 | std::string(test_info_->name()) + "_creds.json", 119 | "{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}"); 120 | Configuration config(std::istringstream( 121 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 122 | )); 123 | Environment environment(config); 124 | EXPECT_NO_THROW(ReadApplicationDefaultCredentials(environment)); 125 | EXPECT_EQ("user@example.com", environment.CredentialsClientEmail()); 126 | EXPECT_EQ("some_key", environment.CredentialsPrivateKey()); 127 | } 128 | 129 | TEST_F(EnvironmentTest, ReadApplicationDefaultCredentialsCaches) { 130 | testing::TemporaryFile credentials_file( 131 | std::string(test_info_->name()) + "_creds.json", 132 | "{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}"); 133 | Configuration config(std::istringstream( 134 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 135 | )); 136 | Environment environment(config); 137 | EXPECT_NO_THROW(ReadApplicationDefaultCredentials(environment)); 138 | credentials_file.SetContents( 139 | "{\"client_email\":\"changed@example.com\",\"private_key\":\"12345\"}" 140 | ); 141 | EXPECT_EQ("user@example.com", environment.CredentialsClientEmail()); 142 | credentials_file.SetContents( 143 | "{\"client_email\":\"extra@example.com\",\"private_key\":\"09876\"}" 144 | ); 145 | EXPECT_EQ("some_key", environment.CredentialsPrivateKey()); 146 | } 147 | 148 | // 149 | // Tests for values that can be read from metadata server. 150 | // 151 | TEST_F(EnvironmentTest, GetMetadataStringWithFakeServer) { 152 | testing::FakeServer server; 153 | server.SetResponse("/a/b/c", "hello"); 154 | 155 | Configuration config; 156 | Environment environment(config); 157 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 158 | &environment, server.GetUrl() + "/"); 159 | 160 | EXPECT_EQ("hello", environment.GetMetadataString("a/b/c")); 161 | EXPECT_EQ("", environment.GetMetadataString("unknown/path")); 162 | } 163 | 164 | TEST_F(EnvironmentTest, ValuesFromMetadataServer) { 165 | testing::FakeServer server; 166 | server.SetResponse("/instance/attributes/cluster-location", 167 | "some-cluster-location"); 168 | server.SetResponse("/instance/attributes/cluster-name", "some-cluster-name"); 169 | server.SetResponse("/instance/id", "some-instance-id"); 170 | server.SetResponse("/instance/zone", 171 | "projects/some-project/zones/some-instance-zone"); 172 | server.SetResponse("/project/numeric-project-id", "12345"); 173 | server.SetResponse("/project/project-id", "my-project"); 174 | 175 | Configuration config; 176 | Environment environment(config); 177 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 178 | &environment, server.GetUrl() + "/"); 179 | 180 | EXPECT_EQ("some-cluster-location", environment.KubernetesClusterLocation()); 181 | EXPECT_EQ("some-cluster-name", environment.KubernetesClusterName()); 182 | EXPECT_EQ("some-instance-id", environment.InstanceId()); 183 | EXPECT_EQ("some-instance-zone", environment.InstanceZone()); 184 | EXPECT_EQ("my-project", environment.ProjectId()); 185 | } 186 | 187 | TEST_F(EnvironmentTest, KubernetesClusterLocationFromMetadataServerKubeEnv) { 188 | testing::FakeServer server; 189 | server.SetResponse("/instance/attributes/kube-env", 190 | "KEY: value\n" 191 | "ZONE: some-kube-env-zone\n"); 192 | 193 | Configuration config; 194 | Environment environment(config); 195 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 196 | &environment, server.GetUrl() + "/"); 197 | 198 | EXPECT_EQ("some-kube-env-zone", environment.KubernetesClusterLocation()); 199 | } 200 | 201 | // 202 | // Tests for values with hardcoded defaults. 203 | // 204 | TEST_F(EnvironmentTest, InstanceResourceTypeDefault) { 205 | Configuration config; 206 | Environment environment(config); 207 | EXPECT_EQ("gce_instance", environment.InstanceResourceType()); 208 | } 209 | 210 | } // namespace google 211 | -------------------------------------------------------------------------------- /test/environment_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef ENVIRONMENT_UTIL_H_ 17 | #define ENVIRONMENT_UTIL_H_ 18 | 19 | #include "../src/environment.h" 20 | 21 | namespace google { 22 | namespace testing { 23 | 24 | class EnvironmentUtil { 25 | public: 26 | static void SetMetadataServerUrlForTest(Environment* environment, 27 | const std::string& url) { 28 | environment->SetMetadataServerUrlForTest(url); 29 | } 30 | }; 31 | 32 | } // namespace testing 33 | } // namespace google 34 | 35 | #endif // ENVIRONMENT_UTIL_H_ 36 | -------------------------------------------------------------------------------- /test/fake_clock.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "fake_clock.h" 18 | 19 | namespace google { 20 | namespace testing { 21 | 22 | FakeClock::time_point FakeClock::now_; 23 | FakeClock::time_point FakeClock::future_now_; 24 | std::mutex FakeClock::mutex_; 25 | 26 | } // namespace testing 27 | } // namespace google 28 | -------------------------------------------------------------------------------- /test/fake_clock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef FAKE_CLOCK_H_ 17 | #define FAKE_CLOCK_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "../src/time.h" 25 | 26 | namespace google { 27 | namespace testing { 28 | 29 | class FakeClock { 30 | public: 31 | // Requirements for Clock, see: 32 | // http://en.cppreference.com/w/cpp/named_req/Clock. 33 | using rep = time::seconds::rep; 34 | using period = time::seconds::period; 35 | using duration = time::seconds; 36 | using time_point = std::chrono::time_point; 37 | static constexpr const bool is_steady = false; 38 | 39 | static time_point now() { 40 | std::lock_guard guard(mutex_); 41 | time_point now = now_; 42 | now_ = future_now_; 43 | return now; 44 | } 45 | 46 | // Increment fake clock's internal time. 47 | static void Advance(duration d) { 48 | std::lock_guard guard(mutex_); 49 | now_ += d; 50 | future_now_ = now_; 51 | } 52 | 53 | // Increment fake clock's internal time after the next call to now(). 54 | static void AdvanceAfterNextNowCall(duration d) { 55 | std::lock_guard guard(mutex_); 56 | future_now_ = now_ + d; 57 | } 58 | 59 | private: 60 | FakeClock() = delete; 61 | ~FakeClock() = delete; 62 | FakeClock(FakeClock const&) = delete; 63 | 64 | static time_point now_; 65 | static time_point future_now_; 66 | static std::mutex mutex_; 67 | }; 68 | 69 | } // namespace testing 70 | } // namespace google 71 | 72 | namespace boost { 73 | namespace asio { 74 | // Specialize boost::asio::wait_traits so that 75 | // basic_waitable_timer::async_wait() will work with a FakeClock. 76 | template<> 77 | struct wait_traits { 78 | static google::testing::FakeClock::duration to_wait_duration( 79 | const google::testing::FakeClock::duration& d) { 80 | // This is the idiom for using a timer with a fake clock; for 81 | // example, see the "Jumping Through Time" section of 82 | // http://blog.think-async.com/2007/08/time-travel.html. 83 | // 84 | // Note that basic_waitable_timer uses WaitTraits, which require 85 | // to_wait_duration, not to_posix_duration: 86 | // https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/WaitTraits.html 87 | return std::min(d, google::time::seconds(0.001)); 88 | } 89 | }; 90 | } // namespace asio 91 | } // namespace boost 92 | 93 | namespace std { 94 | // Allow using std::timed_mutex::try_lock_until with a FakeClock. 95 | template<> 96 | inline bool timed_mutex::try_lock_until< 97 | google::testing::FakeClock, google::testing::FakeClock::duration>( 98 | const google::testing::FakeClock::time_point& timeout_time) { 99 | do { 100 | if (try_lock()) { 101 | return true; 102 | } 103 | } while (google::testing::FakeClock::now() < timeout_time); 104 | return false; 105 | } 106 | } // namespace std 107 | 108 | #endif // FAKE_CLOCK_H_ 109 | -------------------------------------------------------------------------------- /test/fake_http_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef FAKE_HTTP_SERVER_H_ 17 | #define FAKE_HTTP_SERVER_H_ 18 | 19 | #include "../src/time.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace google { 27 | namespace testing { 28 | 29 | using GetHandler = 30 | // (path, headers) -> body 31 | std::function&)>; 33 | using PostHandler = 34 | // (path, headers, body) -> body 35 | std::function&, 37 | std::string)>; 38 | 39 | // Starts a server in a separate thread, allowing it to choose an 40 | // available port. 41 | class FakeServer { 42 | public: 43 | FakeServer(); 44 | ~FakeServer(); 45 | 46 | // A handler for GET requests that returns a constant string. 47 | static GetHandler Return(const std::string& body); 48 | 49 | // Returns the URL for this server without a trailing slash: 50 | // "http://:". 51 | std::string GetUrl(); 52 | 53 | // Sets the response for GET requests to a path. 54 | void SetHandler(const std::string& path, GetHandler handler); 55 | 56 | // Sets the response for POST requests to a path. 57 | void SetHandler(const std::string& path, PostHandler handler); 58 | 59 | // Helper method for simple GET responses. 60 | void SetResponse(const std::string& path, const std::string& response) { 61 | SetHandler(path, Return(response)); 62 | } 63 | 64 | // TODO: Consider changing the stream methods to operate on a stream 65 | // object, rather than looking up the path every time. 66 | 67 | // Indicates that for the given path, the server should stream 68 | // responses over a hanging GET. 69 | void AllowStream(const std::string& path); 70 | 71 | // Returns the number of stream watchers for the given path. 72 | int NumWatchers(const std::string& path); 73 | 74 | // Blocks until the total number of connections (including 75 | // connections that have since been closed) to the given path has 76 | // reached at least min_connections. Returns false if the timeout 77 | // is reached before seeing enough client connections. 78 | bool WaitForMinTotalConnections(const std::string& path, 79 | int min_connections, 80 | time::seconds timeout); 81 | 82 | // Sends a streaming response to all watchers for the given path. 83 | void SendStreamResponse(const std::string& path, const std::string& response); 84 | 85 | // Closes all open streams on the server. 86 | void TerminateAllStreams(); 87 | 88 | private: 89 | struct Handler; 90 | typedef boost::network::http::server Server; 91 | 92 | // Handler that maps paths to response strings. 93 | struct Handler { 94 | // Stream holds the state for an endpoint that streams data over a 95 | // hanging GET. 96 | // 97 | // There is a queue for each client, and the mutex guards access 98 | // to queues vector and any queue element. 99 | // 100 | // This struct only holds a pointer to each queue; it does not 101 | // take ownership. 102 | class Stream { 103 | public: 104 | Stream() : connection_counter_(0) {} 105 | void AddQueue(std::queue* queue); 106 | void RemoveQueue(std::queue* queue); 107 | int NumWatchers(); 108 | bool WaitForMinTotalConnections(int min_connections, 109 | time::seconds timeout); 110 | void SendToAllQueues(const std::string& response); 111 | std::string GetNextResponse(std::queue* queue); 112 | 113 | private: 114 | std::mutex mutex_; 115 | std::condition_variable cv_; 116 | // The vector elements are not owned by the Stream object. 117 | std::vector*> queues_; 118 | int connection_counter_; 119 | }; 120 | 121 | void operator()(Server::request const &request, 122 | Server::connection_ptr connection); 123 | 124 | std::map get_handlers; 125 | std::map post_handlers; 126 | std::map path_streams; 127 | }; 128 | 129 | Handler handler_; 130 | Server server_; 131 | std::thread server_thread_; 132 | }; 133 | 134 | } // testing 135 | } // google 136 | 137 | #endif // FAKE_HTTP_SERVER_H_ 138 | -------------------------------------------------------------------------------- /test/format_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/format.h" 18 | #include "gtest/gtest.h" 19 | 20 | namespace { 21 | 22 | TEST(SubstituteTest, SingleSubstitution) { 23 | const std::string format_str("prefix|{{subst}}|suffix"); 24 | EXPECT_EQ( 25 | "prefix|value|suffix", 26 | format::Substitute(format_str, {{"subst", "value"}}) 27 | ); 28 | } 29 | 30 | TEST(SubstituteTest, MultipleSubstitutions) { 31 | const std::string format_str("prefix|{{first}}|middle|{{second}}|suffix"); 32 | EXPECT_EQ( 33 | "prefix|one|middle|two|suffix", 34 | format::Substitute( 35 | format_str, {{"first", "one"}, {"second", "two"}}) 36 | ); 37 | } 38 | 39 | TEST(SubstituteTest, UnterminatedPlaceholder) { 40 | const std::string format_str("prefix|{{subst|suffix"); 41 | EXPECT_THROW( 42 | format::Substitute(format_str, {{"subst", "value"}}), 43 | format::Exception 44 | ); 45 | } 46 | 47 | TEST(SubstituteTest, UnknownParameter) { 48 | const std::string format_str("prefix|{{subst}}|suffix"); 49 | EXPECT_THROW( 50 | format::Substitute(format_str, {{"unknown", "value"}}), 51 | format::Exception 52 | ); 53 | } 54 | } // namespace 55 | -------------------------------------------------------------------------------- /test/health_checker_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/health_checker.h" 18 | #include "../src/store.h" 19 | #include "../src/time.h" 20 | #include "gtest/gtest.h" 21 | #include 22 | 23 | namespace google { 24 | 25 | class HealthCheckerUnittest : public ::testing::Test { 26 | protected: 27 | static void SetUnhealthy(HealthChecker* health_checker, 28 | const std::string& state_name) { 29 | health_checker->SetUnhealthy(state_name); 30 | } 31 | }; 32 | 33 | TEST_F(HealthCheckerUnittest, DefaultHealthy) { 34 | Configuration config; 35 | MetadataStore store(config); 36 | HealthChecker health_checker(config, store); 37 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 38 | } 39 | 40 | TEST_F(HealthCheckerUnittest, SimpleFailure) { 41 | Configuration config; 42 | MetadataStore store(config); 43 | HealthChecker health_checker(config, store); 44 | SetUnhealthy(&health_checker, "kubernetes_pod_thread"); 45 | EXPECT_EQ(health_checker.UnhealthyComponents(), 46 | std::set({ 47 | "kubernetes_pod_thread", 48 | })); 49 | } 50 | 51 | TEST_F(HealthCheckerUnittest, MultiFailure) { 52 | Configuration config; 53 | MetadataStore store(config); 54 | HealthChecker health_checker(config, store); 55 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 56 | SetUnhealthy(&health_checker, "kubernetes_pod_thread"); 57 | EXPECT_EQ(health_checker.UnhealthyComponents(), 58 | std::set({ 59 | "kubernetes_pod_thread", 60 | })); 61 | SetUnhealthy(&health_checker, "kubernetes_node_thread"); 62 | EXPECT_EQ(health_checker.UnhealthyComponents(), 63 | std::set({ 64 | "kubernetes_pod_thread", 65 | "kubernetes_node_thread", 66 | })); 67 | } 68 | 69 | TEST_F(HealthCheckerUnittest, FailurePersists) { 70 | Configuration config; 71 | MetadataStore store(config); 72 | HealthChecker health_checker(config, store); 73 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 74 | SetUnhealthy(&health_checker, "kubernetes_pod_thread"); 75 | EXPECT_EQ(health_checker.UnhealthyComponents(), 76 | std::set({ 77 | "kubernetes_pod_thread", 78 | })); 79 | SetUnhealthy(&health_checker, "kubernetes_pod_thread"); 80 | EXPECT_EQ(health_checker.UnhealthyComponents(), 81 | std::set({ 82 | "kubernetes_pod_thread", 83 | })); 84 | } 85 | 86 | TEST_F(HealthCheckerUnittest, RecentMetadataSucceeds) { 87 | Configuration config(std::istringstream( 88 | "HealthCheckMaxDataAgeSeconds: 20" 89 | )); 90 | MetadataStore store(config); 91 | HealthChecker health_checker(config, store); 92 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 93 | time_point now = std::chrono::system_clock::now(); 94 | time_point then = now - std::chrono::seconds(10); 95 | store.UpdateMetadata( 96 | MonitoredResource("my_resource", {}), 97 | MetadataStore::Metadata("0", false, then, then, json::object({}))); 98 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 99 | } 100 | 101 | TEST_F(HealthCheckerUnittest, StaleMetadataCausesFailure) { 102 | Configuration config(std::istringstream( 103 | "HealthCheckMaxDataAgeSeconds: 1" 104 | )); 105 | MetadataStore store(config); 106 | HealthChecker health_checker(config, store); 107 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 108 | time_point now = std::chrono::system_clock::now(); 109 | time_point then = now - std::chrono::seconds(10); 110 | store.UpdateMetadata( 111 | MonitoredResource("my_resource", {}), 112 | MetadataStore::Metadata("0", false, then, then, json::object({}))); 113 | EXPECT_EQ(health_checker.UnhealthyComponents(), 114 | std::set({ 115 | "my_resource", 116 | })); 117 | } 118 | 119 | TEST_F(HealthCheckerUnittest, UpdatedMetadataClearsFailure) { 120 | Configuration config(std::istringstream( 121 | "HealthCheckMaxDataAgeSeconds: 1" 122 | )); 123 | MetadataStore store(config); 124 | HealthChecker health_checker(config, store); 125 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 126 | time_point now = std::chrono::system_clock::now(); 127 | time_point then = now - std::chrono::seconds(10); 128 | store.UpdateMetadata( 129 | MonitoredResource("my_resource", {}), 130 | MetadataStore::Metadata("0", false, then, then, json::object({}))); 131 | EXPECT_EQ(health_checker.UnhealthyComponents(), 132 | std::set({ 133 | "my_resource", 134 | })); 135 | now = std::chrono::system_clock::now(); 136 | store.UpdateMetadata( 137 | MonitoredResource("my_resource", {}), 138 | MetadataStore::Metadata("0", false, now, now, json::object({}))); 139 | EXPECT_TRUE(health_checker.UnhealthyComponents().empty()); 140 | } 141 | 142 | } // namespace google 143 | -------------------------------------------------------------------------------- /test/instance_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/configuration.h" 18 | #include "../src/environment.h" 19 | #include "../src/instance.h" 20 | #include "../src/resource.h" 21 | #include "../src/updater.h" 22 | #include "gtest/gtest.h" 23 | 24 | namespace google { 25 | namespace { 26 | 27 | TEST(InstanceTest, GetInstanceMonitoredResource) { 28 | Configuration config(std::istringstream( 29 | "InstanceResourceType: gce_instance\n" 30 | "InstanceId: 1234567891011\n" 31 | "InstanceZone: us-east1-b\n" 32 | )); 33 | Environment env(config); 34 | EXPECT_EQ(MonitoredResource("gce_instance", { 35 | {"instance_id", "1234567891011"}, 36 | {"zone", "us-east1-b"} 37 | }), InstanceReader::InstanceResource(env)); 38 | } 39 | 40 | TEST(InstanceTest, GetInstanceMetatadataQuery) { 41 | Configuration config(std::istringstream( 42 | "InstanceResourceType: gce_instance\n" 43 | "InstanceId: 1234567891011\n" 44 | "InstanceZone: us-east1-b\n" 45 | )); 46 | InstanceReader reader(config); 47 | const auto result = reader.MetadataQuery(); 48 | EXPECT_EQ(1, result.size()); 49 | const MetadataUpdater::ResourceMetadata& rm = result[0]; 50 | EXPECT_EQ(MonitoredResource("gce_instance", { 51 | {"instance_id", "1234567891011"}, 52 | {"zone", "us-east1-b"} 53 | }), rm.resource()); 54 | EXPECT_EQ(std::vector({"", "1234567891011"}), rm.ids()); 55 | EXPECT_TRUE(rm.metadata().ignore); 56 | } 57 | 58 | } // namespace 59 | } // google 60 | -------------------------------------------------------------------------------- /test/logging_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/logging.h" 18 | #include "gtest/gtest.h" 19 | 20 | #include 21 | #include 22 | 23 | namespace google { 24 | namespace { 25 | 26 | TEST(LoggingTest, Logger) { 27 | std::ostringstream out; 28 | 29 | // Logger flushes on destruction. 30 | { 31 | LogStream stream(out); 32 | Logger logger("somefile.cc", 123, Logger::WARNING, &stream); 33 | logger << "Test message"; 34 | } 35 | 36 | EXPECT_TRUE(boost::regex_match(out.str(), boost::regex( 37 | "W\\d{4} \\d{2}:\\d{2}:\\d{2} \\d+ " 38 | "somefile.cc:123] Test message\n"))); 39 | } 40 | 41 | } // namespace 42 | } // namespace google 43 | -------------------------------------------------------------------------------- /test/oauth2_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/oauth2.h" 18 | #include "environment_util.h" 19 | #include "fake_clock.h" 20 | #include "fake_http_server.h" 21 | #include "temp_file.h" 22 | #include "gmock/gmock.h" 23 | #include "gtest/gtest.h" 24 | 25 | #include 26 | 27 | namespace google { 28 | 29 | class OAuth2Test : public ::testing::Test { 30 | protected: 31 | static void SetTokenEndpointForTest(OAuth2* auth, 32 | const std::string& endpoint) { 33 | auth->SetTokenEndpointForTest(endpoint); 34 | } 35 | }; 36 | 37 | namespace { 38 | 39 | TEST_F(OAuth2Test, GetAuthHeaderValueUsingTokenFromCredentials) { 40 | int post_count = 0; 41 | std::map post_headers; 42 | std::string post_body; 43 | 44 | testing::FakeServer oauth_server; 45 | oauth_server.SetHandler( 46 | "/oauth2/v3/token", 47 | [&](const std::string& path, 48 | const std::map& headers, 49 | const std::string& body) -> std::string { 50 | post_count++; 51 | post_headers = headers; 52 | post_body = body; 53 | return 54 | "{\"access_token\": \"the-access-token\"," 55 | " \"token_type\": \"Bearer\"," 56 | " \"expires_in\": 3600}"; 57 | }); 58 | testing::TemporaryFile credentials_file( 59 | std::string(test_info_->name()) + "_creds.json", 60 | "{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}"); 61 | Configuration config(std::istringstream( 62 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 63 | )); 64 | Environment environment(config); 65 | OAuth2 auth(environment); 66 | SetTokenEndpointForTest(&auth, oauth_server.GetUrl() + "/oauth2/v3/token"); 67 | 68 | EXPECT_EQ("Bearer the-access-token", auth.GetAuthHeaderValue()); 69 | 70 | // Verify the POST contents sent to the token endpoint. 71 | const std::pair content_type( 72 | "Content-Type", "application/x-www-form-urlencoded"); 73 | EXPECT_EQ(1, post_count); 74 | EXPECT_THAT(post_headers, ::testing::Contains(content_type)); 75 | EXPECT_THAT(post_body, ::testing::StartsWith( 76 | "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer" 77 | "&assertion=")); 78 | } 79 | 80 | TEST_F(OAuth2Test, GetAuthHeaderValueUsingTokenFromMetadataServer) { 81 | testing::FakeServer metadata_server; 82 | metadata_server.SetResponse("/instance/service-accounts/default/token", 83 | "{\"access_token\": \"the-access-token\"," 84 | " \"token_type\": \"Bearer\"," 85 | " \"expires_in\": 3600}"); 86 | Configuration config; 87 | Environment environment(config); 88 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 89 | &environment, metadata_server.GetUrl() + "/"); 90 | OAuth2 auth(environment); 91 | 92 | EXPECT_EQ("Bearer the-access-token", auth.GetAuthHeaderValue()); 93 | } 94 | 95 | TEST_F(OAuth2Test, GetAuthHeaderValueUsingTokenFromMetadataServerAsFallback) { 96 | testing::FakeServer metadata_server; 97 | metadata_server.SetResponse("/instance/service-accounts/default/token", 98 | "{\"access_token\": \"the-access-token\"," 99 | " \"token_type\": \"Bearer\"," 100 | " \"expires_in\": 3600}"); 101 | // Setup fake OAuth endpoint, but don't set response for 102 | // /oauth2/v3/token, so that it returns an error. 103 | // 104 | // We'll check that we fallback to the Metadata Server. 105 | testing::FakeServer oauth_server; 106 | testing::TemporaryFile credentials_file( 107 | std::string(test_info_->name()) + "_creds.json", 108 | "{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}"); 109 | Configuration config(std::istringstream( 110 | "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" 111 | )); 112 | Environment environment(config); 113 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 114 | &environment, metadata_server.GetUrl() + "/"); 115 | OAuth2 auth(environment); 116 | SetTokenEndpointForTest(&auth, oauth_server.GetUrl() + "/oauth2/v3/token"); 117 | 118 | EXPECT_EQ("Bearer the-access-token", auth.GetAuthHeaderValue()); 119 | } 120 | 121 | TEST_F(OAuth2Test, GetAuthHeaderValueTokenJsonMissingField) { 122 | testing::FakeServer metadata_server; 123 | // JSON is missing "expires_in" field. 124 | metadata_server.SetResponse("/instance/service-accounts/default/token", 125 | "{\"access_token\": \"the-access-token\"," 126 | " \"token_type\": \"Bearer\"}"); 127 | Configuration config; 128 | Environment environment(config); 129 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 130 | &environment, metadata_server.GetUrl() + "/"); 131 | OAuth2 auth(environment); 132 | 133 | EXPECT_EQ("", auth.GetAuthHeaderValue()); 134 | } 135 | 136 | TEST_F(OAuth2Test, GetAuthHeaderValueMetadataServerReturnsEmptyToken) { 137 | testing::FakeServer metadata_server; 138 | metadata_server.SetResponse("/instance/service-accounts/default/token", ""); 139 | Configuration config; 140 | Environment environment(config); 141 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 142 | &environment, metadata_server.GetUrl() + "/"); 143 | OAuth2 auth(environment); 144 | 145 | EXPECT_EQ("", auth.GetAuthHeaderValue()); 146 | } 147 | 148 | namespace { 149 | // OAuth2 implementation using a FakeClock for token expiration. 150 | class FakeOAuth2 : public OAuth2 { 151 | public: 152 | FakeOAuth2(const Environment& environment) 153 | : OAuth2(environment, ExpirationImpl::New()) {} 154 | }; 155 | } // namespace 156 | 157 | TEST_F(OAuth2Test, GetAuthHeaderValueCachingAndExpiration) { 158 | testing::FakeServer metadata_server; 159 | Configuration config; 160 | Environment environment(config); 161 | testing::EnvironmentUtil::SetMetadataServerUrlForTest( 162 | &environment, metadata_server.GetUrl() + "/"); 163 | FakeOAuth2 auth(environment); 164 | 165 | // Metadata Server returns token "1". 166 | metadata_server.SetResponse("/instance/service-accounts/default/token", 167 | "{\"access_token\": \"the-access-token-1\"," 168 | " \"token_type\": \"Bearer\"," 169 | " \"expires_in\": 3600}"); 170 | EXPECT_EQ("Bearer the-access-token-1", auth.GetAuthHeaderValue()); 171 | 172 | // Metadata Server returns token "2", but cached token is still "1". 173 | metadata_server.SetResponse("/instance/service-accounts/default/token", 174 | "{\"access_token\": \"the-access-token-2\"," 175 | " \"token_type\": \"Bearer\"," 176 | " \"expires_in\": 3600}"); 177 | EXPECT_EQ("Bearer the-access-token-1", auth.GetAuthHeaderValue()); 178 | 179 | // Advance clock only 2000, still use cached token of "1". 180 | testing::FakeClock::Advance(std::chrono::seconds(2000)); 181 | EXPECT_EQ("Bearer the-access-token-1", auth.GetAuthHeaderValue()); 182 | 183 | // Advance clock another 1550, so now it is within 60 seconds of 184 | // expiration and we fetch new token of "2" from Metadata Server. 185 | testing::FakeClock::Advance(std::chrono::seconds(1550)); 186 | EXPECT_EQ("Bearer the-access-token-2", auth.GetAuthHeaderValue()); 187 | } 188 | 189 | } // namespace 190 | } // namespace google 191 | -------------------------------------------------------------------------------- /test/reporter_unittest.cc: -------------------------------------------------------------------------------- 1 | #include "../src/reporter.h" 2 | 3 | #include "../src/configuration.h" 4 | #include "fake_clock.h" 5 | #include "fake_http_server.h" 6 | #include "gmock/gmock.h" 7 | #include "gtest/gtest.h" 8 | 9 | #include 10 | 11 | namespace google { 12 | 13 | namespace { 14 | MATCHER_P(ContainsAll, map, "") { 15 | std::vector missing; 16 | for (const auto& kv : map) { 17 | if (!::testing::Value(arg, ::testing::Contains(kv))) { 18 | missing.push_back(::testing::PrintToString(kv)); 19 | } 20 | } 21 | if (!missing.empty()) { 22 | *result_listener << "does not contain " 23 | << boost::algorithm::join(missing, " or "); 24 | } 25 | return missing.empty(); 26 | } 27 | } // namespace 28 | 29 | // MetadataReporter that uses a FakeClock. 30 | class FakeMetadataReporter : public MetadataReporter { 31 | public: 32 | FakeMetadataReporter(const Configuration& config, 33 | MetadataStore* store, double period_s) 34 | : MetadataReporter( 35 | config, store, period_s, /*initial_wait_s=*/0, 36 | TimerImpl::New(false, "fake metadata reporter")) {} 37 | }; 38 | 39 | TEST(ReporterTest, MetadataReporter) { 40 | // Set up a fake server representing the Resource Metadata API. 41 | // It will collect POST data from the MetadataReporter. 42 | std::mutex post_data_mutex; 43 | std::condition_variable post_data_cv; 44 | int post_count = 0; 45 | std::map response_headers; 46 | std::string response_body; 47 | testing::FakeServer server; 48 | constexpr const char kUpdatePath[] = 49 | "/v1beta2/projects/TestProjectId/resourceMetadata:batchUpdate"; 50 | server.SetHandler( 51 | kUpdatePath, 52 | [&](const std::string& path, 53 | const std::map& headers, 54 | const std::string& body) -> std::string { 55 | { 56 | std::lock_guard lk(post_data_mutex); 57 | post_count++; 58 | response_headers = headers; 59 | response_body = body; 60 | } 61 | post_data_cv.notify_all(); 62 | return "this POST response is ignored"; 63 | }); 64 | 65 | // Configure the the MetadataReporter to point to the fake server 66 | // and start it with a 60 second polling interval. We control time 67 | // using a fake clock. 68 | Configuration config(std::istringstream( 69 | "ProjectId: TestProjectId\n" 70 | "MetadataIngestionEndpointFormat: " + server.GetUrl() + 71 | "/v1beta2/projects/{{project_id}}/resourceMetadata:batchUpdate\n" 72 | "MetadataReporterUserAgent: metadata-agent/1.2.3-4\n" 73 | )); 74 | MetadataStore store(config); 75 | MonitoredResource resource("type", {}); 76 | store.UpdateMetadata(resource, MetadataStore::Metadata( 77 | "default-version", 78 | false, 79 | time::rfc3339::FromString("2018-03-03T01:23:45.678901234Z"), 80 | time::rfc3339::FromString("2018-03-03T01:32:45.678901234Z"), 81 | json::object({{"f", json::string("hello")}}))); 82 | double period_s = 60.0; 83 | FakeMetadataReporter reporter(config, &store, period_s); 84 | 85 | // The headers and body we expect to see in the POST requests. 86 | std::map expected_headers{ 87 | {"Content-Type", "application/json"}, 88 | {"User-Agent", "metadata-agent/1.2.3-4"}, 89 | }; 90 | auto expected_body = [](const std::string& state) -> std::string { 91 | return json::object({ 92 | {"entries", json::array({ 93 | json::object({ // MonitoredResourceMetadata 94 | {"resource", json::object({ 95 | {"type", json::string("type")}, 96 | {"labels", json::object({})}, 97 | })}, 98 | {"rawContentVersion", json::string("default-version")}, 99 | {"rawContent", json::object({{"f", json::string("hello")}})}, 100 | {"state", json::string(state)}, 101 | {"createTime", json::string("2018-03-03T01:23:45.678901234Z")}, 102 | {"collectTime", json::string("2018-03-03T01:32:45.678901234Z")}, 103 | }) 104 | })} 105 | })->ToString(); 106 | }; 107 | const std::string expected_body_active = expected_body("ACTIVE"); 108 | const std::string expected_body_deleted = expected_body("DELETED"); 109 | 110 | // Wait for 1st post to server, and verify contents. 111 | { 112 | std::unique_lock lk(post_data_mutex); 113 | post_data_cv.wait(lk, [&post_count]{ return post_count >= 1; }); 114 | } 115 | EXPECT_EQ(1, post_count); 116 | EXPECT_THAT(response_headers, ContainsAll(expected_headers)); 117 | EXPECT_EQ(expected_body_active, response_body); 118 | 119 | // Advance fake clock, wait for 2nd post, verify contents. 120 | testing::FakeClock::AdvanceAfterNextNowCall(time::seconds(60)); 121 | { 122 | std::unique_lock lk(post_data_mutex); 123 | post_data_cv.wait(lk, [&post_count]{ return post_count >= 2; }); 124 | } 125 | EXPECT_EQ(2, post_count); 126 | EXPECT_THAT(response_headers, ContainsAll(expected_headers)); 127 | EXPECT_EQ(expected_body_active, response_body); 128 | 129 | // Mark metadata as deleted in store, advance fake clock, wait for 130 | // 3rd post, verify contents. 131 | store.UpdateMetadata(resource, MetadataStore::Metadata( 132 | "default-version", 133 | true, 134 | time::rfc3339::FromString("2018-03-03T01:23:45.678901234Z"), 135 | time::rfc3339::FromString("2018-03-03T01:32:45.678901234Z"), 136 | json::object({{"f", json::string("hello")}}))); 137 | testing::FakeClock::AdvanceAfterNextNowCall(time::seconds(60)); 138 | { 139 | std::unique_lock lk(post_data_mutex); 140 | post_data_cv.wait(lk, [&post_count]{ return post_count >= 3; }); 141 | } 142 | EXPECT_EQ(3, post_count); 143 | EXPECT_THAT(response_headers, ContainsAll(expected_headers)); 144 | EXPECT_EQ(expected_body_deleted, response_body); 145 | } 146 | 147 | } // namespace google 148 | -------------------------------------------------------------------------------- /test/resource_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/resource.h" 18 | #include "../src/json.h" 19 | #include "gtest/gtest.h" 20 | 21 | namespace { 22 | 23 | TEST(ResourceTest, Type) { 24 | google::MonitoredResource mr("some_resource", {}); 25 | EXPECT_EQ("some_resource", mr.type()); 26 | } 27 | 28 | TEST(ResourceTest, Labels) { 29 | google::MonitoredResource mr("", {{"foo", "bar"}, {"bar", "baz"}}); 30 | EXPECT_EQ(2, mr.labels().size()); 31 | EXPECT_EQ("bar", mr.labels().at("foo")); 32 | EXPECT_EQ("baz", mr.labels().at("bar")); 33 | } 34 | 35 | TEST(ResourceTest, BasicEquality) { 36 | google::MonitoredResource mr1("", {}); 37 | google::MonitoredResource mr2("", {}); 38 | 39 | EXPECT_EQ(mr1, mr2); 40 | } 41 | 42 | TEST(ResourceTest, BasicTypeComparison) { 43 | google::MonitoredResource mr1("1", {}); 44 | google::MonitoredResource mr2("2", {}); 45 | 46 | EXPECT_LT(mr1, mr2); 47 | } 48 | 49 | TEST(ResourceTest, BasicLabelComparison) { 50 | google::MonitoredResource mr1("", {{"a", "a"}}); 51 | google::MonitoredResource mr2("", {{"b", "b"}}); 52 | 53 | EXPECT_NE(mr1, mr2); 54 | EXPECT_LT(mr1, mr2); 55 | } 56 | TEST(ResourceTest, BasicJson) { 57 | json::value target = json::object({ 58 | {"type", json::string("test")}, 59 | {"labels", json::object({ 60 | {"a", json::string("a")}, 61 | {"b", json::string("b")} 62 | })} 63 | }); 64 | 65 | google::MonitoredResource mr("test", {{"a", "a"}, {"b", "b"}}); 66 | 67 | EXPECT_EQ(target->ToString(), mr.ToJSON()->ToString()); 68 | } 69 | } // namespace 70 | -------------------------------------------------------------------------------- /test/temp_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | #ifndef TEMP_FILE_H_ 17 | #define TEMP_FILE_H_ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace google { 24 | namespace testing { 25 | 26 | // A file with a given name in a temporary (unique) directory. 27 | boost::filesystem::path TempPath(const std::string& filename) { 28 | boost::filesystem::path path = boost::filesystem::temp_directory_path(); 29 | // Older versions of boost::filesystem::path require a locale. 30 | const boost::filesystem::path::codecvt_type& codecvt = 31 | std::use_facet(std::locale("")); 32 | path.append(boost::filesystem::unique_path().native(), codecvt); 33 | path.append(filename, codecvt); 34 | return path; 35 | } 36 | 37 | // Creates a file for the lifetime of the object and removes it after. 38 | class TemporaryFile { 39 | public: 40 | TemporaryFile(const std::string& filename, const std::string& contents) 41 | : path_(TempPath(filename)) { 42 | boost::filesystem::create_directories(path_.parent_path()); 43 | SetContents(contents); 44 | } 45 | ~TemporaryFile() { 46 | boost::filesystem::remove_all(path_.parent_path()); 47 | } 48 | void SetContents(const std::string& contents) const { 49 | std::ofstream file(path_.native()); 50 | file << contents << std::flush; 51 | } 52 | const boost::filesystem::path& FullPath() const { return path_; } 53 | private: 54 | boost::filesystem::path path_; 55 | }; 56 | 57 | } // namespace testing 58 | } // namespace google 59 | 60 | #endif // TEMP_FILE_H_ 61 | -------------------------------------------------------------------------------- /test/time_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/time.h" 18 | #include "gtest/gtest.h" 19 | 20 | using namespace google; 21 | 22 | namespace { 23 | 24 | TEST(TimeTest, EpochToString) { 25 | const time_point epoch; 26 | EXPECT_EQ( 27 | "1970-01-01T00:00:00.000000000Z", 28 | time::rfc3339::ToString(epoch) 29 | ); 30 | } 31 | 32 | TEST(TimeTest, RoundtripViaTimePoint) { 33 | const time_point t = 34 | time::rfc3339::FromString("2018-03-03T01:23:45.678901234Z"); 35 | EXPECT_EQ( 36 | "2018-03-03T01:23:45.678901234Z", 37 | time::rfc3339::ToString(t) 38 | ); 39 | } 40 | 41 | TEST(TimeTest, RoundtripViaString) { 42 | const time_point t = std::chrono::system_clock::now(); 43 | EXPECT_EQ( 44 | t, 45 | time::rfc3339::FromString(time::rfc3339::ToString(t)) 46 | ); 47 | } 48 | 49 | TEST(TimeTest, FromStringNoNanos) { 50 | const time_point t = 51 | time::rfc3339::FromString("2018-03-03T01:23:45Z"); 52 | EXPECT_EQ( 53 | "2018-03-03T01:23:45.000000000Z", 54 | time::rfc3339::ToString(t) 55 | ); 56 | } 57 | 58 | TEST(TimeTest, FromStringFewerDigits) { 59 | const time_point t = 60 | time::rfc3339::FromString("2018-03-03T01:23:45.6789Z"); 61 | EXPECT_EQ( 62 | "2018-03-03T01:23:45.678900000Z", 63 | time::rfc3339::ToString(t) 64 | ); 65 | } 66 | 67 | TEST(TimeTest, FromStringMoreDigits) { 68 | const time_point t = 69 | time::rfc3339::FromString("2018-03-03T01:23:45.67890123456789Z"); 70 | EXPECT_EQ( 71 | "2018-03-03T01:23:45.678901234Z", 72 | time::rfc3339::ToString(t) 73 | ); 74 | } 75 | 76 | TEST(TimeTest, FromStringLargeNanos) { 77 | const time_point t = 78 | time::rfc3339::FromString("2018-03-03T01:23:45.9876543210987Z"); 79 | EXPECT_EQ( 80 | "2018-03-03T01:23:45.987654321Z", 81 | time::rfc3339::ToString(t) 82 | ); 83 | } 84 | 85 | TEST(TimeTest, FromStringPositiveNanos) { 86 | const time_point t = 87 | time::rfc3339::FromString("2018-03-03T01:23:45.+678901234Z"); 88 | EXPECT_EQ( 89 | "1970-01-01T00:00:00.000000000Z", 90 | time::rfc3339::ToString(t) 91 | ); 92 | } 93 | 94 | TEST(TimeTest, FromStringNegativeNanos) { 95 | const time_point t = 96 | time::rfc3339::FromString("2018-03-03T01:23:45.-678901234Z"); 97 | EXPECT_EQ( 98 | "1970-01-01T00:00:00.000000000Z", 99 | time::rfc3339::ToString(t) 100 | ); 101 | } 102 | 103 | TEST(TimeTest, FromStringPositiveSeconds) { 104 | const time_point t = 105 | time::rfc3339::FromString("2018-03-03T01:23:+4.567890123Z"); 106 | EXPECT_EQ( 107 | "1970-01-01T00:00:00.000000000Z", 108 | time::rfc3339::ToString(t) 109 | ); 110 | } 111 | 112 | TEST(TimeTest, FromStringNegativeSeconds) { 113 | const time_point t = 114 | time::rfc3339::FromString("2018-03-03T01:23:-4.567890123Z"); 115 | EXPECT_EQ( 116 | "1970-01-01T00:00:00.000000000Z", 117 | time::rfc3339::ToString(t) 118 | ); 119 | } 120 | 121 | TEST(TimeTest, FromStringHexSeconds) { 122 | const time_point t = 123 | time::rfc3339::FromString("2018-03-03T01:23:0x45.678901234Z"); 124 | EXPECT_EQ( 125 | "1970-01-01T00:00:00.000000000Z", 126 | time::rfc3339::ToString(t) 127 | ); 128 | } 129 | 130 | TEST(TimeTest, FromStringInfSeconds) { 131 | const time_point t1 = 132 | time::rfc3339::FromString("2018-03-03T01:23:+infZ"); 133 | EXPECT_EQ( 134 | "1970-01-01T00:00:00.000000000Z", 135 | time::rfc3339::ToString(t1) 136 | ); 137 | const time_point t2 = 138 | time::rfc3339::FromString("2018-03-03T01:23:-infZ"); 139 | EXPECT_EQ( 140 | "1970-01-01T00:00:00.000000000Z", 141 | time::rfc3339::ToString(t2) 142 | ); 143 | const time_point t3 = 144 | time::rfc3339::FromString("2018-03-03T01:23:+nanZ"); 145 | EXPECT_EQ( 146 | "1970-01-01T00:00:00.000000000Z", 147 | time::rfc3339::ToString(t3) 148 | ); 149 | const time_point t4 = 150 | time::rfc3339::FromString("2018-03-03T01:23:-nanZ"); 151 | EXPECT_EQ( 152 | "1970-01-01T00:00:00.000000000Z", 153 | time::rfc3339::ToString(t4) 154 | ); 155 | } 156 | 157 | TEST(TimeTest, FromStringTooManySeconds) { 158 | const time_point t = 159 | time::rfc3339::FromString("2018-03-03T01:23:1045.678901234Z"); 160 | EXPECT_EQ( 161 | "1970-01-01T00:00:00.000000000Z", 162 | time::rfc3339::ToString(t) 163 | ); 164 | } 165 | 166 | TEST(TimeTest, FromStringScientificSeconds) { 167 | const time_point t = 168 | time::rfc3339::FromString("2018-03-03T01:23:45e+0Z"); 169 | EXPECT_EQ( 170 | "1970-01-01T00:00:00.000000000Z", 171 | time::rfc3339::ToString(t) 172 | ); 173 | } 174 | 175 | TEST(TimeTest, SecondsSinceEpoch) { 176 | const time_point t = time_point() + std::chrono::seconds(1500000000); 177 | EXPECT_EQ(1500000000, time::SecondsSinceEpoch(t)); 178 | } 179 | 180 | TEST(TimeTest, SafeLocaltime) { 181 | const std::time_t now_c = 182 | std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 183 | std::tm local_time = time::safe_localtime(&now_c); 184 | EXPECT_EQ(std::mktime(std::localtime(&now_c)), std::mktime(&local_time)); 185 | } 186 | 187 | TEST(TimeTest, SafeGmtime) { 188 | const std::time_t now_c = 189 | std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 190 | std::tm gm_time = time::safe_gmtime(&now_c); 191 | EXPECT_EQ(std::mktime(std::gmtime(&now_c)), std::mktime(&gm_time)); 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /test/util_unittest.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | #include "../src/util.h" 18 | #include "gtest/gtest.h" 19 | 20 | #include "../src/time.h" 21 | 22 | namespace google { 23 | 24 | namespace { 25 | 26 | class Fail {}; 27 | 28 | class Terminate {}; 29 | 30 | class TerminateSubclass : public Fail {}; 31 | 32 | TEST(RetryTest, NoRetriesOnSuccess) { 33 | int invocation_count = 0; 34 | EXPECT_NO_THROW((util::Retry( 35 | 10, time::seconds(0.01), 36 | [&invocation_count]() { ++invocation_count; }))); 37 | EXPECT_EQ(1, invocation_count); 38 | } 39 | 40 | TEST(RetryTest, RetryOnFail) { 41 | int invocation_count = 0; 42 | EXPECT_THROW( 43 | (util::Retry( 44 | 10, time::seconds(0.01), 45 | [&invocation_count]() { ++invocation_count; throw Fail(); })), 46 | Fail); 47 | EXPECT_EQ(10, invocation_count); 48 | } 49 | 50 | TEST(RetryTest, NoRetryOnTerminate) { 51 | int invocation_count = 0; 52 | EXPECT_THROW( 53 | (util::Retry( 54 | 10, time::seconds(0.01), 55 | [&invocation_count]() { ++invocation_count; throw Terminate(); })), 56 | Terminate); 57 | EXPECT_EQ(1, invocation_count); 58 | } 59 | 60 | TEST(RetryTest, RetryWhileFail) { 61 | int invocation_count = 0; 62 | EXPECT_THROW( 63 | (util::Retry( 64 | 10, time::seconds(0.01), 65 | [&invocation_count]() { 66 | if (++invocation_count < 3) { 67 | throw Fail(); 68 | } else { 69 | throw Terminate(); 70 | } 71 | })), 72 | Terminate); 73 | EXPECT_EQ(3, invocation_count); 74 | } 75 | 76 | TEST(RetryTest, RetryWithSubclass) { 77 | int invocation_count = 0; 78 | EXPECT_THROW((util::Retry( 79 | 10, time::seconds(0.01), 80 | [&invocation_count]() { 81 | if (++invocation_count < 5) { 82 | throw Fail(); 83 | } else { 84 | throw TerminateSubclass(); 85 | } 86 | })), TerminateSubclass); 87 | EXPECT_EQ(5, invocation_count); 88 | } 89 | } // namespace 90 | } // namespace google 91 | --------------------------------------------------------------------------------