├── .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