GitHubを使用した分散サーバー管理

GitHubリポジトリからファイルを更新するためのサンプルPythonコード

C05348A3-9AB8-42C9-A6E0-81DB3AC59FEB
           

このスクリプトを使用して、多数のサーバーで実行されていた一連の管理スクリプトを更新しました。 「gitpull」でスクリプトをチェックアウトすることもできましたが、どのスクリプトをどのサーバーにインストールするかをさらに制御したかったので、それを行うためのPythonスクリプトを作成しました。

これを分散型または分散型のアプローチと呼びます。Salt、Chef、Puppetマスターサーバーなどの中央機関がすべてのサーバーに新しい管理スクリプトをプッシュするのとは異なり、サーバーはGitHubから更新されたスクリプトをプルできるからです。

スクリプトはPyGitHubライブラリを使用するので、Yamlライブラリと同様にそれをインストールすることから始めましょう。

pip3はPyGithubをインストールします
pip3インストールpyaml

GitHubからAPIトークンを取得する

次のスクリプトを使用するには、最初にGitHubからAPIトークンを取得する必要があります。 GitHubアカウントで、[設定]、[開発者設定]に移動し、[個人アクセストークン]をクリックして新しいトークンを作成します。今のところ、追加の許可は必要ありません。

特定のリポジトリだけでなく、アカウントへのアクセスを許可するため、その個人用アクセストークンを確実に保護する必要があります。このために、 RSA-cryptoライブラリを使用して暗号化することにしました。次のコードは、ライブラリをインストールし、新しいRSAキーセットを作成し、構成ファイル内の個人用アクセストークンをghe_tokenという名前のオプションの値として暗号化します。そのライブラリと関連するコマンドラインツールの使用の詳細については、公開/秘密鍵RSA暗号化Python暗号化を参照してください。

pip3 installrsa-crypto

 rsa_crypto create
キーパスワードを入力してください:
キーパスワードを再入力します。
キーを作成しています...
パスワードで保護された秘密/公開鍵ファイル/Users/me/rsa_key.binを作成しました
「extract」キーワードを使用して、公開鍵ファイルと秘密鍵ファイルを作成します。

 〜/.rsa_values.confをタッチします

rsa_crypto set -o ghe_token
キーの使用:/Users/me/rsa_key.bin
暗号化されたキー/Users/me/rsa_key.binを開く
キーパスワードを入力してください:
値を入力してください:
セクションの設定:option:ghe_token
 /Users/me/.rsa_values.confを更新しました

次に、Pythonスクリプトは、 ghe_token構成の「オプション」を読み取って復号化し、個人用アクセストークンを取得して使用するように構成されます。これが複雑すぎると思われる場合は、別の解決策を見つけてください。ただし、スクリプトに直接アクセストークンを埋め込まないでください。GitHubに表示され、誰でも使用できるようになる可能性が高くなります。

Pythonスクリプト

このスクリプトを使用する前に、いくつかのパラメーターを変更し、 ghe_repoを変更して独自のリポジトリーを指すようにし、 local_base_pathを変更して、サーバー管理スクリプトを配置するシステム上のローカルディレクトリを指すようにする必要があります。

このスクリプトは、パブリックGitHubを使用していることを前提としていますが、組織内のプライベートGitHub Enterpriseリポジトリを使用している場合に必要なコードも含まれており、コメントアウトされています。

このスクリプトはGitHubリポジトリにあります。

スクリプトは最初に構成ファイルupdate_scripts.yamlを取得します。このファイルには、次の3つのセクションが含まれています。

  • update_always-常にGitHubからプルする必要があるファイルのリストが含まれています
  • update_if_present-存在する場合にのみGitHubから更新する必要があるファイルのリストが含まれています
  • remove-ローカルサーバーから削除する必要があるファイルのリストが含まれています

このファイルを使用すると、すべてのサーバーに展開するスクリプトを一元的に制御できます。新しいスクリプトをインストールする必要がある場合は、それをGitHubにプッシュして構成ファイルにエントリを追加すると、このスクリプトの実行時にサーバーが新しいスクリプトをダウンロードします。また、 touchコマンドを使用してファイルを作成し、この更新スクリプトを実行するだけで、システムを「だまして」 update_if_presentセクションからスクリプトをダウンロードすることもできます。スクリプトがリストに含まれている場合は、スクリプト自体も更新されます。

ファイルの3つのセクションすべてが処理され、 update_alwaysセクションとupdate_if_presentセクションのファイルはGitHubハッシュでのみ更新され、ローカルハッシュとは異なり、プルがより効率的になります。このスクリプトは、すべてのPythonスクリプトを現在のユーザーに対して実行可能にします。

#!/ usr / bin / env python
 #-*-コーディング:utf-8-*-
 from __future__ import(absolute_import、division、
 print_function、unicode_literals)

 __author__ = "Videre Research、LLC"
 __version__ = "1.0.2"


 '''

サーバー管理スクリプトおよびその他のサポートスクリプトをインストールして構成します。

 '''


 #インポート################################################ #################

 OSのインポート
sysをインポート
インポートログ
トレースバックをインポート
base64をインポート
githubをインポートする
yamlをインポートする
hashlibをインポートします
rsa_cryptoをインポートします
argparseをインポートします

if(sys.version_info>(3、0)):
 urllib.parseからインポートurljoin
そうしないと:
 urlparseからインポートurljoin


 #グローバル################################################ ###############

 ##独自の内部GitHubリポジトリを使用している場合はコメントを解除します
#ghe_organization ='my_ghe_repo'
 #ghe_hostname = mydomain.com

 ghe_repo ='Christophe-Gauge / GitHub'
 remote_base_path ='/'
 local_base_path ='/ opt / scripts'
 remote_config_file ='update_scripts.yaml'
 ghe_branch='メイン'

ロガー=logging.getLogger()
 logging.basicConfig(level = logging.INFO)
 logger.info( "パス:%s"%(os.path.realpath(__ file__)))
 logger.info( "バージョン:%s"%(__ version__))

 args = argparse.Namespace(option ='ghe_token')
 ghe_token = rsa_crypto.decrypt_value(args)


 #コード################################################ #####################

 def get_ghe(remote_file、repo、always_update):
 "" "GHEからファイルを取得し、ローカルに保存します" ""
 my_file_name = os.path.basename(remote_file)
 remote_file_name = urljoin(remote_base_path、remote_file)
 local_file_name = os.path.join(local_base_path、my_file_name)
 logger.info( "リモートGHEファイル%s%sを%sに取得しています"%(repo.full_name、remote_file_name、local_file_name))

試す:
 remoteSHA = repo.get_contents(remote_file_name、ref = ghe_branch).sha
 eとしてのgithub.UnknownObjectExceptionを除く:
 logger.error(f "リモートファイルが見つかりません{remote_file_name}")
戻る
eとしての例外を除く:
 logger.error( "Error {0}"。format(str(e)))
 logger.error(traceback.format_exc())
戻る

#ファイルが存在する場合は、ハッシュを取得して更新が必要かどうかを確認しましょう
os.path.exists(local_file_name)の場合:
 #ローカルファイルのSHA1ハッシュを計算します
open(local_file_name、'rb')をfile_for_hashとして使用:
 data = file_for_hash.read()
 filesize = len(data)
 content = "blob" + str(filesize)+ "\ 0" + data.decode('utf-8')
 encode_content = content.encode('utf-8')
 localSHA = hashlib.sha1(encoded_content).hexdigest()
 remoteSHA == localSHAの場合:
 logger.info('ファイルは存在します。ハッシュは同じです。すでに最新のファイルがあり、更新されていません。')
戻る
そうしないと:
 logger.info('ファイルが存在し、ハッシュが異なります%s-%s'%(remoteSHA、localSHA))
そうしないと:
 #このフラグは、ファイルがすでに存在する場合にのみ更新する必要があることを示します
常にではない場合_update:
 logger.info('ファイルが存在せず、更新されていません')
戻る
試す:
 file_contents = repo.get_contents(remote_file_name、ref = ghe_branch)
 local_file_content = str(base64.b64decode(file_contents.content).decode('utf-8'、'ignore'))

 #新しいファイルをディスクに書き込む
open(local_file_name、 "w")をtext_fileとして使用:
 text_file.write(local_file_content)
 my_file_name.endswith('。py')の場合:
 os.chmod(local_file_name、0o700)
そうしないと:
 os.chmod(local_file_name、0o400)
 logger.info('ファイルが更新されました')

 eとしての例外を除く:
 logger.error( "Error {0}"。format(str(e)))
 logger.error(traceback.format_exc())

 def main():
 """主な機能。"""

 gh = github.Github(login_or_token = ghe_token)
 repo = gh.get_repo(ghe_repo)

 ##独自の内部GitHubリポジトリを使用している場合はコメントを解除します
#gh = github.Github(base_url = f "https:// {ghe_hostname} / api / v3"、login_or_token = ghe_token)
 #org = gh.get_organization(ghe_organization)
 #repo = org.get_repo(ghe_repo)

 os.path.exists(local_base_path)でない場合:
 os.makedirs(local_base_path)

 remote_file_name = urljoin(remote_base_path、remote_config_file)
 logger.info( "リモートGHEファイル%s%sを取得しています"%(repo.full_name、remote_file_name))

試す:
 file_contents = repo.get_contents(remote_file_name、ref = ghe_branch)
 text_contents = str(base64.b64decode(file_contents.content).decode('utf-8'、'ignore'))
 file_list = yaml.load(text_contents、Loader = yaml.SafeLoader)
 logger.info(yaml.safe_dump(file_list、default_flow_style = False))
 eとしての例外を除く:
 e.args [0] == 404の場合:
 logger.error(f "リモートファイルが見つかりません{remote_file_name}")
 sys.exit(1)
そうしないと:
 logger.error( "Error {0}"。format(str(e)))
 logger.error(traceback.format_exc())
 sys.exit(1)

 file_list ['update_always']内のファイルの場合:
 logger.info(ファイル)
 get_ghe(file、repo、True)

 file_list ['update_if_present']内のファイルの場合:
 logger.info(ファイル)
 get_ghe(file、repo、False)

 file_list ['remove']内のファイルの場合:
 os.path.exists(file)の場合:
 os.remove(file)
 logger.info('ファイル%sが削除されました'%file)

 sys.exit(0)

 #################################################### #############################


 __name__ == "__main__"の場合:
主要()

 #ENDOFFILE ################################################ ##############
投稿コメント 0

Tagged with:
GitHub