Rustでmut selfとか書いてしまってついつい変数が移動する

こういうキャッシュを実装していて

#[derive(Debug, Clone)]
struct Cache {
    items_data: HashMap<String, Vec<Item>>,
}

impl Cache {
    fn set_items(mut self, name: String, data: Vec<Item>) {
        self.items_data.insert(name, data);
    }
}

これをArc<futures::lock::Mutex<Cache>>型でもっているときに、使うときにロックを取得してエラーが出た。

let mut hoge = cache.lock().await;
hoge.set_items(name, data.clone());
error[E0507]: cannot move out of dereference of `futures_util::lock::mutex::MutexGuard<'_, server::Cache>`
   --> src/server.rs:173:9
    |
173 |         hoge.set_items(name, data.clone());
    |         ^^^^ move occurs because value has type `server::Cache`, which does not implement the `Copy` trait

これはロックとは関係なくてset_itemsが参照ではない要求をしていたからで、参照でも別にいいので下記のように書き換えればコンパイルが通る。

#[derive(Debug, Clone)]
struct Cache {
    items_data: HashMap<String, Vec<Item>>,
}

impl Cache {
    fn set_items(&mut self, name: String, data: Vec<Item>) {
        self.items_data.insert(name, data);
    }
}

なんでこんなことを書いたかというと、これと同じことで詰まったことがもう5回くらいあって(Mutexを絡めたコード書いてると複雑だから混乱しちゃって詰まる)、日本語で検索しても情報はないので、日本語で書いて自分で引っかかろうと思って。

Rustで構造体を含むVecをソートする

RustのHashMapで以下のようなコードを書いたとき、このコードのassert_eq!panicします。入れた順に来るかと思ったし、キーの値でソートしたものを取り出したりできるんじゃないかと思ったんだが、見つけられなかった。また、構造体のソートについて調べると自力でやる方法が見つかったりしたが、結構大変だった。

fn main() {
    use std::{collections::HashMap, io::Write};

    #[derive(Debug, PartialEq, Clone)]
    struct Example {
        name: String,
        age: u8,
    };

    let example1 = Example {
        name: "あんこ".into(),
        age: 16,
    };

    let example2 = Example {
        name: "あんこ".into(),
        age: 105,
    };

    let example3 = Example {
        name: "えなこ".into(),
        age: 24,
    };

    let mut a: HashMap<usize, Example> = HashMap::new();
    a.insert(1, example1.clone());
    a.insert(2, example2.clone());
    a.insert(3, example3.clone());

    let res = a.into_iter().map(|(_, val)| val).collect::<Vec<Example>>();

    assert_eq!(vec![example1, example2, example3], res);
}

で、しばらくしてもう一度調べていたら Eq PartialEq Ord PartialOrd をDriveで追加することで普通にソートができるようです。

fn main() {
    use std::{collections::HashMap, io::Write};

    #[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Clone)]
    struct Example {
        name: String,
        age: u8,
    };

    let example1 = Example {
        name: "あんこ".into(),
        age: 16,
    };

    let example2 = Example {
        name: "あんこ".into(),
        age: 105,
    };

    let example3 = Example {
        name: "えなこ".into(),
        age: 24,
    };

    let mut a: HashMap<usize, Example> = HashMap::new();
    a.insert(1, example1.clone());
    a.insert(2, example2.clone());
    a.insert(3, example3.clone());

    let mut res = a.into_iter().map(|(_, val)| val).collect::<Vec<Example>>();
    res.sort();

    assert_eq!(vec![example1, example2, example3], res);
}

Pythonでpsycopg2を使ってPostgreSQLに接続する

Python2系でも3系でも動いた。取得した値が空だと None が返るからそれを見てwhileとかでループするのが良い。

#!/usr/bin/python

import psycopg2

connection = psycopg2.connect("host='127.0.0.1' port=5432 dbname=example user=root password=password")
cursor = connection.cursor()
cursor.execute("SELECT %s" % "TRUE")

assert cursor.fetchone() == (True,)
assert cursor.fetchone() is None

Ubuntuにpsycopg2をインストールする

インストールしよう

pip3 install psycopg2

エラーがでた

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-kgdzc3xx/psycopg2/

アップグレードしよう

pip3 install --upgrade pip setuptools

エラー内容が変わった

ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output

libpq-dev をインストールしよう

sudo apt install libpq-dev

これで成功した

リモートワーカーが気持ちを切り替えて副業をする方法

リモートワークをしてからプライベートの開発(副業)がちょっと難しくなってきた。

本業用のマシンがあるから開発は捗ると思ったんだけど、ずっと同じ部屋で作業していると気分が変わらないので、ずっと仕事しているような気分になって萎える。

ちょっとお金はかかってしまうが、プライベート開発用のマシンは別にして(私の場合は外出用のノートPCがあったのでそれで代用)、家賃+2万くらいで広い家を借りて(私の場合既に広めの家に住んでるからなんとかなった)別の部屋でやるのが良いみたいで、それが大成功でまた楽しくプライベートの開発もできるようになりました。

在宅勤務は通勤のストレスから開放されて睡眠時間もたくさん確保できるんですけど、メンタルケアとパフォーマンスの維持のための個人個人の工夫が求められますね。

WordPressでJavaScriptのみで管理者としてログインしているか調べる

管理者の時には wpadminbar というIDが付いた要素が出現しているっぽいので、これで判断すればいいかなと思う。非表示にしてたら無理なんだけど。

if (document.getElementById('wpadminbar') !== null) {
  console.log("貴様管理者だな?");
}

テーマ弄ったりしたら確実だけど、弄るとなるとバージョン管理とか大変だから絶対やりたくないし、テーマ気分で変えられなくなるから嫌だ。

Google Cloud DNS APIを利用してLet’s EncryptのワイルドカードSSL証明書の作成と更新を行う

Certbotを今まで使っていましたがLegoを使うことにしました。GOで作られているので、Certbotのように依存関係をたくさん入れる必要もないので環境に優しいです。公式のここで紹介されています。

CertbotにせよLegoにせよ、昨今ではSSL証明書を持つProxyすらDockerコンテナ運用をしているためHTTP-01及びTLS-SNI-01による作成ができない(Proxyはクラスタ上に複数立ち上がっている)ので、DNS-01を使って事前に格納するかマウントしますが、ワンタイムトークンを発行されたあとTXTレコードを手動で追加してから発行してもらうしかないと勘違いしておりましたが、どうやらDNSのAPIを使って自動でいけるみたいです。

設定

GCE_SERVICE_ACCOUNT_FILEにサービスアカウントJSONを指定します。サービスアカウントの権限にGoogle Cloud DNSの権限を付与しておきます。GCS_PROJECTを指定すると403で弾かれるので定義しません。GCS_PROJECTGCS_SERVICE_ACCOUNT_FILEから取得されます。こちらに書いてありました。

--dnsgcloudを指定していますが、これはdocker run --rm -t goacme/lego dnshelpで指定できるリストが得られました。下記のサービスに対応しています。GithubのREADMEにマニュアルへのリンクがあり、そこに以下のサービス一覧がのっていました。

acme-dns, alidns, auroradns, autodns, azure, bindman, bluecat, checkdomain, cloudflare, cloudns, cloudxns, conoha, designate, digitalocean, dnsimple, dnsmadeeasy, dnspod, dode, dreamhost, duckdns, dyn, easydns, exec, exoscale, fastdns, gandi, gandiv5, gcloud, glesys, godaddy, hostingde, httpreq, iij, inwx, joker, lightsail, linode, linodev4, liquidweb, manual, mydnsjp, namecheap, namedotcom, namesilo, netcup, nifcloud, ns1, oraclecloud, otc, ovh, pdns, rackspace, rfc2136, route53, sakuracloud, selectel, stackpath, transip, vegadns, versio, vscale, vultr, zoneee

上記をふまえて、設定はこんな風にします。

version: '3.5'

services:
  app:
    image: goacme/lego
    secrets:
      - cloud_dns_json
    environment:
      - GCE_SERVICE_ACCOUNT_FILE=/run/secrets/cloud_dns_json
    command: >
      --path /lego
      --dns gcloud
      --email admin@example.com
      --domains "example.com"
      --domains "*.example.com"
      --accept-tos
      run
    volumes:
      - ./lego:/lego

secrets:
  cloud_dns_json:
    file: ./cloud_dns.json

カレントディレクトリの./legoにファイルが生成されていると思います。

実際の運用では悲しいやり方をしている

私の場合、例えばこの処理をSwarmクラスタ上で実行したいのだが、今の所Swarmクラスタを指定してのrunができない。特定のノードを指定してrunすることもできるが、Cloud DNSのサービスアカウントJSONはSecretsに格納されており、それにアクセスするにはSwarmを経由したい。だから、結局ENTRYPOINTを指定して、何もせずコンテナを立ち上げた状態で中に入って以下を実行しています。

lego --path /ssl --dns gcloud --email admin@example.com --domains "example.com" --domains "*.example.com" --accept-tos run

renewによる自動更新

renewは簡単で、runrenew --days 60に変更するだけです。60っていうのは、残りの有効期限が60日以内の場合を示します。30とすれば30日以内のものを更新します。私の場合は30日に一度更新したいので、60を指定しています。

lego --path /ssl --dns gcloud --email admin@example.com --domains "example.com" --domains "*.example.com" --accept-tos renew --days 60

再度実行すると残り日数が60日以下ではないので以下のようなことを言われて、何も行われません。

2020/04/02 06:06:27 [example.com] The certificate expires in 89 days, the number of days defined to perform the renewal is 60: no renewal.

メモ

legoが生成する証明書置き場をNFSとした。GCSとめちゃくちゃ悩んでるし今も悩んでいる。

  • Legoコンテナ起動時にGCSからファイルを取得し、定期的にGCSと同期を取ることでNFSを不要とできそうだが、Legoをベースにした独自のDockerイメージを作ったりすると作業コスト増えるし、後から弄る時にうんざりしそう。昨日の自分は他人。
  • GCSを保存先にしてしまうと今度はNginxコンテナが参照する証明書もGCSから取得するように作らなければならない。それも同じ用にうんざりしそう。
  • NFSは空いたVMインスタンスに専用のディスクを追加するだけなので月額100円のコスト増で、多く感じるけど、そのうちNFSがどうしても必要な事も出てきそうなのでNFSで良しとした。

参考