diff --git a/CHANGELOG.md b/CHANGELOG.md index 16a250392e6..8c7cea63b74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The admin UI is removed and unusable in this release. The `[admin]` configuratio - [#8042](https://github.com/influxdata/influxdb/issues/8042): Add bitwise AND, OR and XOR operators to the query language. - [#8302](https://github.com/influxdata/influxdb/pull/8302): Write throughput/concurrency improvements - [#8273](https://github.com/influxdata/influxdb/issues/8273): Remove the admin UI. +- [#8327](https://github.com/influxdata/influxdb/pull/8327): Update to go1.8.1 ### Bugfixes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6100f8aef67..f7d4c259f9d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,7 +69,7 @@ second to sign our CLA, which can be found Installing Go ------------- -InfluxDB requires Go 1.7.4. +InfluxDB requires Go 1.8.1 At InfluxDB we find gvm, a Go version manager, useful for installing Go. For instructions on how to install it see [the gvm page on github](https://github.com/moovweb/gvm). @@ -77,8 +77,8 @@ on how to install it see [the gvm page on github](https://github.com/moovweb/gvm After installing gvm you can install and set the default go version by running the following: - gvm install go1.7.4 - gvm use go1.7.4 --default + gvm install go1.8.1 + gvm use go1.8.1 --default Installing GDM ------------- diff --git a/DOCKER.md b/DOCKER.md deleted file mode 100644 index 4d902e642ec..00000000000 --- a/DOCKER.md +++ /dev/null @@ -1,30 +0,0 @@ -# Docker Setup -======================== - -This document describes how to build and run a minimal InfluxDB container under Docker. Currently, it has only been tested for local development and assumes that you have a working docker environment. - -## Building Image - -To build a docker image for InfluxDB from your current checkout, run the following: - -``` -$ ./build-docker.sh -``` - -This script uses the `golang:1.7.4` image to build a fully static binary of `influxd` and then adds it to a minimal `scratch` image. - -To build the image using a different version of go: - -``` -$ GO_VER=1.7.4 ./build-docker.sh -``` - -Available version can be found [here](https://hub.docker.com/_/golang/). - -## Single Node Container - -This will start an interactive, single-node, that publishes the containers port `8086` and `8088` to the hosts ports `8086` and `8088` respectively. This is identical to starting `influxd` manually. - -``` -$ docker run -it -p 8086:8086 -p 8088:8088 influxdb -``` diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index d30cd300dbb..00000000000 --- a/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM busybox:ubuntu-14.04 - -MAINTAINER Jason Wilder "" - -# admin, http, udp, cluster, graphite, opentsdb, collectd -EXPOSE 8083 8086 8086/udp 8088 2003 4242 25826 - -WORKDIR /app - -# copy binary into image -COPY influxd /app/ - -# Add influxd to the PATH -ENV PATH=/app:$PATH - -# Generate a default config -RUN influxd config > /etc/influxdb.toml - -# Use /data for all disk storage -RUN sed -i 's/dir = "\/.*influxdb/dir = "\/data/' /etc/influxdb.toml - -VOLUME ["/data"] - -ENTRYPOINT ["influxd", "--config", "/etc/influxdb.toml"] diff --git a/Dockerfile_build_ubuntu32 b/Dockerfile_build_ubuntu32 index 49466a0a2e5..f9c975ce777 100644 --- a/Dockerfile_build_ubuntu32 +++ b/Dockerfile_build_ubuntu32 @@ -18,7 +18,7 @@ RUN gem install fpm # Install go ENV GOPATH /root/go -ENV GO_VERSION 1.7.4 +ENV GO_VERSION 1.8.1 ENV GO_ARCH 386 RUN wget https://storage.googleapis.com/golang/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz; \ tar -C /usr/local/ -xf /go${GO_VERSION}.linux-${GO_ARCH}.tar.gz ; \ diff --git a/Dockerfile_build_ubuntu64 b/Dockerfile_build_ubuntu64 index b8ceba34bc1..83a3d908228 100644 --- a/Dockerfile_build_ubuntu64 +++ b/Dockerfile_build_ubuntu64 @@ -21,7 +21,7 @@ RUN gem install fpm # Install go ENV GOPATH /root/go -ENV GO_VERSION 1.7.4 +ENV GO_VERSION 1.8.1 ENV GO_ARCH amd64 RUN wget https://storage.googleapis.com/golang/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz; \ tar -C /usr/local/ -xf /go${GO_VERSION}.linux-${GO_ARCH}.tar.gz ; \ diff --git a/Dockerfile_build_ubuntu64_git b/Dockerfile_build_ubuntu64_git index 61227a34694..0d049ea7af9 100644 --- a/Dockerfile_build_ubuntu64_git +++ b/Dockerfile_build_ubuntu64_git @@ -26,7 +26,7 @@ VOLUME $PROJECT_DIR # Install go -ENV GO_VERSION 1.7.4 +ENV GO_VERSION 1.8.1 ENV GO_ARCH amd64 RUN wget https://storage.googleapis.com/golang/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz; \ tar -C /usr/local/ -xf /go${GO_VERSION}.linux-${GO_ARCH}.tar.gz ; \ diff --git a/build-docker.sh b/build-docker.sh deleted file mode 100755 index 3e45beac776..00000000000 --- a/build-docker.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -e -x - -GO_VER=${GO_VER:-1.7.4} - -docker run -it -v "${GOPATH}":/gopath -v "$(pwd)":/app -e "GOPATH=/gopath" -w /app golang:$GO_VER sh -c 'CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags="-s" -o influxd ./cmd/influxd' - -docker build -t influxdb . diff --git a/circle.yml b/circle.yml index bfa60818142..65267cc0197 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ machine: services: - docker environment: - GODIST: "go1.7.4.linux-amd64.tar.gz" + GODIST: "go1.8.1.linux-amd64.tar.gz" post: - mkdir -p download - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST diff --git a/package.sh b/package.sh deleted file mode 100755 index a4e34878760..00000000000 --- a/package.sh +++ /dev/null @@ -1,571 +0,0 @@ -#!/usr/bin/env bash - -########################################################################### -# Packaging script which creates debian and RPM packages. It optionally -# tags the repo with the given version. -# -# Requirements: GOPATH must be set. 'fpm' must be on the path, and the AWS -# CLI tools must also be installed. -# -# https://github.com/jordansissel/fpm -# http://aws.amazon.com/cli/ -# -# Packaging process: to package a build, simple execute: -# -# package.sh -# -# where is the desired version. If generation of a debian and RPM -# package is successful, the script will offer to tag the repo using the -# supplied version string. -# -# See package.sh -h for options -# -# AWS upload: the script will also offer to upload the packages to S3. If -# this option is selected, the credentials should be present in the file -# ~/aws.conf. The contents should be of the form: -# -# [default] -# aws_access_key_id= -# aws_secret_access_key= -# region = us-east-1 -# -# Trim the leading spaces when creating the file. The script will exit if -# S3 upload is requested, but this file does not exist. - -[ -z $DEBUG ] || set -x - -AWS_FILE=~/aws.conf - -INSTALL_ROOT_DIR=/usr/bin -INFLUXDB_LOG_DIR=/var/log/influxdb -INFLUXDB_DATA_DIR=/var/lib/influxdb -INFLUXDB_SCRIPT_DIR=/usr/lib/influxdb -CONFIG_ROOT_DIR=/etc/influxdb -LOGROTATE_DIR=/etc/logrotate.d - -SAMPLE_CONFIGURATION=etc/config.sample.toml -INITD_SCRIPT=scripts/init.sh -SYSTEMD_SCRIPT=scripts/influxdb.service -POSTINSTALL_SCRIPT=scripts/post-install.sh -PREINSTALL_SCRIPT=scripts/pre-install.sh -POSTUNINSTALL_SCRIPT=scripts/post-uninstall.sh -LOGROTATE=scripts/logrotate - -TMP_WORK_DIR=`mktemp -d` -POST_INSTALL_PATH=`mktemp` -POST_UNINSTALL_PATH=`mktemp` -ARCH=`uname -i` -LICENSE=MIT -URL=influxdb.com -MAINTAINER=support@influxdb.com -VENDOR=Influxdb -DESCRIPTION="Distributed time-series database" - -# Allow path to FPM to be set by environment variables. Some execution contexts -# like cron don't have PATH set correctly to pick it up. -if [ -z "$FPM" ]; then - FPM=`which fpm` -fi - -GO_VERSION="go1.4.3" -GOPATH_INSTALL= -BINS=( - influxd - influx - influx_stress - influx_tsm - influx_inspect - ) - -########################################################################### -# Helper functions. - -# usage prints simple usage information. -usage() { - cat << EOF >&2 -$0 [-h] [-p|-w] [-t ] [-r ] - - should be a dotted version such as 0.9.5. - - -r release candidate number, if any. - Example: -r 7 - -p just build packages - -x build with race-detection enabled - -w build packages for current working directory - imply -p - -t - build package for - can be rpm, tar or deb - can have multiple -t - - Examples: - - $0 0.9.5 -r 9 # Creates 0.9.5-rc9 - $0 0.9.4 # Creates 0.9.4 - -EOF - cleanup_exit $1 -} - -# full_version echoes the full version string, given a version and an optiona;l -# RC number. If the just the version is present, that is echoed. If the RC is -# also provided, then "rc" and the number is concatenated with the version. -# For example, 0.9.4rc4 would be returned if version was 0.9.4 and the RC number -# was 4. -full_version() { - version=$1 - rc=$2 - if [ -z "$rc" ]; then - echo $version - else - echo ${version}-rc${rc} - fi -} - -# rpm_release echoes the RPM release or "iteration" given an RC number. -rpm_release() { - rc=$1 - if [ -z "$rc" ]; then - echo 1 - else - echo 0.1.rc${rc} - fi -} - -# cleanup_exit removes all resources created during the process and exits with -# the supplied returned code. -cleanup_exit() { - rm -r $TMP_WORK_DIR - rm $POST_INSTALL_PATH - rm $POST_UNINSTALL_PATH - exit $1 -} - -# current_branch echos the current git branch. -current_branch() { - echo `git rev-parse --abbrev-ref HEAD` -} - -# check_gopath sanity checks the value of the GOPATH env variable, and determines -# the path where build artifacts are installed. GOPATH may be a colon-delimited -# list of directories. -check_gopath() { - [ -z "$GOPATH" ] && echo "GOPATH is not set." && cleanup_exit 1 - GOPATH_INSTALL=`echo $GOPATH | cut -d ':' -f 1` - [ ! -d "$GOPATH_INSTALL" ] && echo "GOPATH_INSTALL is not a directory." && cleanup_exit 1 - echo "GOPATH ($GOPATH) looks sane, using $GOPATH_INSTALL for installation." -} - -check_gvm() { - if [ -n "$GOPATH" ]; then - existing_gopath=$GOPATH - fi - - source $HOME/.gvm/scripts/gvm - which gvm - if [ $? -ne 0 ]; then - echo "gvm not found -- aborting." - cleanup_exit $1 - fi - gvm use $GO_VERSION - if [ $? -ne 0 ]; then - echo "gvm cannot find Go version $GO_VERSION -- aborting." - cleanup_exit $1 - fi - - # Keep any existing GOPATH set. - if [ -n "$existing_gopath" ]; then - GOPATH=$existing_gopath - fi -} - -# check_clean_tree ensures that no source file is locally modified. -check_clean_tree() { - modified=$(git ls-files --modified | wc -l) - if [ $modified -ne 0 ]; then - echo "The source tree is not clean -- aborting." - cleanup_exit 1 - fi - echo "Git tree is clean." -} - -# update_tree ensures the tree is in-sync with the repo. -update_tree() { - git pull origin $TARGET_BRANCH - if [ $? -ne 0 ]; then - echo "Failed to pull latest code -- aborting." - cleanup_exit 1 - fi - git fetch --tags - if [ $? -ne 0 ]; then - echo "Failed to fetch tags -- aborting." - cleanup_exit 1 - fi - echo "Git tree updated successfully." -} - -# check_tag_exists checks if the existing release already exists in the tags. -check_tag_exists () { - version=$1 - git tag | grep -q "^v$version$" - if [ $? -eq 0 ]; then - echo "Proposed version $version already exists as a tag -- aborting." - cleanup_exit 1 - fi -} - -# make_dir_tree creates the directory structure within the packages. -make_dir_tree() { - work_dir=$1 - version=$2 - - mkdir -p $work_dir/$INSTALL_ROOT_DIR - mkdir -p $work_dir/$INFLUXDB_SCRIPT_DIR/scripts - if [ $? -ne 0 ]; then - echo "Failed to create script directory -- aborting." - cleanup_exit 1 - fi - mkdir -p $work_dir/$CONFIG_ROOT_DIR - if [ $? -ne 0 ]; then - echo "Failed to create configuration directory -- aborting." - cleanup_exit 1 - fi - mkdir -p $work_dir/$LOGROTATE_DIR - if [ $? -ne 0 ]; then - echo "Failed to create logrotate directory -- aborting." - cleanup_exit 1 - fi -} - -# do_build builds the code. The version and commit must be passed in. -do_build() { - for b in ${BINS[*]}; do - rm -f $GOPATH_INSTALL/bin/$b - done - - if [ -n "$WORKING_DIR" ]; then - STASH=`git stash create -a` - if [ $? -ne 0 ]; then - echo "WARNING: failed to stash uncommited local changes" - fi - git reset --hard - fi - - go get -u -f -d ./... - if [ $? -ne 0 ]; then - echo "WARNING: failed to 'go get' packages." - fi - - git checkout $TARGET_BRANCH # go get switches to master, so ensure we're back. - - if [ -n "$WORKING_DIR" ]; then - git stash apply $STASH - if [ $? -ne 0 ]; then #and apply previous uncommited local changes - echo "WARNING: failed to restore uncommited local changes" - fi - fi - - version=$1 - commit=`git rev-parse HEAD` - branch=`current_branch` - if [ $? -ne 0 ]; then - echo "Unable to retrieve current commit -- aborting" - cleanup_exit 1 - fi - - date=`date -u --iso-8601=seconds` - go install $RACE -a -ldflags="-X main.version=$version -X main.branch=$branch -X main.commit=$commit -X main.buildTime=$date" ./... - if [ $? -ne 0 ]; then - echo "Build failed, unable to create package -- aborting" - cleanup_exit 1 - fi - echo "Build completed successfully." -} - -########################################################################### -# Process options -while : -do - case $1 in - -h | --help) - usage 0 - ;; - - -p | --packages-only) - PACKAGES_ONLY="PACKAGES_ONLY" - shift - ;; - - -t | --target) - case "$2" in - 'tar') TAR_WANTED="gz" - ;; - 'deb') DEB_WANTED="deb" - ;; - 'rpm') RPM_WANTED="rpm" - ;; - *) - echo "Unknown target distribution $2" - usage 1 - ;; - esac - shift 2 - ;; - - -r) - RC=$2 - if [ -z "$RC" ]; then - echo "RC number required" - fi - shift 2 - ;; - - -x) - RACE="-race" - shift - ;; - - -w | --working-directory) - PACKAGES_ONLY="PACKAGES_ONLY" - WORKING_DIR="WORKING_DIR" - shift - ;; - - -*) - echo "Unknown option $1" - usage 1 - ;; - - ?*) - if [ -z $VERSION ]; then - VERSION=$1 - shift - else - echo "$1 : aborting version already set to $VERSION" - usage 1 - fi - - - echo $VERSION | grep -i '[r|rc]' 2>&1 >/dev/null - if [ $? -ne 1 -a -z "$NIGHTLY_BUILD" ]; then - echo - echo "$VERSION contains reference to RC - specify RC separately" - echo - usage 1 - fi - ;; - - *) break - esac -done - -if [ -z "$DEB_WANTED$RPM_WANTED$TAR_WANTED" ]; then - TAR_WANTED="gz" - DEB_WANTED="deb" - RPM_WANTED="rpm" -fi - -if [ -z "$VERSION" ]; then - echo -e "Missing version" - usage 1 -fi - -########################################################################### -# Start the packaging process. - -echo -e "\nStarting package process...\n" - -# Ensure the current is correct. -TARGET_BRANCH=`current_branch` -if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then -echo -n "Current branch is $TARGET_BRANCH. Start packaging this branch? [Y/n] " - read response - response=`echo $response | tr 'A-Z' 'a-z'` - if [ "x$response" == "xn" ]; then - echo "Packaging aborted." - cleanup_exit 1 - fi -fi - -check_gvm -check_gopath -if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then - check_clean_tree - update_tree - check_tag_exists `full_version $VERSION $RC` -fi - -do_build `full_version $VERSION $RC` -make_dir_tree $TMP_WORK_DIR `full_version $VERSION $RC` - -########################################################################### -# Copy the assets to the installation directories. - -for b in ${BINS[*]}; do - cp $GOPATH_INSTALL/bin/$b $TMP_WORK_DIR/$INSTALL_ROOT_DIR/ - if [ $? -ne 0 ]; then - echo "Failed to copy binaries to packaging directory ($TMP_WORK_DIR/$INSTALL_ROOT_DIR/) -- aborting." - cleanup_exit 1 - fi -done -echo "${BINS[*]} copied to $TMP_WORK_DIR/$INSTALL_ROOT_DIR/" - -cp $INITD_SCRIPT $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR/$INITD_SCRIPT -if [ $? -ne 0 ]; then - echo "Failed to copy init.d script to packaging directory ($TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR/) -- aborting." - cleanup_exit 1 -fi -echo "$INITD_SCRIPT copied to $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR" - -cp $SYSTEMD_SCRIPT $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR/$SYSTEMD_SCRIPT -if [ $? -ne 0 ]; then - echo "Failed to copy systemd script to packaging directory -- aborting." - cleanup_exit 1 -fi -echo "$SYSTEMD_SCRIPT copied to $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR" - -cp $SAMPLE_CONFIGURATION $TMP_WORK_DIR/$CONFIG_ROOT_DIR/influxdb.conf -if [ $? -ne 0 ]; then - echo "Failed to copy $SAMPLE_CONFIGURATION to packaging directory -- aborting." - cleanup_exit 1 -fi - -install -m 644 $LOGROTATE $TMP_WORK_DIR/$LOGROTATE_DIR/influxdb -if [ $? -ne 0 ]; then - echo "Failed to copy logrotate configuration to packaging directory -- aborting." - cleanup_exit 1 -fi - -########################################################################### -# Create the actual packages. - -if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then - echo -n "Commence creation of $ARCH packages, version `full_version $VERSION $RC`? [Y/n] " - read response - response=`echo $response | tr 'A-Z' 'a-z'` - if [ "x$response" == "xn" ]; then - echo "Packaging aborted." - cleanup_exit 1 - fi -fi - -COMMON_FPM_ARGS="\ ---log error \ --C $TMP_WORK_DIR \ ---vendor $VENDOR \ ---url $URL \ ---license $LICENSE \ ---maintainer $MAINTAINER \ ---after-install $POSTINSTALL_SCRIPT \ ---before-install $PREINSTALL_SCRIPT \ ---after-remove $POSTUNINSTALL_SCRIPT \ ---name influxdb${RACE} \ ---config-files $CONFIG_ROOT_DIR \ ---config-files $LOGROTATE_DIR" - -if [ -n "$DEB_WANTED" ]; then - $FPM -s dir -t deb $deb_args --description "$DESCRIPTION" $COMMON_FPM_ARGS --version `full_version $VERSION $RC` . - if [ $? -ne 0 ]; then - echo "Failed to create Debian package -- aborting." - cleanup_exit 1 - fi - echo "Debian package created successfully." -fi - -if [ -n "$TAR_WANTED" ]; then - if [ -n "$RACE" ]; then - # Tweak race prefix for tarball. - race="race_" - fi - $FPM -s dir -t tar --prefix influxdb_$race`full_version $VERSION $RC`_${ARCH} -p influxdb_$race`full_version $VERSION $RC`_${ARCH}.tar.gz --description "$DESCRIPTION" $COMMON_FPM_ARGS --version `full_version $VERSION $RC ` . - if [ $? -ne 0 ]; then - echo "Failed to create Tar package -- aborting." - cleanup_exit 1 - fi - echo "Tar package created successfully." -fi - -if [ -n "$RPM_WANTED" ]; then - $rpm_args $FPM -s dir -t rpm --description "$DESCRIPTION" $COMMON_FPM_ARGS --depends coreutils --version $VERSION --iteration `rpm_release $RC` . - if [ $? -ne 0 ]; then - echo "Failed to create RPM package -- aborting." - cleanup_exit 1 - fi - echo "RPM package created successfully." -fi - -########################################################################### -# Offer to tag the repo. - -if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then - echo -n "Tag source tree with v`full_version $VERSION $RC` and push to repo? [y/N] " - read response - response=`echo $response | tr 'A-Z' 'a-z'` - if [ "x$response" == "xy" ]; then - echo "Creating tag v`full_version $VERSION $RC` and pushing to repo" - git tag v`full_version $VERSION $RC` - if [ $? -ne 0 ]; then - echo "Failed to create tag v`full_version $VERSION $RC` -- aborting" - cleanup_exit 1 - fi - echo "Tag v`full_version $VERSION $RC` created" - git push origin v`full_version $VERSION $RC` - if [ $? -ne 0 ]; then - echo "Failed to push tag v`full_version $VERSION $RC` to repo -- aborting" - cleanup_exit 1 - fi - echo "Tag v`full_version $VERSION $RC` pushed to repo" - else - echo "Not creating tag v`full_version $VERSION $RC`." - fi -fi - -########################################################################### -# Offer to publish the packages. - -if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then - echo -n "Publish packages to S3? [y/N] " - read response - response=`echo $response | tr 'A-Z' 'a-z'` -fi - -if [ "x$response" == "xy" -o -n "$NIGHTLY_BUILD" ]; then - echo "Publishing packages to S3." - if [ ! -e "$AWS_FILE" ]; then - echo "$AWS_FILE does not exist -- aborting." - cleanup_exit 1 - fi - - for filepath in `ls *.{$DEB_WANTED,$RPM_WANTED,$TAR_WANTED} 2> /dev/null`; do - filename=`basename $filepath` - sum=`md5sum $filename | cut -d ' ' -f 1` - - if [ -n "$NIGHTLY_BUILD" ]; then - # Replace the version string in the filename with "nightly". - v=`full_version $VERSION $RC` - v_underscored=`echo "$v" | tr - _` - v_rpm=$VERSION-`rpm_release $RC` - - # It's ok to run each of these since only 1 will match, leaving - # filename untouched otherwise. - filename=`echo $filename | sed s/$v/nightly/` - filename=`echo $filename | sed s/$v_underscored/nightly/` - filename=`echo $filename | sed s/$v_rpm/nightly-1/` - fi - - AWS_CONFIG_FILE=$AWS_FILE aws s3 cp $filepath s3://influxdb/$filename --acl public-read --region us-east-1 - if [ $? -ne 0 ]; then - echo "Upload failed ($filename) -- aborting". - cleanup_exit 1 - fi - echo "$filename uploaded, MD5 checksum is $sum" - done -else - echo "Not publishing packages to S3." -fi - -########################################################################### -# All done. - -echo -e "\nPackaging process complete." -cleanup_exit 0