【Python】toxで複数のPythonバージョンをテストする

f:id:tm200:20220221223803p:plain

Pythonのテストコードを実行する際に、複数のPythonバージョンで動作確認したい時があり、toxの設定が便利だったので備忘メモ。

インストール

$ pip3 install tox

tox.ini

設定ファイルにlinttestの設定を記載できます。
波括弧で複数バージョンの指定が可能です。
また、lintのルールをflake8の引数で変更可能です。

[tox]
envlist = py3{7,8,9}-{lint,test},coverage-report

[testenv]
setenv =
    COVERAGE_FILE = .coverage.{envname}
changedir = {toxinidir}/tests
install_command = \
    pip install \
    --index-url=https://xxx.xxx.xxx:8000/xxx \
    --trusted-host=xxx.xxx.xxx {opts} {packages}

deps = 
    pytest
    pytest-cov
    coverage
    mock

commands =
    pytest --cov . {posargs}

[lint]
changedir = {toxinidir}
skip_install = True
deps =
    readme_renderer
    flake8
    flake8-import-order
    pylint
    mock
    pytest

commands =
    flake8 --import-order-style=pep8 --max-complexity=11 --max-line-length=120 <モジュールのディレクトリ名> (setup.py, testsなども指定可能)

[testenv:py37-lint]
skip_install = {[lint]skip_install}
changedir = {[lint]changedir}
deps = {[lint]deps}
commands = {[lint]commands}

[testenv:py38-lint]
...py37と同様

[testenv:py39-lint]
...py37と同様

実行方法

PRなどをトリガーに、CICDのパイプラインで各Pythonバージョンのコンテナを起動して、テストを自動実行するようにするとGood😎

#lint
$ tox -e py38-lint

# test
$ tox -e py38-test

【Hive】beeline実行時のオプションについて

f:id:tm200:20211209234112j:plain

beelineでHQLを実行する際のオプション指定方法について記載します。


coin-look.pages.dev


--hiveconf

Hiveの設置をオプションで指定可能です。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' --hiveconf hive.execution.engine=tez --hiveconf tez.queue.name=unfunded

-i <設定ファイル>

指定したいオプションが複数ある場合、--hiveconfではなく-i <設定ファイルのパス>などのように別ファイルにまとめることが可能です。
セッションOPEN時の初期化処理に適用されるようです。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -i hiverc
hiverc
set hive.execution.engine=tez;
set tez.queue.name=unfunded;
set hive.exec.scratchdir=/user/xxx/tmp
.....

-f <HQLファイル>

HQLファイルを実行したい場合、-fで指定します。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -f /path/to/test.hql

--hivevar key1=value1

--hivevarで変数展開が可能です。
複数ある場合は--hivevar key1=value1 --hivevar key2=value2 ...のように続けて記載します。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -f /path/to/test.hql --hivevar yyyymmdd=20211101

バックグラウンド実行

HADOOP_CLIENT_OPTSを設定する必要があります。

$ export HADOOP_CLIENT_OPTS="-Djline.terminal=jline.UnsupportedTerminal"

【React.js】makeStylesのスタイル指定方法

f:id:tm200:20211207233515p:plain

React.js(Next.js)で実装する際にmakeStylesの利用方法を調べたので、忘れないようにメモ。
気になるものがあったら、順次追記していきます。


coin-look.pages.dev


first-child

特に迷うことは無い。

const useStyles = makeStyles({
  root: {
    '& div:first-child': {
       ....
    },
});

last-child

first-childと同様。

const useStyles = makeStyles({
  root: {
    '& div:last-child': {
       ....
    },
});

nth-child

スタイルを指定したい要素の中に&:nth-child(n)という形式で定義する。

const useStyles = makeStyles({
  root: {
    '&:nth-child(2)': {
       ....
    },
});

not

&:not(:~)という形で指定可能。  

const useStyles = makeStyles({
  root: {
    '&:not(:nth-child(2))': {
       ....
    },
});

メディアクエリ

@media (min-width:1280px)のような形で指定可能。  

const useStyles = makeStyles({
  root: {
    display: 'flex',
    '@media (max-width:1024px)': {
      display: 'block',
      ....
    },
});

【HiveQL】pyhiveとPrestoでテーブル操作

f:id:tm200:20210704143739p:plain

beelineで実行していたクエリをpyhive + Prestoで再実装した際に書き方が違う部分があった為、備忘メモ。  


coin-look.pages.dev


 LOCATIONを指定してテーブル作成

元のクエリはこちら。

CREATE EXTERNAL TABLE <テーブル名> (<カラム名> <型>, ...) ROW FORMAT DELIMITED FIELDS TERMINATED BY <区切文字、eg. '\t'> LOCATION <HDFSのパス>

pyhive + Prestoで書くと以下のようになる。

cursor = presto.connect(...).cursor()
query = "CREATE TABLE test_table (id integer, note varchar) WITH (format='TEXTFILE', textfile_field_separator='\t', external_location='/path/to/hdfs')"
cursor.execute(query)

# fetchしないとテーブル作成後の処理が上手くいかない、エラーログも見れる
print(cursor.fetchone())

cursor.execute('desc test_table')
print(cursor.fetchone())

INSERT OVERWRITE

元のクエリ。

INSERT OVERWRITE <テーブル名> SELECT sum_a, b FROM (
  SELECT SUM(a) AS sum_a, b FROM (
    SELECT a, b FROM table1 UNION ALL
    SELECT a, b FROM table2 UNION ALL
    SELECT a, b FROM table3
  ) t1 GROUP BY b
) t2 WHERE sum_a > {hivevar:a_param}

PrestoINSERT OVERWRITEは使用できません。
drop後にINSERT INTOで作成する必要があります。

docs.treasuredata.com

cursor = presto.connect(...).cursor()

# drop
cursor.execute('DROP TABLE IF EXISTS test_table')

query = 'INSERT INTO test_table (sum_a, b) WITH tmp_tb AS (
  SELECT SUM(a) AS sum_a, b FROM (
    SELECT a, b FROM table1 UNION ALL
    SELECT a, b FROM table2 UNION ALL
    SELECT a, b FROM table3
  ) GROUP BY b)
  SELECT sum_a, b FROM tmp_tb WHERE  sum_a > %s'

cursor.execute(query, (100,))
print(cursor.fetchall())

【Node.js】npmパッケージの脆弱性対応

f:id:tm200:20210930200410p:plain

CIで脆弱性チェックがエラーとなり、npm audit以外の方法で応急処置をしたのでメモ。  
修正後は要動作確認。  


coin-look.pages.dev


 npm audit

まずは npm audit 等で検出されたパッケージのレポジトリを調べ、必要であれば package.json のバージョンを修正後にnpm audit fixを実行。
node_modules や package-lock.jsonは削除した方が間違いが無さそう。

$ npm audit fix

npm-force-resolutions

github.com

npm audit fixで解決しなかった場合に、どうにかして脆弱性のあるライブラリのバージョンを修正したい場合にnpm-force-resolutionsが使用できます。
package.jsonのscripts.preinstallにnpx npm-force-resolutionsresolutionsにバージョンを修正したいパッケージ情報を記載します。
依存関係を無理やり変更させる為、使用する場合はバージョンアップ後の動作確認を良く行ってください。

{
  "name": "xxxxx",
  "version": "0.0.1",
  "description": "",
  "scripts": {
    "preinstall": "npx npm-force-resolutions",     # ここ
    "build": "..."
  },
  "dependencies": {
    "vue": "^2.6.14",
    ...
  },
  "dev-dependencies": {
    "vue-jest": "^4.0.1",
    ...
  },
  "resolutions": {
     "ansi-regex": "5.0.1"     # ここ
  }
}

Dockerfileからbuildする際にエラーとなった為、.npmrcにunsafe-perm=trueを追記して解決しました。
unsafe-perm=trueで、パッケージスクリプト(preinstall)の実行時のUID/GID切り替えを抑制します。

# .npmrc
registry=https://xxx.xxx.xxx/...
unsafe-perm=true     # ここ

【Python】urllibでプロキシ設定

f:id:tm200:20210915184401p:plain

urllibで社内のプロキシサーバーを経由して外部接続という処理を実装しました。
外部接続先のAPIBasic認証が必要で、少し詰まったのでメモ。
requestsモジュールだと、プロキシ設定 + 接続先でBasic認証が上手く動かなかった為、urllibモジュールを使用しています。
上手いやり方などあれば、教えてください😊

実装

import os
import base64
import json
import urllib.request


def exec_proxy_request(usename: str, passwd: str, token: str):

    user_pass = f'{usename}:{passwd}'
    header = {
        # アクセス先のBasic認証
        'Authorization': f"Basic {base64.b64encode(user_pass.encode('utf-8')).decode('utf-8')}"
        # プロキシサーバの認証情報、必要に応じて
        'Proxy-Authorization': f'Bearer {token}'
    }

    req = urllib.request.Request('<アクセス先URL>', headers=header, method='GET')
    # プロキシの設定
    req.set_proxy('xxx.proxy.jp', 'http')
    req.set_proxy('xxx.proxy.jp', 'https')

    with urllib.request.urlopen(req) as res:
        status_code = res.getcode()
        res_body = res.read().decode('utf-8')

        if not status_code == 200:
            raise Exception(res_body)

        json_data = json.loads(res_body)

    return json_data

【Kubernetes】IngressのURL Rewrite設定

Ingressのpath設定でリクエストを振り分ける際にURLのrewriteを行ったのでメモ。
設定がコントローラーにより異なるようです。

kubernetes-ingress

annotationsnginx.ingress.kubernetes.io/rewrite-targetを設定すると良いようです。
rewrite-targetに/を指定する事により、/v2アクセス時にservice2/(インデックスルート)にアクセスします。

kubernetes.io

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test
  namespace: test-namespace
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
  • Git Hub

github.com

Nginx Ingress Controller

kubernetes-ingressと同様にnginx.ingress.kubernetes.io/rewrite-targetで指定できるようです。

kubernetes.github.io

  • Git Hub

github.com

nghttpx Ingress Controller

zlab社製のnghttpxingress.zlab.co.jp/path-configにpathのrewrite処理を記載する必要があります。
詳細はドキュメントが参考になります。

nghttp2.org

以下の設定では、example.co.jp/v2/アクセス時にservice2/にルーティングされます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test
  namespace: test-namespace
  annotations:
    ingress.zlab.co.jp/path-config: |
      example.co.jp/v2/:
        mruby: |
          class App
            def on_req(env)
              # `/v2`を削除している
              env.req.path = env.req.path.sub(/^\/[^\/]+/, '')
            end
          end
          App.new

spec:
  rules:
  - http:
      paths:
      # `/v2`も入れないと`example.co.jp/v2`(最後の`/`無し)がNot Foundとなります
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
      - path: /v2/
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80
  • Git Hub

github.com

Traefik Controller (Rancher, k3s)

k3s環境ではIngressControllerにTraefieがデフォルトで適用されます。
今までのIngressControllerと異なり、MIddlewareというCRDのデプロイが別途必要になります。

doc.traefik.io

以下、/v2アクセス時にservice2/(インデックスルート)にアクセスする例となります。

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: replacepathregex-service2
  namespace: test-namespace
spec:
  replacePathRegex:
    regex: ^/v2/(.*)
    replacement: /$1

annotationstraefik.ingress.kubernetes.io/router.middlewaresを指定します。
設定する値は${namespace名}-${Middleware名}@kubernetescrdという形式にする必要があります。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test
  namespace: test-namespace
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: test-namespace-replacepathregex-service2@kubernetescrd
spec:
  ingressClassName: traefik
  rules:
  - http:
      paths:
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80