MySQL升级后从mysql_native_password认证模块更改为caching_sha2_password

MySQL 8.4 LTS 开始默认使用caching_sha2_password认证插件,来取代不太安全的mysql_native_password认证插件。网上很多的文章解决这个问题的方法,是启动MySQL的时候,加上--mysql-native-password=ON选项,以回退并继续使用mysql_native_password认证插件。然而,在MySQL 9.0以后,mysql_native_password认证插件不再被支持。所以更好的解决办法,还是彻底更改认证方式为caching_sha2_password

首先升级MySQL到8.4+后正常启动,默认认证模块会变更为caching_sha2_password

DROP USER 'foo'@'%';
CREATE USER 'foo'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON BAR.* TO 'foo'@'%';
FLUSH PRIVILEGES;

注意:
1. 只是 DELETE FROM mysql.user where User="foo"; FLUSH PRIVILEGES; 可能会导致CREATE USER失败,需要先DROP USER
2. 如果升级后无法登陆,可以启动MySQL的时候加上--skip-grant-tables以临时跳过认证。
3. 对于root用户,可以加上WITH GRANT OPTION

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

客户端这边,以PHP为例,需要确认支持caching_sha2_password认证插件。

<?php
phpinfo();
?>

会显示:

mysqlnd - Loaded plugins:
auth_plugin_caching_sha2_password

如果不支持,请升级版本。

Arc浏览器

总的来说,Arc浏览器还算挺新颖好用的,但也带来一些问题:

1. 去掉了主页

我一直有在使用一个本地主页,是因为有些复杂逻辑的跳转需要Javascript代码,而不是简单的几个链接。

目前我把主页Pin在第一个位置,前面还有Gmail, Calendar,所以Cmd+3会让我直接看到主页,从主页点击跳转的时候,按住Cmd+Shift键,链接会在新标签页打开。

自然,这要好用需要短暂的适应,形成肌肉记忆。

2. 去掉了书签

这个最大的问题,还是不支持Bookmarklet,解决办法是把Bookmarklet转换成插件,网上有现成的工具,但始终感觉还是有点重。

最后,多学习一下Arc的快捷键,会好用很多。也包括不同的链接打开方式:

  • 点击:原标签页打开,留在原标签页
  • Cmd + 点击:新标签页打开,留在原标签页
  • Cmd + Shift + 点击:新标签页打开,留在新标签页
  • Opt + 点击:Split中打开,留在Split
  • Shift + 点击:Peek中打开,留在Peek
  • Cmd + Opt + 点击:Little Arc中打开,留在Little Arc

判断从AppStore升级XCode是否卡住

从AppStore升级XCode有的时候耗时很长,让你不太清楚到底是卡住了,还是只是很慢。下面的步骤可以帮助你判断:

  1. 保持App Store打开
  2. 打开Console App (注意:不是Terminal)
  3. 在Console App的搜索框里,输入”App Store”
  4. 在Console App里点击”Start”

Log里会记录类似如下信息:

ASDAppQuery.resultsDidChange 497799835: <NSProgress: 0x600003ff8d00> : Parent: 0x0 (portion: 0) / Fraction completed: 0.8230 / Completed: 823 of 1000
ASDAppQuery.resultsDidChange 497799835: <NSProgress: 0x600003fe5500> : Parent: 0x0 (portion: 0) / Fraction completed: 0.8280 / Completed: 828 of 1000

表示此时安装正在正常进行。

如果信任第三方工具,可以用下面这个图形化的工具来取代AppStore以安装XCode:

brew install xcodes

MapStruct with Dagger Injection

jsr330componentModel 配合CONSTRUCTORInjectionStrategy可以与Dagger配合使用。

定义Mapper,并在map的同时完成解密:

package net.axqd.mapper;

import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "jsr330",
    uses = DecryptorQualifier.class
    injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface AToBMapper {
    @Mapping(
        source = "src.encrypted",
        target = "decrypted",
        qualifiedByName = { "DecryptorQualifier", "decrypt" })
    B mapAToB(final src)
}

其中,DecryptorQualifier的定义如下:

package net.axqd.mapper;

import lombok.NonNull;
import org.mapstruct.Named;
import javax.inject.Inject;

@Named("DecryptorQualifier")
public class DecryptorQualifier {
    private final Service service;

    @Inject
    public DecryptorQualifier(@NonNull final Service service) {
        this.service = service;
    }

    @Named("decrypt")
    public String decrypt(final String encrypted) {
        return service.decrypt(encrypted);
    }
}

定义Mapper的Provider:

package net.axqd.dagger;

import net.axqd.mapper.AToBMapper;
import net.axqd.mapper.AToBMapperImpl;

import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;

@Module
public class MapperModule {
    @Provides
    @Singleton
    AToBMapper aToBMapper(final DecryptorQualifier qualifier) {
        return new AToBMapperImpl(qualifier);
    }
}

定义Component:

import dagger.Component;
import javax.inject.Singleton;

@Component(modules = {
    MapperModule.class
})
@Singleton
public interface FooBarComponent {
    ...
}

单元测试:

package net.axqd.mapper;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class AToBMapperTest {
    private static final String ENCRYPTED = "encrypted";
    private static final String DECRYPTED = "decrypted";

    @Mock
    private DecryptorQualifier qualifier;

    @InjectMocks
    private AToBMapperImpl mapper;

    @Test
    public void aToBMapper_when_xxx() {
        when(qualifier.decrypt(ENCRYPTED)).thenReturn(DECRYPTED);

        final A a = A.builder().xxx.build();
        final B b = mapper.mapAToB(a);

        assertEquals(...);
    }
}

Private CA for mTLS with cert-manager

第一步,安装cert-manager:

https://cert-manager.io/docs/installation/kubernetes/

第二步,安装自签名ClusterIssuer(所有Namespace共用):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: FooBarSelfSign
spec:
  selfSigned: {}

第三步,签发CA证书(Application Namespace自用):

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
 name: FooBarCACertificate
spec:
 secretName: FooBarCACertificate
 isCA: true
 issuerRef:
   name: FooBarSelfSign
   kind: ClusterIssuer
 commonName: "FooBar CA"

第四步,安装CA(Application Namespace自用):

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
 name: FooBarCA
spec:
 ca:
   secretName: FooBarCACertificate

第五步,生成随机JKS密码(Application Namespace自用):

NOTE:如果不需要JKS可以省略这步。

apiVersion: v1
kind: Secret
metadata:
  name: FooBarJKSPassword
data:
  Password: {{ uuidv4 | b64enc }}

第六步,使用CA签发Application证书(Application Namespace自用):

NOTE:如果不需要JKS,可以省略keystores的部分

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: FooBarServerCertificate
spec:
  secretName: FooBarServerCertificate
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  keySize: 2048
  keyAlgorithm: rsa
  keyEncoding: pkcs1
  commonName: server.foobar.com
  keystores:
    jks:
      create: true
      passwordSecretRef:
        name: FooBarJKSPassword
        key: Password
  issuerRef:
    name: FooBarCA
    kind: Issuer
    group: cert-manager.io
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: FooBarClientCertificate
spec:
  secretName: FooBarClientCertificate
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  keySize: 2048
  keyAlgorithm: rsa
  keyEncoding: pkcs1
  commonName: "FooBar Client"
  keystores:
    jks:
      create: true
      passwordSecretRef:
        name: FooBarJKSPassword
        key: Password
  issuerRef:
    name: FooBarCA
    kind: Issuer
    group: cert-manager.io

生成的FooBarServerCertificateFooBarClientCertificate包含以下内容:

  • tls.key – 证书私钥
  • tls.crt – 证书
  • ca.crt – CA证书
  • keystore.jks
  • truststore.jks

第七步,安装签发的证书到应用:

示例1 – Server Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: FooBarServer
spec:
  template:
    spec:
      containers:
        - name: FooBarServer
          volumeMounts:
            - name: tls
              mountPath: /var/ssl/private
              readOnly: true
      volumes:
        - name: tls
          secret:
            secretName: FooBarServerCertificate
            items:
              - key: truststore.jks
                path: foobar-truststore.jks
              - key: keystore.jks
                path: foobar-keystore.jks
...

示例2 – Ingress Client:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: FooBarIngress
  annotations:
    kubernetes.io/ingress.class: nginx

    # enable mTLS for frontend
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
    nginx.ingress.kubernetes.io/auth-tls-verify-depth: 2
    nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "false"
    # read the ca.crt part of the certificate
    nginx.ingress.kubernetes.io/auth-tls-secret: <another certificate for frontend>
    
    # enable mTLS for backend
    nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
    nginx.ingress.kubernetes.io/proxy-ssl-secret: FooBarClientCertificate
spec:
  rules:
    - host: server.foobar.com
      http:
        paths:
          - path: /xxx
            backend:
              serviceName: FooBarAppService
              servicePort: xxx
  tls:
    - hosts:
        - server.foobar.com
      # read the tls.key and tls.crt parts of the certificate
      secretName: <another certificate for frontend>
...

Base Template in Helm

有时我们需要使用Base Template来同时生成两个稍微有点差别的manifest文件。

如果你只需要更改Map里面的内容,一种很容易的方式是直接合并生成好的YAML。但对于Containers以及Volumes这种数组类型,合并YAML就不是很容易了。

另外一种方式,是生成本地修改的.Values,具体内容如下:

templates/_util.tpl:

{{- /*
chart.util.merge will merge a YAML value file with context,
use it to generate another YAML file and output the result.
This takes an array of three values:
- the top context
- the name of the value overrides
- the name of the target template
*/ -}}
{{- define "chart.util.merge" -}}
{{- $top := first . -}}
{{- $overrides := fromYaml (include (index . 1) $top) | default (dict ) -}}
{{- include (index . 2) (merge $overrides $top) -}}
{{- end -}}

templates/_parent.yaml:

{{/*
Parent Base Template
*/}}
{{- define "chart.parent.tpl" -}}
Current Value: {{ .Values.test.key.value }}
{{- end -}}
{{- define "chart.parent" -}}
{{- include "chart.util.merge" (append . "chart.parent.tpl") -}}
{{- end -}}

templates/child1.yaml:

{{- include "chart.parent" (list . "chart.parent.child1") -}}
{{- define "chart.parent.child1" -}}
Values:
  test:
    key:
      value: child1
{{- end -}}

templates/child2.yaml:

{{- include "chart.parent" (list . "chart.parent.child2") -}}
{{- define "chart.parent.child2" -}}
Values:
  test:
    key:
      value: child2
{{- end -}}

values.yaml:

test:
  key: {}
    # This value is provided by child1.yaml or child2.yaml
    #value: parent

最后结果如下:

$ helm template test test
---
# Source: test/templates/child1.yaml
Current Value: child1
---
# Source: test/templates/child2.yaml
Current Value: child2

使用 MingW-W64 编译支持的 Linux 项目

下载并安装MSys2和MingW-W64:

  • https://www.msys2.org/
  • http://mingw-w64.org/doku.php/download/mingw-builds/

准备环境:

  • (msys2) # pacman -Syu
  • (close terminal window)
  • (msys2) # pacman -Su
  • (msys2) # pacman -S diffutils (for cmp)
  • (msys2) # pacman -S make

编译:

  • (msys2)# export PATH=/c/mingw-w64/x86_64-8.1.0-win32-seh-rt_v6-rev0/mingw64/bin:$PATH
  • (msys2)# ./configure –host=i686-w64-mingw32
  • (msys2)# make && make install

Jenkins及子任务全自动部署

Ansible基础配置

https://galaxy.ansible.com/geerlingguy/jenkins

Ansible额外配置 – Groovy Scripts

“{{ jenkins_home }}/init.groovy.d/{{ item }}”

Jenkins Seed Job – Job-DSL Groovy Scripts

  • 动态API参考文档: http://<your jenkins>/plugin/job-dsl/api-viewer/index.html
  • Playground: http://job-dsl.herokuapp.com/
  • Deprecated NoTriggerBranchProperty for multibranchPipelineJob
    • 临时configure解决方案
  configure { node ->
    def src = node / sources / data / 'jenkins.branch.BranchSource'
    src << 'buildStrategies' {
      'jenkins.branch.buildstrategies.basic.NamedBranchBuildStrategyImpl' {
        filters {
          'jenkins.branch.buildstrategies.basic.NamedBranchBuildStrategyImpl_-WildcardsNameFilter' {
            includes("")
            excludes("*")
            caseSensitive(false)
          }
        }
      }
    }
  }

Global Shared Libraries – Groovy Scripts

  • http://<your jenkins>/pipeline-syntax/

Jenkinsfile – Groovy Scripts

  • @Library(‘PipelineSharedLib@master’) _

Perforce双Client Checkout解决方案

确保两个client的名称不同:

  • pipelineJob / definition / cpsScm / scm / perforce / workspace / streamSpec with pipelineJob / definition / cpsScm / lightweight and scriptPath
  • checkout() in Jenkinsfile

并从scm中提取相关信息:

  checkout([
    $class:        'PerforceScm',
    credential:    scm.credential,
    workspace:     new StreamWorkspaceImpl(
                     'none',                   // charset
                     false,                    // pinHost
                     scm.workspace.streamName, // streamName
                     "jenkins-${NODE_NAME}-${JOB_NAME}-${EXECUTOR_NUMBER}" // format
                   ),
    populate:      scm.populate
  ])

快速重建git p4 clone过的仓库

背景

  • Bitbucket/Stash仓库 https://stash.abc.com/scm/test/foo_bar.git
  • Perforce仓库 //Foo/bar
  • Bitbucket/Stash仓库的master分支保持和Perforce的内容完全一致

首次创建并同步

  • $ git p4 clone //Foo/bar
  • $ git remote add origin https://stash.abc.com/scm/test/foo_bar.git
  • $ git push -u origin master
  • $ git checkout -b p4/master
  • $ git push -u origin p4/master

快速重建之前的仓库 (跳过git p4 clone,而直接syncFromOrigin + git p4 rebase)

  • $ git clone https://stash.abc.com/scm/test/foo_bar.git
  • $ git config –add git-p4.syncFromOrigin “true” <– 先读取remotes/origin/p4/master
  • $ git fetch origin
  • $ git checkout master
  • $ git p4 rebase <– 自动重建remotes/p4/master
  • $ git push –atomic origin master

维护remotes/origin/p4/master分支

  • 这个分支中的内容会从Bitbucket/Stash同步,而避免了从Perforce缓慢的提取过程
  • 定期从master分支更新内容到这个分支,可以使得尽可能多的内容从Bitbucket/Stash同步