Dataloader Docker環境でのCSVファイルダウンロード

# はじめに
一つ前の[記事](https://qiita.com/okazakiyuji/items/b26fad6ac6ad6f2d4b43)で作成したDataloaderのDcokerコンテナを使用して、SalesforceのデータをCSVでダウンロードします。データローダを使ってバッチでのCSVファイルのダウンロードは他に良い記事を探していただくとして、ここでは前の記事のDockerコンテナを使うサンプルとして説明します。

#前提条件
* Dataloaderの基本的な使用方法は理解していること
 <br>参照:[データローダガイド](https://developer.salesforce.com/docs/atlas.ja-jp.dataLoader.meta/dataLoader/data_loader.htm)
* Docker環境のネットの設定を理解していること
 <br>Proxyのを介してSalesforceと通信する場合は追加の設定が必要です。
 <br>参照:[Proxy環境下のDockerトラブルシューティング](https://qiita.com/tora470/items/cd9335f5ce638e11bb97)
* GUI版のDataloaderを使用して、OAuthではなくパスワード認証で接続できることを確認
* Salesforceの設定の影響
 <br>接続環境をIPアドレスで制限していない場合は、セキュリティトークンの取得(リセット)が必要になります。取得したセキュリティトークンはパスワードの後に追加してスクリプトの実行時にパスワードとして指定してください。
 <br>参照:[ログイン履歴にAPI セキュリティトークンが必要ですの表示が出る場合の対処方法](https://help.salesforce.com/articleView?id=000182138&language=ja&type=1)

# ファイル
作成する場所を~/CSVDownloadとしたときにその下にbin,conf,dataフォルダを作成し、次のファイルを用意してください。[bitbucketのリポジトリ](https://bitbucket.org/okazakiyuji/dataloader-samples/src/default/CSVDownload/)からも取得できます

* ~/CSVDownload/Dockerfile
* ~/CSVDownload/bin/create_config.sh
* ~/CSVDownload/bin/extract_csv.sh
* ~/CSVDownload/conf/accountExtractMap.sdl
* ~/CSVDownload/conf/process-conf.xml
<br>フォルダ階層がうまく書けない(ioi) 

```Dockerfile
FROM okazakiyuji/dataloader
MAINTAINER okazakiyuji
RUN localedef -f UTF-8 -i ja_JP /usr/lib/locale/ja_JP.UTF-8
ENV LANG="ja_JP.UTF-8" \
    LANGUAGE="ja_JP:ja" \
    LC_ALL="ja_JP.UTF-8" \
    TZ="Asia/Tokyo"
RUN yum -y install expect
WORKDIR $DL_ROOT
#
ARG SITE=$SITE
ARG USRNAME=$USRNAME
ARG PASSWD=$PASSWD
COPY conf ./conf
COPY bin ./bin
RUN chmod a+x ./bin/create_config.sh
RUN chmod a+x ./bin/extract_csv.sh
RUN create_config.sh $SITE $USRNAME $PASSWD
# RUN cat conf/config.properties
CMD ["extract_csv.sh"]
```
Dockerfileの概要は次のようになっています
* ベースのイメージに一つ前の[記事](https://qiita.com/okazakiyuji/items/b26fad6ac6ad6f2d4b43)で作成したDataloaderコンテナを使用
* ロケール関連の設定
* 必要なファイルのインストール
* ローカルのファイルをコンテナにコピー
* create_config.shでエンコード済パスワードやプロキシ設定をconfig.propertiesファイルに設定し
* 実行時にextract_csv.shを実行する

```accountExtractMap.sdl
id=id
ISDELETED=ISDELETED
NAME=NAME
TYPE=TYPE
PARENTID=PARENTID
OWNERID=OWNERID
CREATEDDATE=CREATEDDATE
CREATEDBYID=CREATEDBYID
LASTMODIFIEDDATE=LASTMODIFIEDDATE
LASTMODIFIEDBYID=LASTMODIFIEDBYID
ISPARTNER=ISPARTNER
ISCUSTOMERPORTAL=ISCUSTOMERPORTAL
```
皆さんご存知のSDLファイルですね。GUIありのデータローダで作ってしまってそのファイルで置き換えても大丈夫です。

```process-conf.xml
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="csvAccountExtractProcess"
          class="com.salesforce.dataloader.process.ProcessRunner"
          singleton="false">
      <description>csvAccountExtract job gets Account info and saves info into a CSV file."</description>
        <property name="name" value="csvAccountExtract"/>
        <property name="configOverrideMap">
            <map>
                <entry key="sfdc.entity" value="Account"/>
                <entry key="sfdc.extractionSOQL" value="SELECT id, ISDELETED, NAME, TYPE, PARENTID, OWNERID, CREATEDDATE, CREATEDBYID, LASTMODIFIEDDATE, LASTMODIFIEDBYID, ISPARTNER, ISCUSTOMERPORTAL FROM Account"/>
                <entry key="process.operation" value="extract"/>
                <entry key="process.mappingFile" value="/opt/dataloader/conf/accountExtractMap.sdl"/>
                <entry key="dataAccess.type" value="csvWrite"/>
                <entry key="dataAccess.name" value="/opt/dataloader/data/accoutExtract.csv"/>
            </map>
        </property>
    </bean>
</beans>
```
Accountダウンロードのbeanだけが書いてあります。他のオブジェクトをダウンロードしたい場合はAccountを文字列置換し、SDLファイルとSOQLを作り直してください.SOQLはAccountのものをまねて、SDLファイルにある項目をずら～と並べたり、GUIありのデータローダから持ってくればOkです。

```create_config.sh
#!/bin/bash
cd "`dirname "$0"`"
CONF_ROOT=$DL_ROOT/conf
KEY_FILE=$DL_ROOT/conf/key.txt
if [ x$DL_ROOT = x ] ; then
    echo データローダのインストールルートを DL_ROOTに設定してください
    exit 1
fi
if [ ! x$3 = x ] ; then
    # 引数に何か設定されていたら環境変数を上書きする
    SITE=$1
    USRNAME=$2
    PASSWD=$3
fi
if [ x$SITE = x -o x$USRNAME = x -o x$PASSWD = x ] ; then
    echo "usage"
    echo "    arg1:site name (ex. login.salesforce.com, test.salesforce.com)"
    echo "    arg2:username"
    echo "    arg3:password"
    exit 1
fi
if [ ! -d $CONF_ROOT ] ; then
    echo $CONF_ROOT を作成してください
    exit 1
fi
if [ -f $KEY_FILE ] ; then
    echo $KEY_FILE を削除します
    rm $KEY_FILE
fi
# 暗号化済パスワードを作成する
java -cp $DATALOADER_CLASSPATH com.salesforce.dataloader.security.EncryptionUtil -e $PASSWD $KEY_FILE > /dev/null
if [ $? = 0 ] ; then
    # V42.1.0では-e オプション一発で作成できたが
    ENC_PW=`java -cp $DATALOADER_CLASSPATH com.salesforce.dataloader.security.EncryptionUtil -e $PASSWD $KEY_FILE | sed -n 2P`
else
    KEYWD=`mkpasswd -s 0 -d 0 -l 16`
    java -cp $DATALOADER_CLASSPATH com.salesforce.dataloader.security.EncryptionUtil -g $KEYWD | sed -e "s/.* - //1" > $KEY_FILE
    ENC_PW=`java -cp $DATALOADER_CLASSPATH com.salesforce.dataloader.security.EncryptionUtil -e $PASSWD $KEY_FILE | sed -e "s/.* - //1"`
fi
echo "# config.properties" > $CONF_ROOT/config.properties
echo process.encryptionKeyFile=$KEY_FILE >> $CONF_ROOT/config.properties
echo sfdc.endpoint=https://$SITE >> $CONF_ROOT/config.properties
echo sfdc.username=$USRNAME >> $CONF_ROOT/config.properties
echo sfdc.password=$ENC_PW >> $CONF_ROOT/config.properties
echo sfdc.timeoutSecs=600 >> $CONF_ROOT/config.properties
echo sfdc.loadBatchSize=200 >> $CONF_ROOT/config.properties
echo sfdc.extractionRequestSize=500 >> $CONF_ROOT/config.properties
echo sfdc.debugMessages=false >> $CONF_ROOT/config.properties
echo sfdc.debugMessagesFile=/opt/dataloader/status/debug.log >> $CONF_ROOT/config.properties
# Proxy設定の出力　（動作確認未）
if [ x$https_proxy != x ]; then
    PROXY_STR=${https_proxy/*:\/\//}
    WORK=${PROXY_STR%@*}
    if [ x$WORK != x$PROXY_STR ]; then
        echo sfdc.proxyUsername=${WORK%:*} >> $CONF_ROOT/config.properties
        echo sfdc.proxyPassword=${WORK#*:} >> $CONF_ROOT/config.properties
        WORK=${PROXY_STR#*@}
    fi
    echo sfdc.proxyHost=${WORK%:*} >> $CONF_ROOT/config.properties
    echo sfdc.proxyPort=${WORK#*:} >> $CONF_ROOT/config.properties
fi
```
暗号化したパスワードやproxyを引数や環境変数から読み取りconfig.propertiesに設定します。

```extract_csv.sh
#!/bin/bash
cd "`dirname "$0"`"
if [ x$DL_ROOT = x ] ; then
    DL_ROOT=$DL_BIN_DIR/..
fi
CONF_DIR=$DL_ROOT/conf
function dl_process () {
    # Dataloader のプロセスを実行する
    echo process: $1
    java -cp $DATALOADER_CLASSPATH -Dsalesforce.config.dir=$CONF_DIR \
        com.salesforce.dataloader.process.ProcessRunner process.name=$1 # > /dev/null
}

dl_process csvAccountExtractProcess
```
CSVダウンロードを実行するコマンドです。単純にプロセスを読んでいるだけです。

# 確認方法

## ビルドする
コンテナをビルドします。SITEは本番環境ならlogin.salesforce.com, sandboxではtest.salesforce.comを指定してください。他はデータローダのGUIあり版で指定しているものを指定してください。proxyあり環境の場合は--build-argでhttp_proxyとhttps_proxyを指定するとかしてください。https_proxyを指定しておくと、確認はできていませんが、config.propertiesにProxy関連の必要な情報が出力されます。

```
$ cd ~/CSVDownload
$ sudo docker build . --tag dlcsv --build-arg SITE=login.salesforce.com --build-arg USRNAME=～～@～～.～～～.com --build-arg PASSWD=～～～～～？？？？
```

(2018/06/25追加)config.propertiesの例です。このように主に接続に必要な項目が出力されるのでprocess.xmlに足りない値を記述してください。

```/opt/dataloader/conf/config.properties
# config.properties
process.encryptionKeyFile=/opt/dataloader/conf/key.txt
sfdc.endpoint=https://*****.salesforce.com
sfdc.username=～～@～～.～～～.com
sfdc.password=************************************************************************************
sfdc.timeoutSecs=600
sfdc.loadBatchSize=200
sfdc.extractionRequestSize=500
sfdc.debugMessages=false
sfdc.debugMessagesFile=/opt/dataloader/status/debug.log
```

## 実行する

実行例です。ローカルフォルダ$PWD/dataをコンテナにマウントし実行します。エラーが発生しなければ、ダウンロードしたファイルがdataフォルダの下に作成されます
```
$ mkdir -p $PWD/data
$ sudo docker run -v$PWD/data:/opt/dataloader/data -t dlcsv
$ cat data/accoutExtract.csv
```